diff --git a/tests/conftest.py b/tests/conftest.py index 7952e9f2..5602cd41 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,53 +2,61 @@ import re +import pytest from docutils import __version_info__ as docutils_version_info DOCUTILS_0_22_PLUS = docutils_version_info >= (0, 22) -def normalize_doctree_xml(text: str) -> str: +@pytest.fixture +def normalize_doctree_xml(): """Normalize docutils XML output for cross-version compatibility. In docutils 0.22+, boolean attributes are serialized as "1"/"0" instead of "True"/"False". This function normalizes to the old format for consistent test fixtures. """ - if DOCUTILS_0_22_PLUS: - # Normalize new format (1/0) to old format (1/0) - # Only replace when it's clearly a boolean attribute value - # Pattern: attribute="1" or attribute="0" - attrs = [ - "force", - "glob", - "hidden", - "id_link", - "includehidden", - "inline", - "internal", - "is_div", - "linenos", - "multi_line_parameter_list", - "multi_line_trailing_comma", - "no-contents-entry", - "no-index", - "no-index-entry", - "no-typesetting", - "no-wrap", - "nocontentsentry", - "noindex", - "noindexentry", - "nowrap", - "refexplicit", - "refspecific", - "refwarn", - "sorted", - "titlesonly", - "toctree", - "translatable", - ] - text = re.sub(rf' ({"|".join(attrs)})="1"', r' \1="True"', text) - text = re.sub(rf' ({"|".join(attrs)})="0"', r' \1="False"', text) - # numbered is changed in math_block, but not in toctree, so we have to be more precise - text = re.sub(r' numbered="1" xml:space', r' numbered="True" xml:space', text) - return text + + def _normalize(text: str) -> str: + if DOCUTILS_0_22_PLUS: + # Normalize new format (1/0) to old format (1/0) + # Only replace when it's clearly a boolean attribute value + # Pattern: attribute="1" or attribute="0" + attrs = [ + "force", + "glob", + "hidden", + "id_link", + "includehidden", + "inline", + "internal", + "is_div", + "linenos", + "multi_line_parameter_list", + "multi_line_trailing_comma", + "no-contents-entry", + "no-index", + "no-index-entry", + "no-typesetting", + "no-wrap", + "nocontentsentry", + "noindex", + "noindexentry", + "nowrap", + "refexplicit", + "refspecific", + "refwarn", + "sorted", + "titlesonly", + "toctree", + "translatable", + ] + text = re.sub(rf' ({"|".join(attrs)})="1"', r' \1="True"', text) + text = re.sub(rf' ({"|".join(attrs)})="0"', r' \1="False"', text) + # numbered is changed in math_block, but not in toctree, so we have to be more precise + text = re.sub( + r' numbered="1" xml:space', r' numbered="True" xml:space', text + ) + return text + + return _normalize diff --git a/tests/test_html/test_html_to_nodes.py b/tests/test_html/test_html_to_nodes.py index 08423a79..d54f0f1f 100644 --- a/tests/test_html/test_html_to_nodes.py +++ b/tests/test_html/test_html_to_nodes.py @@ -6,7 +6,6 @@ from myst_parser.config.main import MdParserConfig from myst_parser.mdit_to_docutils.html_to_nodes import html_to_nodes -from tests.conftest import normalize_doctree_xml FIXTURE_PATH = Path(__file__).parent @@ -30,7 +29,7 @@ def _run_directive(name: str, first_line: str, content: str, position: int): @pytest.mark.param_file(FIXTURE_PATH / "html_to_nodes.md") -def test_html_to_nodes(file_params, mock_renderer): +def test_html_to_nodes(file_params, mock_renderer, normalize_doctree_xml): output = nodes.container() output += html_to_nodes(file_params.content, line_number=0, renderer=mock_renderer) file_params.assert_expected(normalize_doctree_xml(output.pformat()), rstrip=True) diff --git a/tests/test_renderers/test_fixtures_docutils.py b/tests/test_renderers/test_fixtures_docutils.py index c46e51c7..4988f078 100644 --- a/tests/test_renderers/test_fixtures_docutils.py +++ b/tests/test_renderers/test_fixtures_docutils.py @@ -11,7 +11,6 @@ from typing import Any import pytest -from conftest import normalize_doctree_xml from docutils import __version_info__ as docutils_version from docutils.core import Publisher, publish_doctree from pytest_param_files import ParamTestData @@ -22,7 +21,9 @@ @pytest.mark.param_file(FIXTURE_PATH / "docutil_syntax_elements.md") -def test_syntax_elements(file_params: ParamTestData, monkeypatch): +def test_syntax_elements( + file_params: ParamTestData, monkeypatch, normalize_doctree_xml +): """Test conversion of Markdown to docutils AST (before transforms are applied).""" def _apply_transforms(self): @@ -47,7 +48,7 @@ def _apply_transforms(self): @pytest.mark.param_file(FIXTURE_PATH / "docutil_link_resolution.md") -def test_link_resolution(file_params: ParamTestData): +def test_link_resolution(file_params: ParamTestData, normalize_doctree_xml): """Test that Markdown links resolve to the correct target, or give the correct warning.""" settings = settings_from_cmdline(file_params.description) report_stream = StringIO() @@ -70,7 +71,7 @@ def test_link_resolution(file_params: ParamTestData): @pytest.mark.param_file(FIXTURE_PATH / "docutil_roles.md") -def test_docutils_roles(file_params: ParamTestData, monkeypatch): +def test_docutils_roles(file_params: ParamTestData, monkeypatch, normalize_doctree_xml): """Test conversion of Markdown to docutils AST (before transforms are applied).""" def _apply_transforms(self): @@ -90,7 +91,9 @@ def _apply_transforms(self): @pytest.mark.param_file(FIXTURE_PATH / "docutil_directives.md") -def test_docutils_directives(file_params: ParamTestData, monkeypatch): +def test_docutils_directives( + file_params: ParamTestData, monkeypatch, normalize_doctree_xml +): """Test output of docutils directives.""" if "SKIP" in file_params.description: # line-block directive not yet supported pytest.skip(file_params.description) @@ -112,7 +115,7 @@ def _apply_transforms(self): @pytest.mark.param_file(FIXTURE_PATH / "docutil_syntax_extensions.txt") -def test_syntax_extensions(file_params: ParamTestData): +def test_syntax_extensions(file_params: ParamTestData, normalize_doctree_xml): """The description is parsed as a docutils commandline""" settings = settings_from_cmdline(file_params.description) report_stream = StringIO() diff --git a/tests/test_renderers/test_fixtures_sphinx.py b/tests/test_renderers/test_fixtures_sphinx.py index c061febc..4a8e1710 100644 --- a/tests/test_renderers/test_fixtures_sphinx.py +++ b/tests/test_renderers/test_fixtures_sphinx.py @@ -17,14 +17,16 @@ from sphinx_pytest.plugin import CreateDoctree from myst_parser.mdit_to_docutils.sphinx_ import SphinxRenderer -from tests.conftest import normalize_doctree_xml FIXTURE_PATH = Path(__file__).parent.joinpath("fixtures") @pytest.mark.param_file(FIXTURE_PATH / "sphinx_syntax_elements.md") def test_syntax_elements( - file_params: ParamTestData, sphinx_doctree: CreateDoctree, monkeypatch + file_params: ParamTestData, + sphinx_doctree: CreateDoctree, + monkeypatch, + normalize_doctree_xml, ): sphinx_doctree.set_conf({"extensions": ["myst_parser"], "show_warning_types": True}) @@ -52,7 +54,9 @@ def _apply_transforms(self): @pytest.mark.param_file(FIXTURE_PATH / "sphinx_link_resolution.md") -def test_link_resolution(file_params: ParamTestData, sphinx_doctree: CreateDoctree): +def test_link_resolution( + file_params: ParamTestData, sphinx_doctree: CreateDoctree, normalize_doctree_xml +): sphinx_doctree.set_conf( {"extensions": ["myst_parser"], **settings_from_json(file_params.description)} ) @@ -78,7 +82,11 @@ def settings_from_json(string: str | None): @pytest.mark.param_file(FIXTURE_PATH / "tables.md") -def test_tables(file_params: ParamTestData, sphinx_doctree_no_tr: CreateDoctree): +def test_tables( + file_params: ParamTestData, + sphinx_doctree_no_tr: CreateDoctree, + normalize_doctree_xml, +): sphinx_doctree_no_tr.set_conf({"extensions": ["myst_parser"]}) result = sphinx_doctree_no_tr(file_params.content, "index.md") file_params.assert_expected( @@ -88,7 +96,9 @@ def test_tables(file_params: ParamTestData, sphinx_doctree_no_tr: CreateDoctree) @pytest.mark.param_file(FIXTURE_PATH / "directive_options.md") def test_directive_options( - file_params: ParamTestData, sphinx_doctree_no_tr: CreateDoctree + file_params: ParamTestData, + sphinx_doctree_no_tr: CreateDoctree, + normalize_doctree_xml, ): sphinx_doctree_no_tr.set_conf({"extensions": ["myst_parser"]}) result = sphinx_doctree_no_tr(file_params.content, "index.md") @@ -99,7 +109,9 @@ def test_directive_options( @pytest.mark.param_file(FIXTURE_PATH / "sphinx_directives.md") def test_sphinx_directives( - file_params: ParamTestData, sphinx_doctree_no_tr: CreateDoctree + file_params: ParamTestData, + sphinx_doctree_no_tr: CreateDoctree, + normalize_doctree_xml, ): # TODO fix skipped directives # TODO test domain directives @@ -117,7 +129,11 @@ def test_sphinx_directives( @pytest.mark.param_file(FIXTURE_PATH / "sphinx_roles.md") -def test_sphinx_roles(file_params: ParamTestData, sphinx_doctree_no_tr: CreateDoctree): +def test_sphinx_roles( + file_params: ParamTestData, + sphinx_doctree_no_tr: CreateDoctree, + normalize_doctree_xml, +): if file_params.title.startswith("SKIP"): pytest.skip(file_params.title) @@ -140,7 +156,11 @@ def test_sphinx_roles(file_params: ParamTestData, sphinx_doctree_no_tr: CreateDo @pytest.mark.param_file(FIXTURE_PATH / "dollarmath.md") -def test_dollarmath(file_params: ParamTestData, sphinx_doctree_no_tr: CreateDoctree): +def test_dollarmath( + file_params: ParamTestData, + sphinx_doctree_no_tr: CreateDoctree, + normalize_doctree_xml, +): sphinx_doctree_no_tr.set_conf( {"extensions": ["myst_parser"], "myst_enable_extensions": ["dollarmath"]} ) @@ -152,7 +172,10 @@ def test_dollarmath(file_params: ParamTestData, sphinx_doctree_no_tr: CreateDoct @pytest.mark.param_file(FIXTURE_PATH / "amsmath.md") def test_amsmath( - file_params: ParamTestData, sphinx_doctree_no_tr: CreateDoctree, monkeypatch + file_params: ParamTestData, + sphinx_doctree_no_tr: CreateDoctree, + monkeypatch, + normalize_doctree_xml, ): monkeypatch.setattr(SphinxRenderer, "_random_label", lambda self: "mock-uuid") sphinx_doctree_no_tr.set_conf( @@ -166,7 +189,10 @@ def test_amsmath( @pytest.mark.param_file(FIXTURE_PATH / "containers.md") def test_containers( - file_params: ParamTestData, sphinx_doctree_no_tr: CreateDoctree, monkeypatch + file_params: ParamTestData, + sphinx_doctree_no_tr: CreateDoctree, + monkeypatch, + normalize_doctree_xml, ): monkeypatch.setattr(SphinxRenderer, "_random_label", lambda self: "mock-uuid") sphinx_doctree_no_tr.set_conf( @@ -180,7 +206,9 @@ def test_containers( @pytest.mark.param_file(FIXTURE_PATH / "eval_rst.md") def test_evalrst_elements( - file_params: ParamTestData, sphinx_doctree_no_tr: CreateDoctree + file_params: ParamTestData, + sphinx_doctree_no_tr: CreateDoctree, + normalize_doctree_xml, ): sphinx_doctree_no_tr.set_conf({"extensions": ["myst_parser"]}) result = sphinx_doctree_no_tr(file_params.content, "index.md") @@ -191,7 +219,9 @@ def test_evalrst_elements( @pytest.mark.param_file(FIXTURE_PATH / "definition_lists.md") def test_definition_lists( - file_params: ParamTestData, sphinx_doctree_no_tr: CreateDoctree + file_params: ParamTestData, + sphinx_doctree_no_tr: CreateDoctree, + normalize_doctree_xml, ): sphinx_doctree_no_tr.set_conf( {"extensions": ["myst_parser"], "myst_enable_extensions": ["deflist"]} @@ -203,7 +233,11 @@ def test_definition_lists( @pytest.mark.param_file(FIXTURE_PATH / "attributes.md") -def test_attributes(file_params: ParamTestData, sphinx_doctree_no_tr: CreateDoctree): +def test_attributes( + file_params: ParamTestData, + sphinx_doctree_no_tr: CreateDoctree, + normalize_doctree_xml, +): sphinx_doctree_no_tr.set_conf( { "extensions": ["myst_parser"], diff --git a/tests/test_renderers/test_include_directive.py b/tests/test_renderers/test_include_directive.py index e7065747..9f03d633 100644 --- a/tests/test_renderers/test_include_directive.py +++ b/tests/test_renderers/test_include_directive.py @@ -3,7 +3,6 @@ from pathlib import Path import pytest -from conftest import normalize_doctree_xml from docutils.core import publish_doctree from myst_parser.parsers.docutils_ import Parser @@ -12,7 +11,7 @@ @pytest.mark.param_file(FIXTURE_PATH / "mock_include.md") -def test_render(file_params, tmp_path, monkeypatch): +def test_render(file_params, tmp_path, monkeypatch, normalize_doctree_xml): monkeypatch.chdir(tmp_path) tmp_path.joinpath("other.md").write_text("a\nb\nc") diff --git a/tests/test_renderers/test_myst_config.py b/tests/test_renderers/test_myst_config.py index 9a276cce..5805bd13 100644 --- a/tests/test_renderers/test_myst_config.py +++ b/tests/test_renderers/test_myst_config.py @@ -5,7 +5,6 @@ from pathlib import Path import pytest -from conftest import normalize_doctree_xml from docutils import __version_info__ as docutils_version from docutils.core import Publisher, publish_string from pytest_param_files import ParamTestData @@ -17,7 +16,7 @@ @pytest.mark.param_file(FIXTURE_PATH / "myst-config.txt") -def test_cmdline(file_params: ParamTestData): +def test_cmdline(file_params: ParamTestData, normalize_doctree_xml): """The description is parsed as a docutils commandline""" if file_params.title == "attrs_image" and docutils_version < (0, 22): # loose system messages are also output to ast in 0.22 https://github.com/live-clones/docutils/commit/dc4e16315b4fbe391417a6f7aad215b9389a9c74 diff --git a/tests/test_renderers/test_myst_refs.py b/tests/test_renderers/test_myst_refs.py index fcdfd619..b9c74bf0 100644 --- a/tests/test_renderers/test_myst_refs.py +++ b/tests/test_renderers/test_myst_refs.py @@ -1,7 +1,6 @@ import sys import pytest -from conftest import normalize_doctree_xml from sphinx.util.console import strip_colors from sphinx_pytest.plugin import CreateDoctree @@ -42,6 +41,7 @@ def test_parse( should_warn: bool, sphinx_doctree: CreateDoctree, file_regression, + normalize_doctree_xml, ): sphinx_doctree.set_conf({"extensions": ["myst_parser"], "show_warning_types": True}) result = sphinx_doctree(text, "index.md") diff --git a/tests/test_sphinx/conftest.py b/tests/test_sphinx/conftest.py index ddace0d6..87bf6110 100644 --- a/tests/test_sphinx/conftest.py +++ b/tests/test_sphinx/conftest.py @@ -41,7 +41,6 @@ def test_basic(app, status, warning, get_sphinx_app_output): from docutils import nodes from myst_parser._compat import findall -from tests.conftest import normalize_doctree_xml SOURCE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "sourcedirs")) @@ -95,7 +94,7 @@ def read( @pytest.fixture -def get_sphinx_app_doctree(file_regression): +def get_sphinx_app_doctree(file_regression, normalize_doctree_xml): def read( app, docname="index", diff --git a/tests/test_sphinx/test_sphinx_builds.py b/tests/test_sphinx/test_sphinx_builds.py index 0276b247..4ac336e4 100644 --- a/tests/test_sphinx/test_sphinx_builds.py +++ b/tests/test_sphinx/test_sphinx_builds.py @@ -13,8 +13,6 @@ import pytest from sphinx.util.console import strip_colors -from tests.conftest import normalize_doctree_xml - SOURCE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "sourcedirs")) @@ -433,6 +431,7 @@ def test_substitutions( get_sphinx_app_doctree, get_sphinx_app_output, file_regression, + normalize_doctree_xml, ): """test setting addition configuration values.""" app.build()