def test_add_adjacent(self, document): region1 = Region(0, 1, None, None) region2 = Region(1, 2, None, None) region3 = Region(2, 3, None, None) document.add(region1) document.add(region3) document.add(region2) assert [e.region for e in document] == [region1, region2, region3]
def test_example_line_and_column(self): text = 'R1XYZ\nR2XYZ\nR3XYZ\nR4XYZ\nR4XYZ\n' i = text.index document = Document(text, '') document.add(Region(0, i('R2')+2, None, None)) document.add(Region(i('R3')-1, i('R3')+2, None, None)) document.add(Region(i('R4')+3, len(text), None, None)) assert ([(e.line, e.column) for e in document] == [(1, 1), (2, 6), (4, 4)])
def test_add_overlaps_with_previous(self, document): region1 = Region(0, 2, None, None) region2 = Region(1, 3, None, None) document.add(region1) with pytest.raises(ValueError) as excinfo: document.add(region2) assert str(excinfo.value) == ( '<Region start=0 end=2 None>' ' from line 1, column 1 to line 1, column 3 overlaps ' '<Region start=1 end=3 None>' ' from line 1, column 2 to line 1, column 4')
def test_add_overlaps_with_next(self, document): region1 = Region(0, 1, None, None) region2 = Region(1, 3, None, None) region3 = Region(2, 4, None, None) document.add(region1) document.add(region3) with pytest.raises(ValueError) as excinfo: document.add(region2) assert str(excinfo.value) == ( '<Region start=1 end=3 None> ' 'from line 1, column 2 to line 1, column 4 overlaps ' '<Region start=2 end=4 None> ' 'from line 1, column 3 to line 1, column 5')
def skip(document): """ A parser function to be included when your documentation makes use of :ref:`skipping <skip-parser>` examples in a document. """ for match in re.finditer(SKIP, document.text): yield Region(match.start(), match.end(), match.groups(), evaluate_skip)
def test_evaluate_raises_exception(self, document): def evaluator(example): raise ValueError('foo!') region = Region(0, 1, 'the data', evaluator) example = Example(document, 1, 2, region, {}) with pytest.raises(ValueError) as excinfo: example.evaluate() assert str(excinfo.value) == 'foo!'
def test_add_before_start(self, document): region = Region(-1, 0, None, None) with pytest.raises(ValueError) as excinfo: document.add(region) assert str( excinfo.value) == ('<Region start=-1 end=0 None> ' 'from line 1, column 0 to line 1, column 1 ' 'is before start of document')
def test_add_after_end(self, document): region = Region(len(document.text), len(document.text) + 1, None, None) with pytest.raises(ValueError) as excinfo: document.add(region) assert str( excinfo.value) == ('<Region start=8 end=9 None> ' 'from line 1, column 9 to line 1, column 10 ' 'goes beyond end of document')
def parse_bash_blocks(document): if document.path.endswith('development.txt'): return for start_match, end_match, source in document.find_region_sources( BASHBLOCK_START, BASHBLOCK_END): command, output = source.split('\n', 1) parsed = command, textwrap.dedent(output) yield Region(start_match.start(), end_match.end(), parsed, evaluate_bash_block)
def test_evaluate_okay(self, document): def evaluator(example): example.namespace['parsed'] = example.parsed region = Region(0, 1, 'the data', evaluator) namespace = {} example = Example(document, 1, 2, region, namespace) result = example.evaluate() assert result is None assert namespace == {'parsed': 'the data'}
def __call__(self, document: Document) -> Iterable[Region]: for start_match in re.finditer(PLOT_DIRECTIVE_START, document.text): source_start = start_match.end() indent = str(len(start_match.group("indent"))) end_pattern = re.compile(r"(\n\Z|\n[ \t]{0," + indent + "}(?=\\S))") end_match = end_pattern.search(document.text, source_start) source_end = end_match.start() source = textwrap.dedent(document.text[source_start:source_end]) yield Region(start_match.start(), source_end, source, self.evaluate)
def parse_literalinclude_python_blocks(document): for start_match, end_match, source in document.find_region_sources( LITERALPYTHON_START, LITERALPYTHON_END ): literalinclude_python = start_match.groups()[0].strip() if any([fnmatch.fnmatch(literalinclude_python, ignore_pattern) for ignore_pattern in IGNORES]): continue print(literalinclude_python) yield Region(start_match.start(), end_match.end(), literalinclude_python, evaluate_literalinclude_python_blocks)
def test_evaluate_not_okay(self, document): def evaluator(example): return 'foo!' region = Region(0, 1, 'the data', evaluator) example = Example(document, 1, 2, region, {}) with pytest.raises(SybilFailure) as excinfo: example.evaluate() assert str(excinfo.value) == ( 'Example at /the/path, line 1, column 2 did not evaluate as ' 'expected:\nfoo!' ) assert excinfo.value.example is example assert excinfo.value.result == 'foo!'
def __call__(self, document): for start_match in re.finditer(CODEBLOCK_START, document.text): source_start = start_match.end() indent = str(len(start_match.group('indent'))) end_pattern = re.compile(r'(\n\Z|\n[ \t]{0,' + indent + '}(?=\S))') end_match = end_pattern.search(document.text, source_start) source_end = end_match.start() source = textwrap.dedent(document.text[source_start:source_end]) # There must be a nicer way to get code.co_firstlineno # to be correct... line_count = document.text.count('\n', 0, source_start) if self.future_imports: line_count -= 1 source = 'from __future__ import {}\n{}'.format( ', '.join(self.future_imports), source) line_prefix = '\n' * line_count source = line_prefix + source yield Region(start_match.start(), source_end, source, evaluate_code_block)
def __call__(self, document): for start_match, end_match, source in document.find_region_sources( FILEBLOCK_START, FILEBLOCK_END): lines = source.splitlines() class_ = CLASS.match(lines[1]) if not class_: continue index = 3 if lines[index].strip() == '::': index += 1 source = textwrap.dedent('\n'.join(lines[index:])).lstrip() if source[-1] != '\n': source += '\n' parsed = FileBlock(path=start_match.group(1), content=source, action=class_.group(1)) yield Region(start_match.start(), end_match.end(), parsed, self.evaluate)
def parse_captures(document): """ A parser function to be included when your documentation makes use of :ref:`capture-parser` examples. """ lines = DocumentReverseIterator(document) for end_index, line in lines: directive = CAPTURE_DIRECTIVE.match(line) if directive: region_end = lines.current_line_end_position indent = directive.group('indent') for start_index, line in lines: if indent_matches(line, indent): # don't include the preceding line in the capture start_index += 1 break else: # make it blow up start_index = end_index if end_index - start_index < 2: raise ValueError( ("couldn't find the start of the block to match " "%r on line %i of %s") % (directive.group(), end_index + 1, document.path)) # after dedenting, we need to remove excess leading and trailing # newlines, before adding back the final newline that's strippped # off text = dedent(''.join(lines[start_index:end_index])).strip() + '\n' name = directive.group('name') parsed = name, text yield Region(lines.current_line_end_position, region_end, parsed, evaluate_capture)
def test_add_no_overlap(self, document): region1 = Region(0, 1, None, None) region2 = Region(6, 8, None, None) document.add(region1) document.add(region2) assert [e.region for e in document] == [region1, region2]
def test_add(self, document): region = Region(0, 1, None, None) document.add(region) assert [e.region for e in document] == [region]
def test_repr(self, document): region = Region(0, 1, 'parsed', 'evaluator') example = Example(document, 1, 2, region, {}) assert (repr(example) == "<Example path=/the/path line=1 column=2 using 'evaluator'>")
def parse_for(letter, document): for m in re.finditer(r'(%s+) (\d+) check' % letter, document.text): yield Region(m.start(), m.end(), (m.group(1), int(m.group(2))), partial(check, letter))
def test_repr(self): region = Region(0, 1, 'parsed', 'evaluator') assert repr(region) == "<Region start=0 end=1 'evaluator'>"
def parse_first_line(document): line = document.text.split('\n', 1)[0] yield Region(0, len(line), line, None)
def test_example_path(self, document): document.add(Region(0, 1, None, None)) assert [e.document for e in document] == [document]
def test_add_out_of_order(self, document): region1 = Region(0, 1, None, None) region2 = Region(6, 8, None, None) document.add(region2) document.add(region1) assert [e.region for e in document] == [region1, region2]
def parse_for_y(document): for m in re.finditer(r'(Y+) (\d+) check', document.text): yield Region(m.start(), m.end(), (m.group(1), int(m.group(2))), partial(check, 'Y'))
def parse(document): for m in re.finditer(r'([XY]+) (\d+) check', document.text): yield Region(m.start(), m.end(), m.start(), check_into_namespace)