def _get_inline_overrides( self, testplan: TestPlanUnit ) -> "List[Tuple[str, List[Tuple[str, str]]]]": """ Look at the include field of a test plan and collect all of the in-line overrides. For an include statement that has any overrides they are collected into a list of tuples ``(field, value)`` and this list is subsequently packed into a tuple ``(pattern, field_value_list)``. """ override_list = [] if testplan.include is not None: class V(Visitor): def visit_IncludeStmt_node(self, node: IncludeStmt): if not node.overrides: return pattern = r"^{}$".format( testplan.qualify_id(node.pattern.text)) field_value_list = [ (override_exp.field.text.replace('-', '_'), override_exp.value.text) for override_exp in node.overrides] override_list.append((pattern, field_value_list)) V().visit(IncludeStmtList.parse(testplan.include)) for tp_unit in testplan.get_nested_part(): override_list.extend(self._get_inline_overrides(tp_unit)) return override_list
def _get_matchers(self, testplan, text): """ Parse the specified text and create a list of matchers :param text: string of text, including newlines and comments, to parse :returns: A generator returning quads (lineno_offset, field, matcher, error) where ``lineno_offset`` is the offset of a line number from the start of the text, ``field`` is the name of the field in a job definition unit that the matcher should be applied, ``matcher`` can be None (then ``error`` is relevant) or one of the ``IMatcher`` subclasses discussed below. Supported matcher objects include: PatternMatcher: This matcher is created for lines of text that **are** regular expressions. The pattern is automatically expanded to include ^...$ (if missing) so that it cannot silently match a portion of a job definition OperatorMatcher: This matcher is created for lines of text that **are not** regular expressions. The matcher uses the operator.eq operator (equality) and stores the expected job identifier as the right-hand-side value """ results = [] class V(Visitor): def visit_IncludeStmt_node(self, node: IncludeStmt): if isinstance(node.pattern, ReFixed): target_id = testplan.qualify_id(node.pattern.text) matcher = OperatorMatcher(operator.eq, target_id) elif isinstance(node.pattern, RePattern): pattern = testplan.qualify_pattern(node.pattern.text) matcher = PatternMatcher(pattern) result = (node.lineno, 'id', matcher) results.append(result) V().visit(IncludeStmtList.parse(text, 0)) return results
def parse_matchers(self, text): """ Parse the specified text and create a list of matchers :param text: string of text, including newlines and comments, to parse :returns: A generator returning quads (lineno_offset, field, matcher, error) where ``lineno_offset`` is the offset of a line number from the start of the text, ``field`` is the name of the field in a job definition unit that the matcher should be applied, ``matcher`` can be None (then ``error`` is relevant) or one of the ``IMatcher`` subclasses discussed below. Supported matcher objects include: PatternMatcher: This matcher is created for lines of text that **are** regular expressions. The pattern is automatically expanded to include ^...$ (if missing) so that it cannot silently match a portion of a job definition OperatorMatcher: This matcher is created for lines of text that **are not** regular expressions. The matcher uses the operator.eq operator (equality) and stores the expected job identifier as the right-hand-side value """ from plainbox.impl.xparsers import Error from plainbox.impl.xparsers import ReErr, ReFixed, RePattern from plainbox.impl.xparsers import IncludeStmt from plainbox.impl.xparsers import IncludeStmtList from plainbox.impl.xparsers import Visitor outer_self = self class IncludeStmtVisitor(Visitor): def __init__(self): self.results = [] # (lineno_offset, field, matcher, error) def visit_IncludeStmt_node(self, node: IncludeStmt): if isinstance(node.pattern, ReErr): matcher = None error = node.pattern.exc elif isinstance(node.pattern, ReFixed): target_id = outer_self.qualify_id(node.pattern.text) matcher = OperatorMatcher(operator.eq, target_id) error = None elif isinstance(node.pattern, RePattern): text = node.pattern.text # Ensure that pattern is surrounded by ^ and $ if text.startswith('^') and text.endswith('$'): target_id_pattern = '^{}$'.format( outer_self.qualify_id(text[1:-1])) elif text.startswith('^'): target_id_pattern = '^{}$'.format( outer_self.qualify_id(text[1:])) elif text.endswith('$'): target_id_pattern = '^{}$'.format( outer_self.qualify_id(text[:-1])) else: target_id_pattern = '^{}$'.format( outer_self.qualify_id(text)) matcher = PatternMatcher(target_id_pattern) error = None result = (node.lineno, 'id', matcher, error) self.results.append(result) def visit_Error_node(self, node: Error): # we're just faking an exception object here error = ValueError(node.msg) result = (node.lineno, 'id', None, error) self.results.append(result) visitor = IncludeStmtVisitor() visitor.visit(IncludeStmtList.parse(text, 0, 0)) return visitor.results