diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 8e246f0749e..f73c1a5fc44 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -16,7 +16,8 @@ from sphinx.events import EventManager from sphinx.io import read_doc from sphinx.locale import __ -from sphinx.util import import_object, logging, progress_message, rst, status_iterator +from sphinx.util import (get_filetype, import_object, logging, progress_message, rst, + status_iterator) from sphinx.util.build_phase import BuildPhase from sphinx.util.console import bold # type: ignore from sphinx.util.docutils import sphinx_domains @@ -465,7 +466,8 @@ def read_doc(self, docname: str) -> None: self.env.note_dependency(docutilsconf) filename = self.env.doc2path(docname) - publisher = self.app.registry.create_publisher(self.app, filename) + filetype = get_filetype(self.app.config.source_suffix, filename) + publisher = self.app.registry.get_publisher(self.app, filetype) with sphinx_domains(self.env), rst.default_role(docname, self.config.default_role): doctree = read_doc(publisher, docname, filename) diff --git a/sphinx/io.py b/sphinx/io.py index 57e2e0899b3..95b5a74fab4 100644 --- a/sphinx/io.py +++ b/sphinx/io.py @@ -5,7 +5,7 @@ from docutils import nodes from docutils.core import Publisher from docutils.frontend import Values -from docutils.io import FileInput, Input +from docutils.io import FileInput, Input, NullOutput from docutils.parsers import Parser from docutils.readers import standalone from docutils.transforms import Transform @@ -165,3 +165,31 @@ def read_doc(publisher: Publisher, docname: str, filename: str) -> nodes.documen # settings get modified in ``write_doctree``; get a local copy doctree.settings = doctree.settings.copy() return doctree + + +def create_publisher(app, filetype): + reader = SphinxStandaloneReader() + reader.setup(app) + + parser = app.registry.create_source_parser(app, filetype) + if parser.__class__.__name__ == 'CommonMarkParser' and parser.settings_spec == (): + # a workaround for recommonmark + # If recommonmark.AutoStrictify is enabled, the parser invokes reST parser + # internally. But recommonmark-0.4.0 does not provide settings_spec for reST + # parser. As a workaround, this copies settings_spec for RSTParser to the + # CommonMarkParser. + from docutils.parsers.rst import Parser as RSTParser + + parser.settings_spec = RSTParser.settings_spec + + pub = Publisher( + reader=reader, + parser=parser, + writer=SphinxDummyWriter(), + source_class=SphinxFileInput, + destination=NullOutput() + ) + # Propagate exceptions by default when used programmatically: + defaults = {"traceback": True, **app.env.settings} + # Set default settings + pub.settings = pub.setup_option_parser(**defaults).get_default_values() # type: ignore diff --git a/sphinx/registry.py b/sphinx/registry.py index 1edc90ebc2a..6770abb02a2 100644 --- a/sphinx/registry.py +++ b/sphinx/registry.py @@ -9,7 +9,7 @@ from docutils import nodes from docutils.core import Publisher -from docutils.io import Input, NullOutput +from docutils.io import Input from docutils.nodes import Element, Node, TextElement from docutils.parsers import Parser from docutils.parsers.rst import Directive @@ -28,11 +28,11 @@ from sphinx.environment import BuildEnvironment from sphinx.errors import ExtensionError, SphinxError, VersionRequirementError from sphinx.extension import Extension -from sphinx.io import SphinxDummyWriter, SphinxFileInput, SphinxStandaloneReader +from sphinx.io import create_publisher from sphinx.locale import __ from sphinx.parsers import Parser as SphinxParser from sphinx.roles import XRefRole -from sphinx.util import get_filetype, logging +from sphinx.util import logging from sphinx.util.logging import prefixed_warnings from sphinx.util.typing import RoleFunction, TitleGetter @@ -128,7 +128,7 @@ def __init__(self) -> None: self.transforms: List[Type[Transform]] = [] # private cache of Docutils Publishers (file type -> publisher object) - self._publishers: Dict[str, Publisher] = {} + self.publishers: Dict[str, Publisher] = {} def add_builder(self, builder: Type[Builder], override: bool = False) -> None: logger.debug('[app] adding builder: %r', builder) @@ -466,40 +466,14 @@ def get_envversion(self, app: "Sphinx") -> Dict[str, str]: envversion['sphinx'] = ENV_VERSION return envversion - def create_publisher(self, app: "Sphinx", filename: str) -> Publisher: - filetype = get_filetype(app.config.source_suffix, filename) + def get_publisher(self, app: "Sphinx", filetype: str) -> Publisher: try: - return self._publishers[filetype] + return self.publishers[filetype] except KeyError: pass - - reader = SphinxStandaloneReader() - reader.setup(app) - - parser = app.registry.create_source_parser(app, filetype) - if parser.__class__.__name__ == 'CommonMarkParser' and parser.settings_spec == (): - # a workaround for recommonmark - # If recommonmark.AutoStrictify is enabled, the parser invokes reST parser - # internally. But recommonmark-0.4.0 does not provide settings_spec for reST - # parser. As a workaround, this copies settings_spec for RSTParser to the - # CommonMarkParser. - from docutils.parsers.rst import Parser as RSTParser - - parser.settings_spec = RSTParser.settings_spec - - pub = Publisher( - reader=reader, - parser=parser, - writer=SphinxDummyWriter(), - source_class=SphinxFileInput, - destination=NullOutput() - ) - # Propagate exceptions by default when used programmatically: - defaults = {"traceback": True, **app.env.settings} - # Set default settings - pub.settings = pub.setup_option_parser(**defaults).get_default_values() # type: ignore - self._publishers[filetype] = pub - return pub + publisher = create_publisher(app, filetype) + self.publishers[filetype] = publisher + return publisher def merge_source_suffix(app: "Sphinx", config: Config) -> None: