From a8371a919b4f1552853a879377251ad25c3d988f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Mon, 6 Nov 2023 13:44:17 +0100 Subject: [PATCH] Make the first party extensions optional, add [extensions] extra --- pyproject.toml | 33 +++++++++++++++++++++++++++------ sphinx/application.py | 18 ++++++++++++++---- sphinx/testing/fixtures.py | 6 ++++++ tests/test_api_translator.py | 2 ++ tests/test_build_html.py | 3 +++ 5 files changed, 52 insertions(+), 10 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a0ada3cd3ae..7cb0112897a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,12 +55,6 @@ classifiers = [ "Topic :: Utilities", ] dependencies = [ - "sphinxcontrib-applehelp", - "sphinxcontrib-devhelp", - "sphinxcontrib-jsmath", - "sphinxcontrib-htmlhelp>=2.0.0", - "sphinxcontrib-serializinghtml>=1.1.9", - "sphinxcontrib-qthelp", "Jinja2>=3.0", "Pygments>=2.14", "docutils>=0.18.1,<0.21", @@ -76,8 +70,35 @@ dependencies = [ dynamic = ["version"] [project.optional-dependencies] +applehelp = [ + "sphinxcontrib-applehelp", +] +devhelp = [ + "sphinxcontrib-devhelp", +] +jsmath = [ + "sphinxcontrib-jsmath", +] +htmlhelp = [ + "sphinxcontrib-htmlhelp>=2.0.0", +] +serializinghtml = [ + "sphinxcontrib-serializinghtml>=1.1.9", +] +qthelp = [ + "sphinxcontrib-qthelp", +] +extensions = [ + "sphinx[applehelp]", + "sphinx[devhelp]", + "sphinx[jsmath]", + "sphinx[htmlhelp]", + "sphinx[serializinghtml]", + "sphinx[qthelp]", +] docs = [ "sphinxcontrib-websupport", + "sphinx[extensions]", ] lint = [ "flake8>=3.5.0", diff --git a/sphinx/application.py b/sphinx/application.py index d5fbaa9f30a..776bafa1ee3 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -24,7 +24,12 @@ from sphinx import locale, package_dir from sphinx.config import Config from sphinx.environment import BuildEnvironment -from sphinx.errors import ApplicationError, ConfigError, VersionRequirementError +from sphinx.errors import ( + ApplicationError, + ConfigError, + ExtensionError, + VersionRequirementError, +) from sphinx.events import EventManager from sphinx.highlighting import lexer_classes from sphinx.locale import __ @@ -226,7 +231,7 @@ def __init__(self, srcdir: str | os.PathLike[str], confdir: str | os.PathLike[st # load all built-in extension modules, first-party extension modules, # and first-party themes for extension in builtin_extensions: - self.setup_extension(extension) + self.setup_extension(extension, optional=extension in _first_party_extensions) # load all user-given extension modules for extension in self.config.extensions: @@ -395,7 +400,7 @@ def build(self, force_all: bool = False, filenames: list[str] | None = None) -> # ---- general extensibility interface ------------------------------------- - def setup_extension(self, extname: str) -> None: + def setup_extension(self, extname: str, optional: bool = False) -> None: """Import and setup a Sphinx extension module. Load the extension given by the module *name*. Use this if your @@ -403,7 +408,12 @@ def setup_extension(self, extname: str) -> None: called twice. """ logger.debug('[app] setting up extension: %r', extname) - self.registry.load_extension(self, extname) + try: + self.registry.load_extension(self, extname) + except ExtensionError: + logger.debug('[app] extension not found: %r', extname) + if not optional: + raise @staticmethod def require_sphinx(version: tuple[int, int] | str) -> None: diff --git a/sphinx/testing/fixtures.py b/sphinx/testing/fixtures.py index 0cc4882fe1c..f57f709b415 100644 --- a/sphinx/testing/fixtures.py +++ b/sphinx/testing/fixtures.py @@ -22,6 +22,7 @@ 'sphinx(builder, testroot=None, freshenv=False, confoverrides=None, tags=None,' ' docutilsconf=None, parallel=0): arguments to initialize the sphinx test application.' ), + 'sphinxcontrib(...): required sphinxcontrib.* extensions', 'test_params(shared_result=...): test parameters.', ] @@ -67,6 +68,11 @@ def app_params(request: Any, test_params: dict, shared_result: SharedResult, sphinx.application.Sphinx initialization """ + # ##### process pytest.mark.sphinxcontrib + for info in reversed(list(request.node.iter_markers("sphinxcontrib"))): + for arg in info.args: + pytest.importorskip("sphinxcontrib." + arg) + # ##### process pytest.mark.sphinx pargs = {} diff --git a/tests/test_api_translator.py b/tests/test_api_translator.py index 9f2bd448863..81575b71946 100644 --- a/tests/test_api_translator.py +++ b/tests/test_api_translator.py @@ -36,6 +36,7 @@ def test_singlehtml_set_translator_for_singlehtml(app, status, warning): assert translator_class.__name__ == 'ConfSingleHTMLTranslator' +@pytest.mark.sphinxcontrib('serializinghtml') @pytest.mark.sphinx('pickle', testroot='api-set-translator') def test_pickle_set_translator_for_pickle(app, status, warning): translator_class = app.builder.get_translator_class() @@ -43,6 +44,7 @@ def test_pickle_set_translator_for_pickle(app, status, warning): assert translator_class.__name__ == 'ConfPickleTranslator' +@pytest.mark.sphinxcontrib('serializinghtml') @pytest.mark.sphinx('json', testroot='api-set-translator') def test_json_set_translator_for_json(app, status, warning): translator_class = app.builder.get_translator_class() diff --git a/tests/test_build_html.py b/tests/test_build_html.py index a89a6fcafaf..8bd44111853 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -1547,6 +1547,7 @@ def test_html_math_renderer_is_imgmath(app, status, warning): assert app.builder.math_renderer_name == 'imgmath' +@pytest.mark.sphinxcontrib('serializinghtml', 'jsmath') @pytest.mark.sphinx('html', testroot='basic', confoverrides={'extensions': ['sphinxcontrib.jsmath', 'sphinx.ext.imgmath']}) @@ -1567,6 +1568,7 @@ def test_html_math_renderer_is_duplicated2(app, status, warning): assert app.builder.math_renderer_name == 'imgmath' # The another one is chosen +@pytest.mark.sphinxcontrib('jsmath') @pytest.mark.sphinx('html', testroot='basic', confoverrides={'extensions': ['sphinxcontrib.jsmath', 'sphinx.ext.imgmath'], @@ -1575,6 +1577,7 @@ def test_html_math_renderer_is_chosen(app, status, warning): assert app.builder.math_renderer_name == 'imgmath' +@pytest.mark.sphinxcontrib('jsmath') @pytest.mark.sphinx('html', testroot='basic', confoverrides={'extensions': ['sphinxcontrib.jsmath', 'sphinx.ext.mathjax'],