Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Contributors
* Erik Bedard -- config options for :mod:`sphinx.ext.duration`
* Etienne Desautels -- apidoc module
* Ezio Melotti -- collapsible sidebar JavaScript
* Fazeel Usmani -- parser API fixes
* Filip Vavera -- napoleon todo directive
* Florian Best -- log improvements
* Glenn Matthews -- python domain signature improvements
Expand Down
13 changes: 13 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
Release 9.2.0 (in development)
==============================

Deprecated
----------

* #14371: Un-deprecate :py:attr:`!Parser.config` and :py:attr:`!Parser.env`.
These remain the supported way for a custom parser to read the build
configuration and environment.
:py:meth:`!Parser.set_application` continues to be deprecated and is
scheduled for removal in Sphinx 10.
Patch by Fazeel Usmani.

Release 9.1.0 (released Dec 31, 2025)
=====================================

Expand Down
33 changes: 33 additions & 0 deletions doc/extdev/parserapi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,36 @@ to configure their settings appropriately.

.. autoclass:: Parser
:members:

Accessing the Sphinx config and environment
-------------------------------------------

A custom parser can read Sphinx :class:`~sphinx.config.Config` values and the
:class:`~sphinx.environment.BuildEnvironment` through the inherited
``Parser.config`` and ``Parser.env`` properties. Sphinx populates the
backing ``_config`` and ``_env`` attributes when it instantiates a parser
registered via :meth:`.Sphinx.add_source_parser`, so they are available
from the start of ``parse()``.

.. code-block:: python

from sphinx.parsers import Parser


class MyParser(Parser):
supported = ('mytype',)

def parse(self, inputstring, document):
# ``self.config`` and ``self.env`` are populated by Sphinx
encoding = self.config.source_encoding
docname = self.env.docname
...

.. versionchanged:: 9.2
``Parser.config`` and ``Parser.env`` are no longer deprecated.

.. deprecated:: 9.0
The ``Parser.set_application`` hook is deprecated and will be removed in
Sphinx 10. Sphinx now populates ``_config`` and ``_env`` directly when
the parser instance is created, so custom parsers no longer need to
override ``set_application`` to capture these objects.
34 changes: 25 additions & 9 deletions sphinx/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,40 @@ class Parser(docutils.parsers.Parser):

@property
def config(self) -> Config:
"""The config object."""
cls_module = self.__class__.__module__
cls_name = self.__class__.__qualname__
_deprecation_warning(cls_module, f'{cls_name}.config', remove=(10, 0))
"""The config object.

Populated by Sphinx when it instantiates a parser registered via
:meth:`.Sphinx.add_source_parser`, so this is available from the
start of ``parse()``.

.. versionchanged:: 9.2
No longer deprecated.
"""
return self._config

@property
def env(self) -> BuildEnvironment:
"""The environment object."""
cls_module = self.__class__.__module__
cls_name = self.__class__.__qualname__
_deprecation_warning(cls_module, f'{cls_name}.env', remove=(10, 0))
"""The environment object.

Populated by Sphinx when it instantiates a parser registered via
:meth:`.Sphinx.add_source_parser`, so this is available from the
start of ``parse()``.

.. versionchanged:: 9.2
No longer deprecated.
"""
return self._env

def set_application(self, app: Sphinx) -> None:
"""set_application will be called from Sphinx to set app and other instance variables
"""Legacy compatibility hook for receiving the Sphinx application.

:param sphinx.application.Sphinx app: Sphinx application object

.. deprecated:: 9.0
Sphinx now makes ``config`` and ``env`` available on the parser
instance automatically, so custom parsers no longer need to
override this method to capture the application. It will be
removed in Sphinx 10.
"""
cls_module = self.__class__.__module__
cls_name = self.__class__.__qualname__
Expand Down
37 changes: 36 additions & 1 deletion tests/test_markup/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

from __future__ import annotations

import warnings
from typing import TYPE_CHECKING
from unittest.mock import Mock, patch

import pytest

from sphinx.parsers import RSTParser
from sphinx.deprecation import RemovedInSphinx10Warning
from sphinx.parsers import Parser, RSTParser
from sphinx.util.docutils import new_document

if TYPE_CHECKING:
Expand Down Expand Up @@ -68,3 +70,36 @@ def test_RSTParser_prolog_epilog(RSTStateMachine: Mock, app: SphinxTestApp) -> N
('dummy.rst', 0, ' hello Sphinx world'),
('dummy.rst', 1, ' Sphinx is a document generator'),
]


@pytest.mark.sphinx('html', testroot='basic')
def test_parser_config_env_populated_by_registry(app: SphinxTestApp) -> None:
"""Parsers built via the registry expose config/env without warning (#14371).

This goes through the real construction path in
:meth:`.SphinxComponentRegistry.create_source_parser` so a regression in
that code path (for example, removal of the ``isinstance(..., SphinxParser)``
gate) would be caught here.
"""
parser = app.registry.create_source_parser(
'restructuredtext', config=app.config, env=app.env
)
# ``create_source_parser`` is typed as ``docutils.parsers.Parser``; narrow
# to the Sphinx base class so ``config`` / ``env`` access type-checks and
# so a future regression that stops wrapping Sphinx parsers is caught here.
assert isinstance(parser, Parser)

with warnings.catch_warnings():
warnings.simplefilter('error', RemovedInSphinx10Warning)
assert parser.config is app.config
assert parser.env is app.env


@pytest.mark.sphinx('html', testroot='basic')
def test_parser_set_application_still_deprecated(app: SphinxTestApp) -> None:
"""``Parser.set_application`` must still emit a deprecation warning (#14371)."""
parser = RSTParser()
with pytest.warns(RemovedInSphinx10Warning, match='set_application'):
parser.set_application(app)
assert parser.config is app.config
assert parser.env is app.env
Loading