#!/usr/bin/env python
from html.parser import HTMLParser
import html
import sys
class GinkgoLogFilterParser(HTMLParser):
def __init__(self):
super().__init__()
# Stack to keep track of nested elements
self.stack = []
# Store the raw HTML strings of matching 'tt' elements
self.results = []
def _get_classes(self, attrs):
"""Helper to extract classes from an attribute list."""
for attr, value in attrs:
if attr == 'class' and value:
return value.split()
return []
def handle_starttag(self, tag, attrs):
classes = self._get_classes(attrs)
is_tt = 'tt' in classes
is_failed = 'log-failed' in classes
# If we see a 'log-failed' class, flag all 'tt' ancestors in the stack
if is_failed:
for node in self.stack:
if node['is_tt']:
node['keep'] = True
# Push the new element to the stack
self.stack.append({
'tag': tag,
'text_chunks': [],
'is_tt': is_tt,
'keep': False
})
def handle_startendtag(self, tag, attrs):
# Handle self-closing tags just to check for the failure class
classes = self._get_classes(attrs)
if 'log-failed' in classes:
for node in self.stack:
if node['is_tt']:
node['keep'] = True
def handle_data(self, data):
# Capture raw text data
if self.stack:
self.stack[-1]['text_chunks'].append(data)
def handle_endtag(self, tag):
# Find the matching start tag in the stack
for i in reversed(range(len(self.stack))):
if self.stack[i]['tag'] == tag:
# Pop the matching tag and any unclosed children
while len(self.stack) > i:
node = self.stack.pop()
# Join all the collected text for this node
node_text = "".join(node['text_chunks'])
# Trim down the spaces for the ginkgo long indentation.
node_text = node_text.removeprefix(' ' * 9)
# If this is a 'tt' element and it contains a 'log-failed' child, save the text
if node['is_tt'] and node['keep']:
self.results.append(node_text)
# Pass the text of this node up to its parent so we don't lose inner text
if self.stack:
self.stack[-1]['text_chunks'].append(node_text)
break
class BatsLogFilterParser(HTMLParser):
def __init__(self):
super().__init__()
# Current Tag which text should be stored
self.record = None
self.data = ""
def _get_classes(self, attrs):
"""Helper to extract classes from an attribute list."""
for attr, value in attrs:
if attr == 'class' and value:
return value.split()
return []
def handle_starttag(self, tag, attrs):
classes = self._get_classes(attrs)
if 'bats-failed' in classes or 'bats-log-failblock' in classes or 'bats-log' in classes:
self.record = tag
def handle_data(self, data):
if self.record:
self.data += data
def handle_endtag(self, tag):
if self.record == tag:
self.data += "\n"
self.record = None
def filter_html_file(file_path):
# Read the HTML content
with open(file_path, 'r', encoding='utf-8') as f:
html_content = f.read()
if 'int-' in file_path:
parser = GinkgoLogFilterParser()
parser.feed(html_content)
return parser.results
parser = BatsLogFilterParser()
parser.feed(html_content)
return [parser.data]
# Running the filter
matching_elements = filter_html_file(sys.argv[1])
for element in matching_elements:
print(f"```")
print(element)
print("```")