diff --git a/doc/development/tutorials/examples/todo.py b/doc/development/tutorials/examples/todo.py index 25aedfb0543..a8aa1ec4a1d 100644 --- a/doc/development/tutorials/examples/todo.py +++ b/doc/development/tutorials/examples/todo.py @@ -76,7 +76,7 @@ def process_todo_nodes(app, doctree, fromdocname): # Replace all todolist nodes with a list of the collected todos. # Augment each todo with a backlink to the original location. - env = app.builder.env + env = app.env if not hasattr(env, 'todo_all_todos'): env.todo_all_todos = [] diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 71f89cdcc50..fa181480934 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -48,6 +48,7 @@ if TYPE_CHECKING: from collections.abc import Iterable, Sequence, Set + from pathlib import Path from docutils.nodes import Node @@ -242,8 +243,8 @@ def compile_catalogs(self, catalogs: set[CatalogInfo], message: str) -> None: if not self.config.gettext_auto_build: return - def cat2relpath(cat: CatalogInfo) -> str: - return relpath(cat.mo_path, self.env.srcdir).replace(os.path.sep, SEP) + def cat2relpath(cat: CatalogInfo, srcdir: Path = self.srcdir) -> str: + return relpath(cat.mo_path, srcdir).replace(os.path.sep, SEP) logger.info(bold(__('building [mo]: ')) + message) for catalog in status_iterator( @@ -615,14 +616,15 @@ def merge(docs: list[str], otherenv: bytes) -> None: @final def read_doc(self, docname: str, *, _cache: bool = True) -> None: """Parse a file and add/update inventory entries for the doctree.""" - self.env.prepare_settings(docname) + env = self.env + env.prepare_settings(docname) # Add confdir/docutils.conf to dependencies list if exists docutilsconf = os.path.join(self.confdir, 'docutils.conf') if os.path.isfile(docutilsconf): - self.env.note_dependency(docutilsconf) + env.note_dependency(docutilsconf) - filename = str(self.env.doc2path(docname)) + filename = str(env.doc2path(docname)) filetype = get_filetype(self.app.config.source_suffix, filename) publisher = self.app.registry.get_publisher(self.app, filetype) self.env.current_document._parser = publisher.parser @@ -630,7 +632,7 @@ def read_doc(self, docname: str, *, _cache: bool = True) -> None: # explicitly re-initialise for each document publisher.settings.record_dependencies = DependencyList() with ( - sphinx_domains(self.env), + sphinx_domains(env), rst.default_role(docname, self.config.default_role), ): # set up error_handler for the target document @@ -642,11 +644,11 @@ def read_doc(self, docname: str, *, _cache: bool = True) -> None: doctree = publisher.document # store time of reading, for outdated files detection - self.env.all_docs[docname] = time.time_ns() // 1_000 + env.all_docs[docname] = time.time_ns() // 1_000 # cleanup - self.env.current_document = _CurrentDocument() - self.env.ref_context.clear() + env.current_document = _CurrentDocument() + env.ref_context.clear() self.write_doctree(docname, doctree, _cache=_cache) diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py index 62eb1177c48..3ce79947c48 100644 --- a/sphinx/builders/changes.py +++ b/sphinx/builders/changes.py @@ -128,7 +128,7 @@ def hl(no: int, line: str) -> str: logger.info(bold(__('copying source files...'))) for docname in self.env.all_docs: with open( - self.env.doc2path(docname), encoding=self.env.config.source_encoding + self.env.doc2path(docname), encoding=self.config.source_encoding ) as f: try: lines = f.readlines() diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index c6ac25ed085..62e2959a37f 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -146,10 +146,8 @@ class I18nBuilder(Builder): def init(self) -> None: super().init() - self.env.set_versioning_method( - self.versioning_method, self.env.config.gettext_uuid - ) - self.tags = I18nTags() + self.env.set_versioning_method(self.versioning_method, self.config.gettext_uuid) + self.tags = self.app.tags = I18nTags() self.catalogs: defaultdict[str, Catalog] = defaultdict(Catalog) def get_target_uri(self, docname: str, typ: str | None = None) -> str: @@ -174,7 +172,7 @@ def write_doc(self, docname: str, doctree: nodes.document) -> None: if not _is_node_in_substitution_definition(node): catalog.add(msg, node) - if 'index' in self.env.config.gettext_additional_targets: + if 'index' in self.config.gettext_additional_targets: # Extract translatable messages from index entries. for node, entries in traverse_translatable_index(doctree): for entry_type, value, _target_id, _main, _category_key in entries: diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index 9a1001fceaf..0e5b352635c 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -771,7 +771,7 @@ def write_domain_indices(self) -> None: def copy_image_files(self) -> None: if self.images: - stringify_func = ImageAdapter(self.app.env).get_original_image_uri + stringify_func = ImageAdapter(self.env).get_original_image_uri ensuredir(self._images_dir) for src in status_iterator( self.images, diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 489b0d542dc..5e979817b92 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -476,7 +476,7 @@ def copy_latex_additional_files(self) -> None: def copy_image_files(self) -> None: if self.images: - stringify_func = ImageAdapter(self.app.env).get_original_image_uri + stringify_func = ImageAdapter(self.env).get_original_image_uri for src in status_iterator( self.images, __('copying images... '), diff --git a/sphinx/builders/texinfo.py b/sphinx/builders/texinfo.py index 7143e68c4f0..a2f37e373ad 100644 --- a/sphinx/builders/texinfo.py +++ b/sphinx/builders/texinfo.py @@ -196,7 +196,7 @@ def copy_assets(self) -> None: def copy_image_files(self, targetname: str) -> None: if self.images: - stringify_func = ImageAdapter(self.app.env).get_original_image_uri + stringify_func = ImageAdapter(self.env).get_original_image_uri for src in status_iterator( self.images, __('copying images... '), diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index 1488d4ee391..b56a915596b 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -267,7 +267,7 @@ def run(self) -> list[Node]: finally: # Private attributes for ToC generation. Will be modified or removed # without notice. - if self.env.config.toc_object_entries: + if self.config.toc_object_entries: signode['_toc_parts'] = self._object_hierarchy_parts(signode) signode['_toc_name'] = self._toc_entry_name(signode) else: diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py index f564367d867..53ff96f8d5b 100644 --- a/sphinx/domains/__init__.py +++ b/sphinx/domains/__init__.py @@ -107,6 +107,7 @@ class Domain: data_version = 0 def __init__(self, env: BuildEnvironment) -> None: + domain_data: dict[str, dict[str, Any]] = env.domaindata self.env: BuildEnvironment = env self._role_cache: dict[str, RoleFunction] = {} self._directive_cache: dict[str, type[Directive]] = {} @@ -119,13 +120,13 @@ def __init__(self, env: BuildEnvironment) -> None: self.roles = dict(self.roles) self.indices = list(self.indices) - if self.name not in env.domaindata: + if self.name not in domain_data: assert isinstance(self.initial_data, dict) new_data = copy.deepcopy(self.initial_data) new_data['version'] = self.data_version - self.data = env.domaindata[self.name] = new_data + self.data = domain_data[self.name] = new_data else: - self.data = env.domaindata[self.name] + self.data = domain_data[self.name] if self.data['version'] != self.data_version: raise OSError('data of %r domain out of date' % self.label) for name, obj in self.object_types.items(): diff --git a/sphinx/domains/c/__init__.py b/sphinx/domains/c/__init__.py index 992d95b932d..4ce698b98ac 100644 --- a/sphinx/domains/c/__init__.py +++ b/sphinx/domains/c/__init__.py @@ -219,7 +219,7 @@ def describe_signature( ast.describe_signature(signode, 'lastIsName', self.env, options) def run(self) -> list[Node]: - env = self.state.document.settings.env # from ObjectDescription.run + env = self.env if env.current_document.c_parent_symbol is None: root = env.domaindata['c']['root_symbol'] env.current_document.c_parent_symbol = root @@ -235,8 +235,8 @@ def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration: parent_symbol: Symbol = self.env.current_document.c_parent_symbol max_len = ( - self.env.config.c_maximum_signature_line_length - or self.env.config.maximum_signature_line_length + self.config.c_maximum_signature_line_length + or self.config.maximum_signature_line_length or 0 ) signode['multi_line_parameter_list'] = ( @@ -244,7 +244,7 @@ def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration: and (len(sig) > max_len > 0) ) - parser = DefinitionParser(sig, location=signode, config=self.env.config) + parser = DefinitionParser(sig, location=signode, config=self.config) try: ast = self.parse_definition(parser) parser.assert_end() @@ -400,7 +400,7 @@ def run(self) -> list[Node]: stack: list[Symbol] = [] else: parser = DefinitionParser( - self.arguments[0], location=self.get_location(), config=self.env.config + self.arguments[0], location=self.get_location(), config=self.config ) try: name = parser.parse_namespace_object() @@ -427,7 +427,7 @@ def run(self) -> list[Node]: if self.arguments[0].strip() in {'NULL', '0', 'nullptr'}: return [] parser = DefinitionParser( - self.arguments[0], location=self.get_location(), config=self.env.config + self.arguments[0], location=self.get_location(), config=self.config ) try: name = parser.parse_namespace_object() @@ -567,7 +567,7 @@ def apply(self, **kwargs: Any) -> None: sig = node.sig parent_key = node.parentKey try: - parser = DefinitionParser(sig, location=node, config=self.env.config) + parser = DefinitionParser(sig, location=node, config=self.config) name = parser.parse_xref_object() except DefinitionError as e: logger.warning(e, location=node) @@ -716,7 +716,7 @@ def __init__(self, asCode: bool) -> None: def run(self) -> tuple[list[Node], list[system_message]]: text = self.text.replace('\n', ' ') parser = DefinitionParser( - text, location=self.get_location(), config=self.env.config + text, location=self.get_location(), config=self.config ) # attempt to mimic XRefRole classes, except that... try: diff --git a/sphinx/domains/cpp/__init__.py b/sphinx/domains/cpp/__init__.py index b696e529b43..08c596b0374 100644 --- a/sphinx/domains/cpp/__init__.py +++ b/sphinx/domains/cpp/__init__.py @@ -259,7 +259,7 @@ def add_target_and_index( break if not is_in_concept and 'no-index-entry' not in self.options: stripped_name = name - for prefix in self.env.config.cpp_index_common_prefix: + for prefix in self.config.cpp_index_common_prefix: if name.startswith(prefix): stripped_name = stripped_name[len(prefix) :] break @@ -308,7 +308,7 @@ def describe_signature( ast.describe_signature(signode, 'lastIsName', self.env, options) def run(self) -> list[Node]: - env = self.state.document.settings.env # from ObjectDescription.run + env = self.env if env.current_document.cpp_parent_symbol is None: root = env.domaindata['cpp']['root_symbol'] env.current_document.cpp_parent_symbol = root @@ -348,8 +348,8 @@ def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration: parent_symbol: Symbol = self.env.current_document.cpp_parent_symbol max_len = ( - self.env.config.cpp_maximum_signature_line_length - or self.env.config.maximum_signature_line_length + self.config.cpp_maximum_signature_line_length + or self.config.maximum_signature_line_length or 0 ) signode['multi_line_parameter_list'] = ( @@ -357,7 +357,7 @@ def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration: and (len(sig) > max_len > 0) ) - parser = DefinitionParser(sig, location=signode, config=self.env.config) + parser = DefinitionParser(sig, location=signode, config=self.config) try: ast = self.parse_definition(parser) parser.assert_end() @@ -440,7 +440,7 @@ def _toc_entry_name(self, sig_node: desc_signature) -> str: if not sig_node.get('_toc_parts'): return '' - config = self.env.config + config = self.config objtype = sig_node.parent.get('objtype') if config.add_function_parentheses and objtype in {'function', 'method'}: parens = '()' @@ -710,7 +710,7 @@ def apply(self, **kwargs: Any) -> None: sig = node.sig parent_key = node.parentKey try: - parser = DefinitionParser(sig, location=node, config=self.env.config) + parser = DefinitionParser(sig, location=node, config=self.config) ast, is_shorthand = parser.parse_xref_object() parser.assert_end() except DefinitionError as e: diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index 77162ade5d2..36b7f15e3c1 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -101,8 +101,8 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] signode['fullname'] = fullname max_len = ( - self.env.config.javascript_maximum_signature_line_length - or self.env.config.maximum_signature_line_length + self.config.javascript_maximum_signature_line_length + or self.config.maximum_signature_line_length or 0 ) multi_line_parameter_list = ( @@ -238,7 +238,7 @@ def _toc_entry_name(self, sig_node: desc_signature) -> str: if not sig_node.get('_toc_parts'): return '' - config = self.env.config + config = self.config objtype = sig_node.parent.get('objtype') if config.add_function_parentheses and objtype in {'function', 'method'}: parens = '()' diff --git a/sphinx/domains/python/__init__.py b/sphinx/domains/python/__init__.py index 9683e4a7bc8..7cd61b1e3e3 100644 --- a/sphinx/domains/python/__init__.py +++ b/sphinx/domains/python/__init__.py @@ -261,7 +261,7 @@ def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str: name, cls = name_cls try: clsname, methname = name.rsplit('.', 1) - if modname and self.env.config.add_module_names: + if modname and self.config.add_module_names: clsname = f'{modname}.{clsname}' except ValueError: if modname: @@ -357,7 +357,7 @@ def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str: name, cls = name_cls try: clsname, attrname = name.rsplit('.', 1) - if modname and self.env.config.add_module_names: + if modname and self.config.add_module_names: clsname = f'{modname}.{clsname}' except ValueError: if modname: @@ -417,7 +417,7 @@ def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str: name, cls = name_cls try: clsname, attrname = name.rsplit('.', 1) - if modname and self.env.config.add_module_names: + if modname and self.config.add_module_names: clsname = f'{modname}.{clsname}' except ValueError: if modname: @@ -457,7 +457,7 @@ def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str: name, cls = name_cls try: clsname, attrname = name.rsplit('.', 1) - if modname and self.env.config.add_module_names: + if modname and self.config.add_module_names: clsname = f'{modname}.{clsname}' except ValueError: if modname: diff --git a/sphinx/domains/python/_object.py b/sphinx/domains/python/_object.py index f8472823657..6fa288e0fdc 100644 --- a/sphinx/domains/python/_object.py +++ b/sphinx/domains/python/_object.py @@ -286,8 +286,8 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] signode['fullname'] = fullname max_len = ( - self.env.config.python_maximum_signature_line_length - or self.env.config.maximum_signature_line_length + self.config.python_maximum_signature_line_length + or self.config.maximum_signature_line_length or 0 ) @@ -321,7 +321,7 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] if prefix: signode += addnodes.desc_addname(prefix, prefix) - elif modname and add_module and self.env.config.add_module_names: + elif modname and add_module and self.config.add_module_names: nodetext = f'{modname}.' signode += addnodes.desc_addname(nodetext, nodetext) @@ -474,7 +474,7 @@ def _toc_entry_name(self, sig_node: desc_signature) -> str: if not sig_node.get('_toc_parts'): return '' - config = self.env.config + config = self.config objtype = sig_node.parent.get('objtype') if config.add_function_parentheses and objtype in {'function', 'method'}: parens = '()' diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index 3493aec68cb..54b1d9007f4 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -84,12 +84,11 @@ def _toc_entry_name(self, sig_node: desc_signature) -> str: if not sig_node.get('_toc_parts'): return '' - config = self.env.config objtype = sig_node.parent.get('objtype') *parents, name = sig_node['_toc_parts'] if objtype == 'directive:option': return f':{name}:' - if config.toc_object_entries_show_parents in {'domain', 'all'}: + if self.config.toc_object_entries_show_parents in {'domain', 'all'}: name = ':'.join(sig_node['_toc_parts']) if objtype == 'role': return f':{name}:' diff --git a/sphinx/domains/std/__init__.py b/sphinx/domains/std/__init__.py index cd5478a7fc8..f28bd835ec6 100644 --- a/sphinx/domains/std/__init__.py +++ b/sphinx/domains/std/__init__.py @@ -258,13 +258,13 @@ def handle_signature(self, sig: str, signode: desc_signature) -> str: args = '[' + args if count: - if self.env.config.option_emphasise_placeholders: + if self.config.option_emphasise_placeholders: signode += addnodes.desc_sig_punctuation(',', ',') signode += addnodes.desc_sig_space() else: signode += addnodes.desc_addname(', ', ', ') signode += addnodes.desc_name(optname, optname) - if self.env.config.option_emphasise_placeholders: + if self.config.option_emphasise_placeholders: add_end_bracket = False if args: if args[0] == '[' and args[-1] == ']': diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index 74b75d6921b..f8774e46a99 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -695,6 +695,7 @@ def get_and_resolve_doctree( toctreenode, prune=prune_toctrees, includehidden=includehidden, + tags=builder.tags, ) if result is None: toctreenode.parent.replace(toctreenode, []) @@ -735,6 +736,7 @@ def resolve_toctree( titles_only=titles_only, collapse=collapse, includehidden=includehidden, + tags=builder.tags, ) def resolve_references( diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py index 3079c7dc543..89a386a9432 100644 --- a/sphinx/environment/adapters/toctree.py +++ b/sphinx/environment/adapters/toctree.py @@ -86,6 +86,7 @@ def global_toctree_for_doc( titles_only=titles_only, collapse=collapse, includehidden=includehidden, + tags=builder.tags, ) for toctree_node in env.master_doctree.findall(addnodes.toctree) ) @@ -110,6 +111,7 @@ def _resolve_toctree( titles_only: bool = False, collapse: bool = False, includehidden: bool = False, + tags: Tags, ) -> Element | None: """Resolve a *toctree* node into individual bullet lists with titles as items, returning None (if no containing titles are found) or @@ -159,7 +161,7 @@ def _resolve_toctree( titles_only, collapse, includehidden, - builder.tags, + tags, toctree_ancestors, included, excluded, @@ -558,13 +560,14 @@ def resolve( titles_only=titles_only, collapse=collapse, includehidden=includehidden, + tags=builder.tags, ) def get_toctree_ancestors(self, docname: str) -> list[str]: return [*_get_toctree_ancestors(self.env.toctree_includes, docname)] def get_toc_for(self, docname: str, builder: Builder) -> Node: - return document_toc(self.env, docname, self.env.app.builder.tags) + return document_toc(self.env, docname, self.env.app.tags) def get_toctree_for( self, diff --git a/sphinx/environment/collectors/asset.py b/sphinx/environment/collectors/asset.py index d4cb24d1126..24f8b275e8b 100644 --- a/sphinx/environment/collectors/asset.py +++ b/sphinx/environment/collectors/asset.py @@ -73,9 +73,9 @@ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None: # Search language-specific figures at first i18n_imguri = get_image_filename_for_language(imguri, app.env) _, full_i18n_imgpath = app.env.relfn2path(i18n_imguri, docname) - self.collect_candidates(app.env, full_i18n_imgpath, candidates, node) + self.collect_candidates(app.srcdir, full_i18n_imgpath, candidates, node) - self.collect_candidates(app.env, full_imgpath, candidates, node) + self.collect_candidates(app.srcdir, full_imgpath, candidates, node) else: # substitute imguri by figure_language_filename # (ex. foo.png -> foo.en.png) @@ -106,14 +106,14 @@ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None: def collect_candidates( self, - env: BuildEnvironment, + srcdir: Path, imgpath: str, candidates: dict[str, str], node: Node, ) -> None: globbed: dict[str, list[str]] = {} for filename in glob(imgpath): - new_imgpath = _relative_path(Path(filename), env.srcdir) + new_imgpath = _relative_path(Path(filename), srcdir) try: mimetype = guess_mimetype(filename) if mimetype is None: diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index f6ae4890fad..64abac28638 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -46,7 +46,8 @@ from typing import ClassVar, Literal, TypeAlias from sphinx.application import Sphinx - from sphinx.environment import BuildEnvironment + from sphinx.environment import BuildEnvironment, _CurrentDocument + from sphinx.events import EventManager from sphinx.ext.autodoc.directive import DocumenterBridge _AutodocObjType = Literal[ @@ -370,6 +371,8 @@ def __init__( self.directive = directive self.config: Config = directive.env.config self.env: BuildEnvironment = directive.env + self._current_document: _CurrentDocument = directive.env.current_document + self._events: EventManager = directive.env.events self.options = directive.genopt self.name = name self.indent = indent @@ -554,7 +557,7 @@ def format_signature(self, **kwargs: Any) -> str: ) args = None - result = self.env.events.emit_firstresult( + result = self._events.emit_firstresult( 'autodoc-process-signature', self.objtype, self.fullname, @@ -613,9 +616,9 @@ def get_doc(self) -> list[list[str]] | None: def process_doc(self, docstrings: list[list[str]]) -> Iterator[str]: """Let the user process the docstrings before adding them.""" for docstringlines in docstrings: - if self.env.app: + if self._events is not None: # let extensions preprocess docstrings - self.env.events.emit( + self._events.emit( 'autodoc-process-docstring', self.objtype, self.fullname, @@ -840,9 +843,9 @@ def is_filtered_inherited_member(name: str, obj: Any) -> bool: # give the user a chance to decide whether this member # should be skipped - if self.env.app: + if self._events is not None: # let extensions preprocess docstrings - skip_user = self.env.events.emit_firstresult( + skip_user = self._events.emit_firstresult( 'autodoc-skip-member', self.objtype, membername, @@ -878,9 +881,9 @@ def document_members(self, all_members: bool = False) -> None: *self.options.members*. """ # set current namespace for finding members - self.env.current_document.autodoc_module = self.modname + self._current_document.autodoc_module = self.modname if self.objpath: - self.env.current_document.autodoc_class = self.objpath[0] + self._current_document.autodoc_class = self.objpath[0] want_all = ( all_members or self.options.inherited_members or self.options.members is ALL @@ -918,8 +921,8 @@ def document_members(self, all_members: bool = False) -> None: ) # reset current objects - self.env.current_document.autodoc_module = '' - self.env.current_document.autodoc_class = '' + self._current_document.autodoc_module = '' + self._current_document.autodoc_class = '' def sort_members( self, documenters: list[tuple[Documenter, bool]], order: str @@ -1261,7 +1264,7 @@ def resolve_name( # if documenting a toplevel object without explicit module, # it can be contained in another auto directive ... - modname = self.env.current_document.autodoc_module + modname = self._current_document.autodoc_module # ... or in the scope of a module directive if not modname: modname = self.env.ref_context.get('py:module') @@ -1287,7 +1290,7 @@ def resolve_name( # if documenting a class-level object without path, # there must be a current class, either from a parent # auto directive ... - mod_cls = self.env.current_document.autodoc_class + mod_cls = self._current_document.autodoc_class # ... or from a class directive if not mod_cls: mod_cls = self.env.ref_context.get('py:class', '') @@ -1298,7 +1301,7 @@ def resolve_name( parents = [cls] # if the module name is still missing, get it like above if not modname: - modname = self.env.current_document.autodoc_module + modname = self._current_document.autodoc_module if not modname: modname = self.env.ref_context.get('py:module') # ... else, it stays None, which means invalid @@ -1432,7 +1435,7 @@ def format_args(self, **kwargs: Any) -> str: kwargs.setdefault('unqualified_typehints', True) try: - self.env.events.emit('autodoc-before-process-signature', self.object, False) + self._events.emit('autodoc-before-process-signature', self.object, False) sig = inspect.signature( self.object, type_aliases=self.config.autodoc_type_aliases ) @@ -1685,7 +1688,7 @@ def get_user_defined_function_or_method(obj: Any, attr: str) -> Any: call = None if call is not None: - self.env.events.emit('autodoc-before-process-signature', call, True) + self._events.emit('autodoc-before-process-signature', call, True) try: sig = inspect.signature( call, @@ -1704,7 +1707,7 @@ def get_user_defined_function_or_method(obj: Any, attr: str) -> Any: new = None if new is not None: - self.env.events.emit('autodoc-before-process-signature', new, True) + self._events.emit('autodoc-before-process-signature', new, True) try: sig = inspect.signature( new, @@ -1718,7 +1721,7 @@ def get_user_defined_function_or_method(obj: Any, attr: str) -> Any: # Finally, we should have at least __init__ implemented init = get_user_defined_function_or_method(self.object, '__init__') if init is not None: - self.env.events.emit('autodoc-before-process-signature', init, True) + self._events.emit('autodoc-before-process-signature', init, True) try: sig = inspect.signature( init, @@ -1733,7 +1736,7 @@ def get_user_defined_function_or_method(obj: Any, attr: str) -> Any: # handle it. # We don't know the exact method that inspect.signature will read # the signature from, so just pass the object itself to our hook. - self.env.events.emit('autodoc-before-process-signature', self.object, False) + self._events.emit('autodoc-before-process-signature', self.object, False) try: sig = inspect.signature( self.object, @@ -1886,7 +1889,7 @@ def add_directive_header(self, sig: str) -> None: else: bases = [] - self.env.events.emit( + self._events.emit( 'autodoc-process-bases', self.fullname, self.object, self.options, bases ) @@ -2387,7 +2390,7 @@ def format_args(self, **kwargs: Any) -> str: if inspect.isstaticmethod( self.object, cls=self.parent, name=self.object_name ): - self.env.events.emit( + self._events.emit( 'autodoc-before-process-signature', self.object, False ) sig = inspect.signature( @@ -2396,7 +2399,7 @@ def format_args(self, **kwargs: Any) -> str: type_aliases=self.config.autodoc_type_aliases, ) else: - self.env.events.emit( + self._events.emit( 'autodoc-before-process-signature', self.object, True ) sig = inspect.signature( @@ -3049,7 +3052,7 @@ def format_args(self, **kwargs: Any) -> str: return '' # update the annotations of the property getter - self.env.events.emit('autodoc-before-process-signature', func, False) + self._events.emit('autodoc-before-process-signature', func, False) # correctly format the arguments for a property return super().format_args(**kwargs) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 3b51817b9a7..0c045424a90 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -870,7 +870,7 @@ def process_generate_options(app: Sphinx) -> None: genfiles = app.config.autosummary_generate if genfiles is True: - env = app.builder.env + env = app.env genfiles = [ str(env.doc2path(x, base=False)) for x in env.found_docs diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index 45a906f28f5..979a447886b 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -240,6 +240,7 @@ def fix_svg_relative_paths( self: HTML5Translator | LaTeXTranslator | TexinfoTranslator, filepath: str ) -> None: """Change relative links in generated svg files to be relative to imgpath.""" + env = self.builder.env tree = ET.parse(filepath) # NoQA: S314 root = tree.getroot() ns = {'svg': 'http://www.w3.org/2000/svg', 'xlink': 'http://www.w3.org/1999/xlink'} @@ -255,11 +256,11 @@ def fix_svg_relative_paths( # not a relative link continue - docname = self.builder.env.path2doc(self.document['source']) + docname = env.path2doc(self.document['source']) if docname is None: # This shouldn't happen! continue - doc_dir = self.builder.app.outdir.joinpath(docname).resolve().parent + doc_dir = self.builder.outdir.joinpath(docname).resolve().parent old_path = doc_dir / rel_uri img_path = doc_dir / self.builder.imgpath diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index c4381b7aff1..dd4321349a4 100644 --- a/sphinx/ext/inheritance_diagram.py +++ b/sphinx/ext/inheritance_diagram.py @@ -58,7 +58,7 @@ class E(B): pass from docutils.nodes import Node from sphinx.application import Sphinx - from sphinx.environment import BuildEnvironment + from sphinx.config import Config from sphinx.util.typing import ExtensionMetadata, OptionSpec from sphinx.writers.html5 import HTML5Translator from sphinx.writers.latex import LaTeXTranslator @@ -302,7 +302,7 @@ def generate_dot( self, name: str, urls: dict[str, str] | None = None, - env: BuildEnvironment | None = None, + config: Config | None = None, graph_attrs: dict | None = None, node_attrs: dict | None = None, edge_attrs: dict | None = None, @@ -328,10 +328,10 @@ def generate_dot( n_attrs.update(node_attrs) if edge_attrs is not None: e_attrs.update(edge_attrs) - if env: - g_attrs.update(env.config.inheritance_graph_attrs) - n_attrs.update(env.config.inheritance_node_attrs) - e_attrs.update(env.config.inheritance_edge_attrs) + if config: + g_attrs.update(config.inheritance_graph_attrs) + n_attrs.update(config.inheritance_node_attrs) + e_attrs.update(config.inheritance_edge_attrs) res: list[str] = [ f'digraph {name} {{\n', @@ -450,7 +450,7 @@ def html_visit_inheritance_diagram( name = 'inheritance%s' % graph_hash # Create a mapping from fully-qualified class names to URLs. - graphviz_output_format = self.builder.env.config.graphviz_output_format.upper() + graphviz_output_format = self.config.graphviz_output_format.upper() current_filename = os.path.basename( self.builder.current_docname + self.builder.out_suffix ) @@ -471,7 +471,7 @@ def html_visit_inheritance_diagram( else: urls[child['reftitle']] = '#' + child.get('refid') - dotcode = graph.generate_dot(name, urls, env=self.builder.env) + dotcode = graph.generate_dot(name, urls, config=self.config) render_dot_html( self, node, @@ -496,7 +496,7 @@ def latex_visit_inheritance_diagram( name = 'inheritance%s' % graph_hash dotcode = graph.generate_dot( - name, env=self.builder.env, graph_attrs={'size': '"6.0,6.0"'} + name, config=self.config, graph_attrs={'size': '"6.0,6.0"'} ) render_dot_latex(self, node, dotcode, {}, 'inheritance') raise nodes.SkipNode @@ -515,7 +515,7 @@ def texinfo_visit_inheritance_diagram( name = 'inheritance%s' % graph_hash dotcode = graph.generate_dot( - name, env=self.builder.env, graph_attrs={'size': '"6.0,6.0"'} + name, config=self.config, graph_attrs={'size': '"6.0,6.0"'} ) render_dot_texinfo(self, node, dotcode, {}, 'inheritance') raise nodes.SkipNode diff --git a/sphinx/ext/intersphinx/_load.py b/sphinx/ext/intersphinx/_load.py index b8257b9165d..c7ea85a0d79 100644 --- a/sphinx/ext/intersphinx/_load.py +++ b/sphinx/ext/intersphinx/_load.py @@ -140,8 +140,9 @@ def load_mappings(app: Sphinx) -> None: The intersphinx mappings are expected to be normalized. """ + env = app.env now = int(time.time()) - inventories = InventoryAdapter(app.builder.env) + inventories = InventoryAdapter(env) intersphinx_cache: dict[InventoryURI, InventoryCacheEntry] = inventories.cache intersphinx_mapping: IntersphinxMapping = app.config.intersphinx_mapping diff --git a/sphinx/ext/linkcode.py b/sphinx/ext/linkcode.py index 6643ea7f862..61d2c2e7383 100644 --- a/sphinx/ext/linkcode.py +++ b/sphinx/ext/linkcode.py @@ -40,7 +40,7 @@ class LinkcodeError(SphinxError): def doctree_read(app: Sphinx, doctree: Node) -> None: - env = app.builder.env + env = app.env resolve_target = getattr(env.config, 'linkcode_resolve', None) if not callable(env.config.linkcode_resolve): diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py index 39b8a5fc8cc..698aad355ae 100644 --- a/sphinx/ext/viewcode.py +++ b/sphinx/ext/viewcode.py @@ -87,7 +87,7 @@ def is_supported_builder(builder: Builder) -> bool: def doctree_read(app: Sphinx, doctree: Node) -> None: - env = app.builder.env + env = app.env if not hasattr(env, '_viewcode_modules'): env._viewcode_modules = {} # type: ignore[attr-defined] @@ -251,7 +251,7 @@ def should_generate_module_page(app: Sphinx, modname: str) -> bool: def collect_pages(app: Sphinx) -> Iterator[tuple[str, dict[str, Any], str]]: - env = app.builder.env + env = app.env if not hasattr(env, '_viewcode_modules'): return if not is_supported_builder(app.builder): diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 3f19d3663a0..683df0b6bbf 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -272,7 +272,8 @@ class IndexBuilder: def __init__( self, env: BuildEnvironment, lang: str, options: dict[str, str], scoring: str ) -> None: - self.env = env + self._domains = env.domains + self._env_version = env.version # docname -> title self._titles: dict[str, str | None] = env._search_index_titles # docname -> filename @@ -323,7 +324,10 @@ def load(self, stream: IO, format: Any) -> None: format = self.formats[format] frozen = format.load(stream) # if an old index is present, we treat it as not existing. - if not isinstance(frozen, dict) or frozen.get('envversion') != self.env.version: + if ( + not isinstance(frozen, dict) + or frozen.get('envversion') != self._env_version + ): msg = 'old format' raise ValueError(msg) index2fn = frozen['docnames'] @@ -362,7 +366,7 @@ def get_objects( rv: dict[str, list[tuple[int, int, int, str, str]]] = {} otypes = self._objtypes onames = self._objnames - for domain in self.env.domains.sorted(): + for domain in self._domains.sorted(): sorted_objects = sorted(domain.get_objects()) for fullname, dispname, type, docname, anchor, prio in sorted_objects: if docname not in fn2index: @@ -452,7 +456,7 @@ def freeze(self) -> dict[str, Any]: 'objtypes': objtypes, 'objnames': objnames, 'titleterms': title_terms, - 'envversion': self.env.version, + 'envversion': self._env_version, 'alltitles': alltitles, 'indexentries': index_entries, } diff --git a/sphinx/testing/restructuredtext.py b/sphinx/testing/restructuredtext.py index 38fe96d79b4..b04b61a4021 100644 --- a/sphinx/testing/restructuredtext.py +++ b/sphinx/testing/restructuredtext.py @@ -16,20 +16,21 @@ def parse(app: Sphinx, text: str, docname: str = 'index') -> nodes.document: """Parse a string as reStructuredText with Sphinx application.""" + env = app.env try: app.env.current_document.docname = docname reader = SphinxStandaloneReader() reader.setup(app) parser = RSTParser() parser.set_application(app) - with sphinx_domains(app.env): + with sphinx_domains(env): return publish_doctree( text, str(app.srcdir / f'{docname}.rst'), reader=reader, parser=parser, settings_overrides={ - 'env': app.env, + 'env': env, 'gettext_compact': True, 'input_encoding': 'utf-8', 'output_encoding': 'unicode', @@ -37,4 +38,4 @@ def parse(app: Sphinx, text: str, docname: str = 'index') -> nodes.document: }, ) finally: - app.env.current_document.docname = '' + env.current_document.docname = '' diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py index de880c9c27f..011b3081356 100644 --- a/sphinx/transforms/post_transforms/__init__.py +++ b/sphinx/transforms/post_transforms/__init__.py @@ -277,7 +277,7 @@ def run(self, **kwargs: Any) -> None: # result in a "Losing ids" exception if there is a target node before # the only node, so we make sure docutils can transfer the id to # something, even if it's just a comment and will lose the id anyway... - process_only_nodes(self.document, self.app.builder.tags) + process_only_nodes(self.document, self.app.tags) class SigElementFallbackTransform(SphinxPostTransform): diff --git a/sphinx/transforms/post_transforms/images.py b/sphinx/transforms/post_transforms/images.py index 7040952ac14..6a2da874a28 100644 --- a/sphinx/transforms/post_transforms/images.py +++ b/sphinx/transforms/post_transforms/images.py @@ -83,7 +83,7 @@ def _download_image(self, node: nodes.image, path: Path) -> None: timestamp: float = ceil(path.stat().st_mtime) headers['If-Modified-Since'] = epoch_to_rfc1123(timestamp) - config = self.app.config + config = self.config r = requests.get( node['uri'], headers=headers, @@ -94,7 +94,7 @@ def _download_image(self, node: nodes.image, path: Path) -> None: msg = __('Could not fetch remote image: %s [%d]') logger.warning(msg, node['uri'], r.status_code) else: - self.app.env.original_image_uri[_StrPath(path)] = node['uri'] + self.env.original_image_uri[_StrPath(path)] = node['uri'] if r.status_code == 200: path.write_bytes(r.content) @@ -106,7 +106,7 @@ def _download_image(self, node: nodes.image, path: Path) -> None: def _process_image(self, node: nodes.image, path: Path) -> None: str_path = _StrPath(path) - self.app.env.original_image_uri[str_path] = node['uri'] + self.env.original_image_uri[str_path] = node['uri'] mimetype = guess_mimetype(path, default='*') if mimetype != '*' and not path.suffix: @@ -114,14 +114,14 @@ def _process_image(self, node: nodes.image, path: Path) -> None: ext = get_image_extension(mimetype) or '' with_ext = path.with_name(path.name + ext) os.replace(path, with_ext) - self.app.env.original_image_uri.pop(str_path) - self.app.env.original_image_uri[_StrPath(with_ext)] = node['uri'] + self.env.original_image_uri.pop(str_path) + self.env.original_image_uri[_StrPath(with_ext)] = node['uri'] path = with_ext path_str = str(path) node['candidates'].pop('?') node['candidates'][mimetype] = path_str node['uri'] = path_str - self.app.env.images.add_file(self.env.docname, path_str) + self.env.images.add_file(self.env.docname, path_str) class DataURIExtractor(BaseImageConverter): @@ -145,7 +145,7 @@ def handle(self, node: nodes.image) -> None: ensuredir(os.path.join(self.imagedir, 'embeded')) digest = sha1(image.data, usedforsecurity=False).hexdigest() path = _StrPath(self.imagedir, 'embeded', digest + ext) - self.app.env.original_image_uri[path] = node['uri'] + self.env.original_image_uri[path] = node['uri'] with open(path, 'wb') as f: f.write(image.data) @@ -154,7 +154,7 @@ def handle(self, node: nodes.image) -> None: node['candidates'].pop('?') node['candidates'][image.mimetype] = path_str node['uri'] = path_str - self.app.env.images.add_file(self.env.docname, path_str) + self.env.images.add_file(self.env.docname, path_str) def get_filename_for(filename: str, mimetype: str) -> str: diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py index 1bfcd7451e4..2f14de03ffa 100644 --- a/sphinx/util/docfields.py +++ b/sphinx/util/docfields.py @@ -410,7 +410,7 @@ def transform(self, node: nodes.field_list) -> None: self.directive.domain or '', target, contnode=content[0], - env=self.directive.state.document.settings.env, + env=self.directive.env, ) if _is_single_paragraph(field_body): paragraph = cast('nodes.paragraph', field_body[0]) @@ -477,7 +477,7 @@ def transform(self, node: nodes.field_list) -> None: else: fieldtype, items, location = entry fieldtypes = types.get(fieldtype.name, {}) - env = self.directive.state.document.settings.env + env = self.directive.env inliner = self.directive.state.inliner domain = self.directive.domain or '' new_list += fieldtype.make_field( diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index 5a26bc88e10..0c91f4df195 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -726,6 +726,7 @@ def __init__(self, document: nodes.document, builder: Builder) -> None: self.builder = builder self.config = builder.config self.settings = document.settings + self._domains = builder.env.domains def dispatch_visit(self, node: Node) -> None: """ diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 2675405198f..e96e537bf2a 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -419,9 +419,7 @@ def append_fignumber(figtype: str, figure_id: str) -> None: self.body.append(prefix % '.'.join(map(str, numbers)) + ' ') self.body.append('') - figtype = self.builder.env.domains.standard_domain.get_enumerable_node_type( - node - ) + figtype = self._domains.standard_domain.get_enumerable_node_type(node) if figtype: if len(node['ids']) == 0: msg = __('Any IDs not assigned for %s node') % node.tagname diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index cb07c0bf615..ea6ab38222f 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -560,7 +560,7 @@ def generate( indices_config = frozenset(indices_config) else: check_names = False - for domain in self.builder.env.domains.sorted(): + for domain in self._domains.sorted(): for index_cls in domain.indices: index_name = f'{domain.name}-{index_cls.name}' if check_names and index_name not in indices_config: @@ -1816,7 +1816,7 @@ def add_target(id: str) -> None: while isinstance(next_node, nodes.target): next_node = next_node.next_node(ascend=True) - domain = self.builder.env.domains.standard_domain + domain = self._domains.standard_domain if isinstance(next_node, HYPERLINK_SUPPORT_NODES): return if ( diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index bae5714425e..db737dbb4cd 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -10,7 +10,6 @@ from docutils import nodes, writers from sphinx import __display_version__, addnodes -from sphinx.errors import ExtensionError from sphinx.locale import _, __, admonitionlabels from sphinx.util import logging from sphinx.util.docutils import SphinxTranslator @@ -493,7 +492,7 @@ def generate( indices_config = frozenset(indices_config) else: check_names = False - for domain in self.builder.env.domains.sorted(): + for domain in self._domains.sorted(): for index_cls in domain.indices: index_name = f'{domain.name}-{index_cls.name}' if check_names and index_name not in indices_config: @@ -507,7 +506,7 @@ def generate( generate(content, collapsed), )) # only add the main Index if it's not empty - domain = self.builder.env.domains.index_domain + domain = self._domains.index_domain for docname in self.builder.docnames: if domain.entries[docname]: self.indices.append((_('Index'), '\n@printindex ge\n')) @@ -1420,11 +1419,11 @@ def visit_desc_signature(self, node: Element) -> None: self.add_anchor(id, node) # use the full name of the objtype for the category try: - domain = self.builder.env.get_domain(node.parent['domain']) + domain = self._domains[node.parent['domain']] name = domain.get_type_name( domain.object_types[objtype], self.config.primary_domain == domain.name ) - except (KeyError, ExtensionError): + except KeyError: name = objtype # by convention, the deffn category should be capitalized like a title category = self.escape_arg(smart_capwords(name)) diff --git a/sphinx/writers/xml.py b/sphinx/writers/xml.py index 825f6da5ca7..f909e3406f9 100644 --- a/sphinx/writers/xml.py +++ b/sphinx/writers/xml.py @@ -16,10 +16,11 @@ class XMLWriter(BaseXMLWriter): # type: ignore[misc] def __init__(self, builder: Builder) -> None: super().__init__() self.builder = builder + self._config = builder.config def translate(self, *args: Any, **kwargs: Any) -> None: self.document.settings.newlines = self.document.settings.indents = ( - self.builder.env.config.xml_pretty + self._config.xml_pretty ) self.document.settings.xml_declaration = True self.document.settings.doctype_declaration = True diff --git a/tests/test_directives/test_directive_object_description.py b/tests/test_directives/test_directive_object_description.py index 7f3ffba633a..501a6c9772a 100644 --- a/tests/test_directives/test_directive_object_description.py +++ b/tests/test_directives/test_directive_object_description.py @@ -14,21 +14,24 @@ from sphinx.util.docutils import sphinx_domains if TYPE_CHECKING: - from sphinx.builders import Builder + from sphinx.application import Sphinx + from sphinx.environment import BuildEnvironment -def _doctree_for_test(builder: Builder, docname: str) -> nodes.document: - builder.env.prepare_settings(docname) - publisher = create_publisher(builder.app, 'restructuredtext') - with sphinx_domains(builder.env): - publisher.set_source(source_path=str(builder.env.doc2path(docname))) +def _doctree_for_test( + app: Sphinx, env: BuildEnvironment, docname: str +) -> nodes.document: + env.prepare_settings(docname) + publisher = create_publisher(app, 'restructuredtext') + with sphinx_domains(env): + publisher.set_source(source_path=str(env.doc2path(docname))) publisher.publish() return publisher.document @pytest.mark.sphinx('text', testroot='object-description-sections') def test_object_description_sections(app): - doctree = _doctree_for_test(app.builder, 'index') + doctree = _doctree_for_test(app, app.env, 'index') # # # diff --git a/tests/test_environment/test_environment_toctree.py b/tests/test_environment/test_environment_toctree.py index 0020fb9a161..890fd596bf8 100644 --- a/tests/test_environment/test_environment_toctree.py +++ b/tests/test_environment/test_environment_toctree.py @@ -452,7 +452,7 @@ def test_domain_objects_document_scoping(app): @pytest.mark.test_params(shared_result='test_environment_toctree_basic') def test_document_toc(app): app.build() - toctree = document_toc(app.env, 'index', app.builder.tags) + toctree = document_toc(app.env, 'index', app.tags) assert_node( toctree, @@ -502,8 +502,8 @@ def test_document_toc(app): @pytest.mark.test_params(shared_result='test_environment_toctree_basic') def test_document_toc_only(app): app.build() - builder = StandaloneHTMLBuilder(app, app.env) - toctree = document_toc(app.env, 'index', builder.tags) + StandaloneHTMLBuilder(app, app.env) # adds format/builder tags + toctree = document_toc(app.env, 'index', app.tags) assert_node( toctree, @@ -561,7 +561,7 @@ def test_document_toc_only(app): @pytest.mark.test_params(shared_result='test_environment_toctree_basic') def test_document_toc_tocdepth(app): app.build() - toctree = document_toc(app.env, 'tocdepth', app.builder.tags) + toctree = document_toc(app.env, 'tocdepth', app.tags) assert_node( toctree, diff --git a/tests/test_extensions/test_ext_autodoc.py b/tests/test_extensions/test_ext_autodoc.py index 89d8d037269..63f7f090ec5 100644 --- a/tests/test_extensions/test_ext_autodoc.py +++ b/tests/test_extensions/test_ext_autodoc.py @@ -10,17 +10,18 @@ import itertools import operator import sys -from types import SimpleNamespace from typing import TYPE_CHECKING from unittest.mock import Mock from warnings import catch_warnings import pytest -from docutils.statemachine import ViewList from sphinx import addnodes from sphinx.ext.autodoc import ALL, ModuleLevelDocumenter, Options +# NEVER import these objects from sphinx.ext.autodoc directly +from sphinx.ext.autodoc.directive import DocumenterBridge + from tests.test_extensions.autodoc_util import do_autodoc try: @@ -34,8 +35,10 @@ if TYPE_CHECKING: from typing import Any + from sphinx.environment import BuildEnvironment + -def make_directive_bridge(env): +def make_directive_bridge(env: BuildEnvironment) -> DocumenterBridge: options = Options( inherited_members=False, undoc_members=False, @@ -54,11 +57,11 @@ def make_directive_bridge(env): ignore_module_all=False, ) - directive = SimpleNamespace( + directive = DocumenterBridge( env=env, - genopt=options, - result=ViewList(), - record_dependencies=set(), + reporter=None, + options=options, + lineno=0, state=Mock(), ) directive.state.document.settings.tab_width = 8 diff --git a/tests/test_markup/test_markup.py b/tests/test_markup/test_markup.py index 62c4482ae55..09deb90ff0f 100644 --- a/tests/test_markup/test_markup.py +++ b/tests/test_markup/test_markup.py @@ -26,6 +26,7 @@ @pytest.fixture def settings(app): + env = app.env texescape.init() # otherwise done by the latex builder with warnings.catch_warnings(): warnings.filterwarnings('ignore', category=DeprecationWarning) @@ -37,10 +38,10 @@ def settings(app): ) settings = optparser.get_default_values() settings.smart_quotes = True - settings.env = app.builder.env + settings.env = env settings.env.current_document.docname = 'dummy' settings.contentsname = 'dummy' - domain_context = sphinx_domains(settings.env) + domain_context = sphinx_domains(env) domain_context.enable() yield settings domain_context.disable() diff --git a/tests/test_markup/test_parser.py b/tests/test_markup/test_parser.py index 3f40a21693a..eb8ccf24f1d 100644 --- a/tests/test_markup/test_parser.py +++ b/tests/test_markup/test_parser.py @@ -29,7 +29,7 @@ def test_RSTParser_prolog_epilog(RSTStateMachine, app): ] # with rst_prolog - app.env.config.rst_prolog = 'this is rst_prolog\nhello reST!' + app.config.rst_prolog = 'this is rst_prolog\nhello reST!' parser.parse(text, document) (content, _), _ = RSTStateMachine().run.call_args assert list(content.xitems()) == [ @@ -41,8 +41,8 @@ def test_RSTParser_prolog_epilog(RSTStateMachine, app): ] # with rst_epilog - app.env.config.rst_prolog = None - app.env.config.rst_epilog = 'this is rst_epilog\ngood-bye reST!' + app.config.rst_prolog = None + app.config.rst_epilog = 'this is rst_epilog\ngood-bye reST!' parser.parse(text, document) (content, _), _ = RSTStateMachine().run.call_args assert list(content.xitems()) == [ @@ -54,8 +54,8 @@ def test_RSTParser_prolog_epilog(RSTStateMachine, app): ] # expandtabs / convert whitespaces - app.env.config.rst_prolog = None - app.env.config.rst_epilog = None + app.config.rst_prolog = None + app.config.rst_epilog = None text = '\thello Sphinx world\n\v\fSphinx is a document generator' parser.parse(text, document) (content, _), _ = RSTStateMachine().run.call_args diff --git a/tests/test_util/test_util_i18n.py b/tests/test_util/test_util_i18n.py index c1dfad5c78b..9040083a844 100644 --- a/tests/test_util/test_util_i18n.py +++ b/tests/test_util/test_util_i18n.py @@ -119,7 +119,7 @@ def test_get_filename_for_language(app): app.env.current_document.docname = 'index' # language is en - app.env.config.language = 'en' + app.config.language = 'en' assert get_filename('foo.png', app.env) == 'foo.en.png' assert get_filename('foo.bar.png', app.env) == 'foo.bar.en.png' assert get_filename('dir/foo.png', app.env) == 'dir/foo.en.png' @@ -127,8 +127,8 @@ def test_get_filename_for_language(app): assert get_filename('foo', app.env) == 'foo.en' # modify figure_language_filename and language is 'en' - app.env.config.language = 'en' - app.env.config.figure_language_filename = 'images/{language}/{root}{ext}' + app.config.language = 'en' + app.config.figure_language_filename = 'images/{language}/{root}{ext}' assert get_filename('foo.png', app.env) == 'images/en/foo.png' assert get_filename('foo.bar.png', app.env) == 'images/en/foo.bar.png' assert get_filename('subdir/foo.png', app.env) == 'images/en/subdir/foo.png' @@ -136,8 +136,8 @@ def test_get_filename_for_language(app): assert get_filename('foo', app.env) == 'images/en/foo' # new path and basename tokens - app.env.config.language = 'en' - app.env.config.figure_language_filename = '{path}{language}/{basename}{ext}' + app.config.language = 'en' + app.config.figure_language_filename = '{path}{language}/{basename}{ext}' assert get_filename('foo.png', app.env) == 'en/foo.png' assert get_filename('foo.bar.png', app.env) == 'en/foo.bar.png' assert get_filename('subdir/foo.png', app.env) == 'subdir/en/foo.png' @@ -145,13 +145,13 @@ def test_get_filename_for_language(app): assert get_filename('foo', app.env) == 'en/foo' # invalid figure_language_filename - app.env.config.figure_language_filename = '{root}.{invalid}{ext}' + app.config.figure_language_filename = '{root}.{invalid}{ext}' with pytest.raises(SphinxError): get_filename('foo.png', app.env) # docpath (for a document in the top of source directory) - app.env.config.language = 'en' - app.env.config.figure_language_filename = '/{docpath}{language}/{basename}{ext}' + app.config.language = 'en' + app.config.figure_language_filename = '/{docpath}{language}/{basename}{ext}' assert get_filename('foo.png', app.env) == '/en/foo.png' # docpath (for a document in the sub directory)