diff --git a/CHANGES.rst b/CHANGES.rst index 6f7279e8011..c60e72df936 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,49 +1,15 @@ -Release 9.1.0 (released Dec 31, 2025) -===================================== +Release 9.2.0 (in development) +============================== Dependencies ------------ -* #14153: Drop Python 3.11 support. -* #12555: Drop Docutils 0.20 support. - Patch by Adam Turner - Features added -------------- -* Add :meth:`~sphinx.application.Sphinx.add_static_dir` for copying static - assets from extensions to the build output. - Patch by Jared Dillard +* Add :confval:`linkcheck_ignore_status_codes` to ignore configurable status + codes for certain URLs. + Patch by Leo Singer Bugs fixed ---------- - -* #14189: autodoc: Fix duplicate ``:no-index-entry:`` for modules. - Patch by Adam Turner -* #13713: Fix compatibility with MyST-Parser. - Patch by Adam Turner -* Fix tests for Python 3.15. - Patch by Adam Turner -* #14089: autodoc: Fix default option parsing. - Patch by Adam Turner -* Remove incorrect static typing assertions. - Patch by Adam Turner -* #14050: LaTeXTranslator fails to build documents using the "acronym" - standard role. - Patch by Günter Milde -* LaTeX: Fix rendering for grid filled merged vertical cell. - Patch by Tim Nordell -* #14228: LaTeX: Fix overrun footer for cases of merged vertical table cells. - Patch by Tim Nordell -* #14207: Fix creating ``HTMLThemeFactory`` objects in third-party extensions. - Patch by Adam Turner -* #3099: LaTeX: PDF build crashes if a code-block contains more than - circa 1350 codelines (about 27 a4-sized pages at default pointsize). - Patch by Jean-François B. -* #14064: LaTeX: TABs ending up in sphinxVerbatim fail to obey tab stops. - Patch by Jean-François B. -* #14089: autodoc: Improve support for non-weakreferencable objects. - Patch by Adam Turner -* LaTeX: Fix accidental removal at ``3.5.0`` (#8854) of the documentation of - ``literalblockcappos`` key of :ref:`'sphinxsetup' `. - Patch by Jean-François B. diff --git a/doc/changes/9.1.rst b/doc/changes/9.1.rst new file mode 100644 index 00000000000..b3a70310531 --- /dev/null +++ b/doc/changes/9.1.rst @@ -0,0 +1,54 @@ +========== +Sphinx 9.1 +========== + + +Release 9.1.0 (released Dec 31, 2025) +===================================== + +Dependencies +------------ + +* #14153: Drop Python 3.11 support. +* #12555: Drop Docutils 0.20 support. + Patch by Adam Turner + +Features added +-------------- + +* Add :meth:`~sphinx.application.Sphinx.add_static_dir` for copying static + assets from extensions to the build output. + Patch by Jared Dillard + +Bugs fixed +---------- + +* #14189: autodoc: Fix duplicate ``:no-index-entry:`` for modules. + Patch by Adam Turner +* #13713: Fix compatibility with MyST-Parser. + Patch by Adam Turner +* Fix tests for Python 3.15. + Patch by Adam Turner +* #14089: autodoc: Fix default option parsing. + Patch by Adam Turner +* Remove incorrect static typing assertions. + Patch by Adam Turner +* #14050: LaTeXTranslator fails to build documents using the "acronym" + standard role. + Patch by Günter Milde +* LaTeX: Fix rendering for grid filled merged vertical cell. + Patch by Tim Nordell +* #14228: LaTeX: Fix overrun footer for cases of merged vertical table cells. + Patch by Tim Nordell +* #14207: Fix creating ``HTMLThemeFactory`` objects in third-party extensions. + Patch by Adam Turner +* #3099: LaTeX: PDF build crashes if a code-block contains more than + circa 1350 codelines (about 27 a4-sized pages at default pointsize). + Patch by Jean-François B. +* #14064: LaTeX: TABs ending up in sphinxVerbatim fail to obey tab stops. + Patch by Jean-François B. +* #14089: autodoc: Improve support for non-weakreferencable objects. + Patch by Adam Turner +* LaTeX: Fix accidental removal at ``3.5.0`` (#8854) of the documentation of + ``literalblockcappos`` key of :ref:`'sphinxsetup' `. + Patch by Jean-François B. diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 6bb45d41840..69a58aeb754 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -3952,6 +3952,23 @@ and the number of workers to use. .. versionadded:: 1.1 +.. confval:: linkcheck_ignore_status_codes + :type: :code-py:`dict` + :default: :code-py:`{}` + + A dictionary that maps regular expressions for URLs to status codes that + should be ignored. + + Example: + + .. code-block:: python + + linkcheck_ignore_status_codes = { + r"^https://doi.org/": [403] + } + + .. versionadded:: 9.2 + Domain options ============== diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index 5889e75d126..5cfab5345d4 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -393,6 +393,10 @@ def __init__( (re.compile(pattern), auth_info) for pattern, auth_info in config.linkcheck_auth ] + self.ignore_status_codes = [ + (re.compile(pattern), frozenset(codes)) + for pattern, codes in config.linkcheck_ignore_status_codes.items() + ] self.timeout: int | float | None = config.linkcheck_timeout self.request_headers: dict[str, dict[str, str]] = ( @@ -596,6 +600,16 @@ def _check_uri(self, uri: str, hyperlink: Hyperlink) -> _URIProperties: ) except HTTPError as err: + if any( + pattern.match(req_url) and status_code in codes + for pattern, codes in self.ignore_status_codes + ): + return ( + _Status.IGNORED, + f'ignored status: {status_code}', + status_code, + ) + error_message = str(err) # Unauthorized: the client did not provide required credentials @@ -844,8 +858,12 @@ def setup(app: Sphinx) -> ExtensionMetadata: '', types=frozenset({frozenset, list, set, tuple}), ) + app.add_config_value( + 'linkcheck_ignore_status_codes', {}, '', types=frozenset({dict}) + ) app.add_event('linkcheck-process-uri') + app.add_event('linkcheck-process-result') # priority 900 to happen after ``check_confval_types()`` app.connect('config-inited', compile_linkcheck_allowed_redirects, priority=900) diff --git a/tests/roots/test-linkcheck-ignore-status-codes/conf.py b/tests/roots/test-linkcheck-ignore-status-codes/conf.py new file mode 100644 index 00000000000..ed41a70d0b2 --- /dev/null +++ b/tests/roots/test-linkcheck-ignore-status-codes/conf.py @@ -0,0 +1,3 @@ +exclude_patterns = ['_build'] +linkcheck_timeout = 0.25 +linkcheck_ignore_status_codes = {r'^http://localhost:\d+/.*': [418]} diff --git a/tests/roots/test-linkcheck-ignore-status-codes/index.rst b/tests/roots/test-linkcheck-ignore-status-codes/index.rst new file mode 100644 index 00000000000..c55b6595c89 --- /dev/null +++ b/tests/roots/test-linkcheck-ignore-status-codes/index.rst @@ -0,0 +1 @@ +* `Example Foo `_ diff --git a/tests/test_builders/test_build_linkcheck.py b/tests/test_builders/test_build_linkcheck.py index 7b036ec4506..f0e3dafcee6 100644 --- a/tests/test_builders/test_build_linkcheck.py +++ b/tests/test_builders/test_build_linkcheck.py @@ -1522,3 +1522,21 @@ def test_linkcheck_case_sensitivity( assert rowsby[f'http://{address}/path1']['status'] == expected_path1 assert rowsby[f'http://{address}/path2']['status'] == expected_path2 assert rowsby[f'http://{address}/PATH3']['status'] == expected_path3 + + +@pytest.mark.sphinx( + 'linkcheck', + testroot='linkcheck-ignore-status-codes', + freshenv=True, +) +def test_ignore_status_codes(app: SphinxTestApp) -> None: + class TeapotServerErrorHandler(BaseHTTPRequestHandler): + protocol_version = 'HTTP/1.1' + + def do_GET(self) -> None: + self.send_error(418, "I'm a teapot") + + with serve_application(app, TeapotServerErrorHandler) as address: + app.build() + content = (app.outdir / 'output.txt').read_text(encoding='utf8') + assert not content