From d132cd2d2fea9a22e5d323c187aa6229f45dd57b Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Sat, 28 Mar 2020 16:35:42 +0000 Subject: [PATCH] Test reporter warnings --- myst_parser/docutils_renderer.py | 47 +++++++++----- myst_parser/mocking.py | 2 +- setup.py | 2 +- .../fixtures/directive_options.md | 5 +- .../fixtures/reporter_warnings.md | 65 +++++++++++++++++++ .../fixtures/syntax_elements.md | 2 +- tests/test_renderers/test_error_reporting.py | 27 ++++++++ 7 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 tests/test_renderers/fixtures/reporter_warnings.md create mode 100644 tests/test_renderers/test_error_reporting.py diff --git a/myst_parser/docutils_renderer.py b/myst_parser/docutils_renderer.py index 208f2a03..5833ac21 100644 --- a/myst_parser/docutils_renderer.py +++ b/myst_parser/docutils_renderer.py @@ -102,10 +102,21 @@ def render(self, tokens: List[Token], options, env: AttrDict): if f"render_{token.type}" in self.rules: self.rules[f"render_{token.type}"](token) else: - # TODO make this reporter warning - print(f"no render method for: {token.type}") + self.current_node.append( + self.reporter.warning( + f"No render method for: {token.type}", line=token.map[0] + ) + ) - # TODO log warning for duplicate reference definitions + # log warnings for duplicate reference definitions + # "duplicate_refs": [{"href": "ijk", "label": "B", "map": [4, 5], "title": ""}], + for dup_ref in self.env.get("duplicate_refs", []): + self.document.append( + self.reporter.warning( + f"Duplicate reference definition: {dup_ref['label']}", + line=dup_ref["map"][0] + 1, + ) + ) if not self.config.get("output_footnotes", True): return self.document @@ -119,6 +130,8 @@ def render(self, tokens: List[Token], options, env: AttrDict): if refnode["refname"] not in foot_refs: foot_refs[refnode["refname"]] = True + # TODO log warning for duplicate footnote definitions + if foot_refs: self.current_node.append(nodes.transition()) for footref in foot_refs: @@ -166,8 +179,11 @@ def nested_render_text(self, text: str, lineno: int, disable_front_matter=True): if f"render_{token.type}" in self.rules: self.rules[f"render_{token.type}"](token) else: - # TODO make this reporter warning - print(f"no render method for: {token.type}") + self.current_node.append( + self.reporter.warning( + f"No render method for: {token.type}", line=token.map[0] + ) + ) @contextmanager def current_node_context(self, node, append: bool = False): @@ -189,7 +205,7 @@ def render_children(self, token): def add_line_and_source_path(self, node, token): """Copy the line number and document source path to the docutils node.""" try: - node.line = token.map[0] + 1 + node.line = token.map[0] except (AttributeError, TypeError): pass node.source = self.document["source"] @@ -402,11 +418,10 @@ def render_link_open(self, token): self.render_children(token) def handle_cross_reference(self, token, destination): - # TODO use the docutils error reporting mechanisms, rather than raising if not self.config.get("ignore_missing_refs", False): - raise NotImplementedError( - "reference not found in current document: {} (lines: {})".format( - destination, token.map + self.current_node.append( + self.reporter.warning( + f"Reference not found: {destination}", line=token.map[0] ) ) @@ -426,7 +441,8 @@ def render_image(self, token): img_node = nodes.image() self.add_line_and_source_path(img_node, token) img_node["uri"] = token.attrGet("src") - # TODO ideally we would render proper markup here + # TODO ideally we would render proper markup here, + # this probably requires an upstream change in sphinx img_node["alt"] = self.renderInlineAsText(token.children) self.current_node.append(img_node) @@ -451,7 +467,7 @@ def render_front_matter(self, token): data = yaml.safe_load(token.content) except (yaml.parser.ParserError, yaml.scanner.ScannerError) as error: msg_node = self.reporter.error( - "Front matter block:\n" + str(error), line=token.map[0] + 1 + "Front matter block:\n" + str(error), line=token.map[0] ) msg_node += nodes.literal_block(token.content, token.content) self.current_node += [msg_node] @@ -606,7 +622,7 @@ def render_directive(self, token: Token): ) # type: (Directive, list) if not directive_class: error = self.reporter.error( - "Unknown directive type '{}'\n".format(name), + 'Unknown directive type "{}".\n'.format(name), # nodes.literal_block(content, content), line=position, ) @@ -619,7 +635,7 @@ def render_directive(self, token: Token): ) except DirectiveParsingError as error: error = self.reporter.error( - "Directive '{}':\n{}".format(name, error), + "Directive '{}': {}".format(name, error), nodes.literal_block(content, content), line=position, ) @@ -669,7 +685,7 @@ def render_directive(self, token: Token): result = [msg_node] except MockingError as exc: error = self.reporter.error( - "Directive '{}' cannot be mocked:\n{}: {}".format( + "Directive '{}' cannot be mocked: {}: {}".format( name, exc.__class__.__name__, exc ), nodes.literal_block(content, content), @@ -691,7 +707,6 @@ def render_directive(self, token: Token): def dict_to_docinfo(data): """Render a key/val pair as a docutils field node.""" - # TODO this data could be used to support default option values for directives docinfo = nodes.docinfo() for key, value in data.items(): diff --git a/myst_parser/mocking.py b/myst_parser/mocking.py index 3c069bae..456d6d55 100644 --- a/myst_parser/mocking.py +++ b/myst_parser/mocking.py @@ -295,7 +295,7 @@ def run(self): except Exception as error: raise DirectiveError( 4, - 'Directive "{}": error reading file: {}\n{error}.'.format( + 'Directive "{}": error reading file: {}\n{}.'.format( self.name, path, error ), ) diff --git a/setup.py b/setup.py index a4d4e6b5..74640ac1 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ ], keywords="markdown lexer parser development docutils sphinx", python_requires=">=3.6", - install_requires=["markdown-it-py~=0.4"], + install_requires=["markdown-it-py~=0.4.1"], extras_require={ "sphinx": ["pyyaml", "docutils>=0.15", "sphinx>=2,<3"], "code_style": ["flake8<3.8.0,>=3.7.0", "black", "pre-commit==1.17.0"], diff --git a/tests/test_renderers/fixtures/directive_options.md b/tests/test_renderers/fixtures/directive_options.md index e3edabc4..60ef8409 100644 --- a/tests/test_renderers/fixtures/directive_options.md +++ b/tests/test_renderers/fixtures/directive_options.md @@ -142,8 +142,7 @@ foo - Directive 'restructuredtext-test-directive': - Invalid options YAML: mapping values are not allowed here + Directive 'restructuredtext-test-directive': Invalid options YAML: mapping values are not allowed here in "", line 2, column 8: option2: b ^ @@ -163,7 +162,7 @@ Unknown Directive: - Unknown directive type 'unknown' + Unknown directive type "unknown". Problem retrieving directive entry from language module 'en': 'str' object has no attribute 'directives'. diff --git a/tests/test_renderers/fixtures/reporter_warnings.md b/tests/test_renderers/fixtures/reporter_warnings.md new file mode 100644 index 00000000..1d0d858d --- /dev/null +++ b/tests/test_renderers/fixtures/reporter_warnings.md @@ -0,0 +1,65 @@ +Duplicate Reference definitions: +. +[a]: b +[a]: c +. +source/path:2: (WARNING/2) Duplicate reference definition: A +. + +Missing Reference: +. +[a](b) +. +source/path:1: (WARNING/2) Reference not found: b +. + +Unknown role: +. +abc + +{xyz}`a` +. +source/path:3: (ERROR/3) Unknown interpreted text role "xyz". +. + +Unknown directive: +. + +```{xyz} +``` +. +source/path:2: (ERROR/3) Unknown directive type "xyz". +. + +Bad Front Matter: +. +--- +a: { +--- +. +source/path:1: (ERROR/3) Front matter block: +while parsing a flow node +expected the node content, but found '' + in "", line 1, column 5: + a: { + ^ +. + +Directive parsing error: +. + +```{class} +``` +. +source/path:2: (ERROR/3) Directive 'class': 1 argument(s) required, 0 supplied +. + +Directive run error: +. + +```{date} +x +``` +. +source/path:2: (ERROR/3) Invalid context: the "date" directive can only be used within a substitution definition. +. diff --git a/tests/test_renderers/fixtures/syntax_elements.md b/tests/test_renderers/fixtures/syntax_elements.md index 4e3d6cda..942f0bda 100644 --- a/tests/test_renderers/fixtures/syntax_elements.md +++ b/tests/test_renderers/fixtures/syntax_elements.md @@ -580,7 +580,7 @@ a: { --- . - + Front matter block: while parsing a flow node diff --git a/tests/test_renderers/test_error_reporting.py b/tests/test_renderers/test_error_reporting.py new file mode 100644 index 00000000..2c333d09 --- /dev/null +++ b/tests/test_renderers/test_error_reporting.py @@ -0,0 +1,27 @@ +from pathlib import Path + +import pytest + +from markdown_it.utils import read_fixture_file +from myst_parser.docutils_renderer import make_document +from myst_parser.main import to_docutils + + +FIXTURE_PATH = Path(__file__).parent.joinpath("fixtures") + + +@pytest.mark.parametrize( + "line,title,input,expected", + read_fixture_file(FIXTURE_PATH.joinpath("reporter_warnings.md")), +) +def test_basic(line, title, input, expected): + document = make_document("source/path") + messages = [] + + def observer(msg_node): + if msg_node["level"] > 1: + messages.append(msg_node.astext()) + + document.reporter.attach_observer(observer) + to_docutils(input, document=document, renderer="docutils") + assert "\n".join(messages).rstrip() == expected.rstrip()