Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a new type for the current document's environment state #13151

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
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
11 changes: 8 additions & 3 deletions sphinx/builders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
from docutils import nodes
from docutils.utils import DependencyList

from sphinx.environment import CONFIG_CHANGED_REASON, CONFIG_OK, BuildEnvironment
from sphinx.environment import (
CONFIG_CHANGED_REASON,
CONFIG_OK,
BuildEnvironment,
_CurrentDocument,
)
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.errors import SphinxError
from sphinx.locale import __
Expand Down Expand Up @@ -615,7 +620,7 @@ def read_doc(self, docname: str, *, _cache: bool = True) -> None:
filename = str(self.env.doc2path(docname))
filetype = get_filetype(self.app.config.source_suffix, filename)
publisher = self.app.registry.get_publisher(self.app, filetype)
self.env.temp_data['_parser'] = publisher.parser
self.env.current_document._parser = publisher.parser
# record_dependencies is mutable even though it is in settings,
# explicitly re-initialise for each document
publisher.settings.record_dependencies = DependencyList()
Expand All @@ -635,7 +640,7 @@ def read_doc(self, docname: str, *, _cache: bool = True) -> None:
self.env.all_docs[docname] = time.time_ns() // 1_000

# cleanup
self.env.temp_data.clear()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should keep this one if people add stuff in their own temp_data and expect it to be cleaned up.

self.env.current_document = _CurrentDocument()
self.env.ref_context.clear()

self.write_doctree(docname, doctree, _cache=_cache)
Expand Down
17 changes: 6 additions & 11 deletions sphinx/directives/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,9 +284,9 @@ def run(self) -> list[Node]:
# needed for association of version{added,changed} directives
object_name: ObjDescT = self.names[0]
if isinstance(object_name, tuple):
self.env.temp_data['object'] = str(object_name[0])
self.env.current_document.obj_desc_name = str(object_name[0])
else:
self.env.temp_data['object'] = str(object_name)
self.env.current_document.obj_desc_name = str(object_name)
self.before_content()
content_children = self.parse_content_to_nodes(allow_section_headings=True)
content_node = addnodes.desc_content('', *content_children)
Expand All @@ -296,7 +296,7 @@ def run(self) -> list[Node]:
'object-description-transform', self.domain, self.objtype, content_node
)
DocFieldTransformer(self).transform_all(content_node)
self.env.temp_data['object'] = ''
self.env.temp_data['object'] = None
self.after_content()

if node['no-typesetting']:
Expand Down Expand Up @@ -335,7 +335,7 @@ def run(self) -> list[Node]:
)
if role:
docutils.register_role('', role) # type: ignore[arg-type]
self.env.temp_data['default_role'] = role_name
self.env.current_document.default_role = role_name
else:
literal_block = nodes.literal_block(self.block_text, self.block_text)
reporter = self.state.reporter
Expand All @@ -362,13 +362,8 @@ class DefaultDomain(SphinxDirective):

def run(self) -> list[Node]:
domain_name = self.arguments[0].lower()
# if domain_name not in env.domains:
# # try searching by label
# for domain in env.domains.sorted():
# if domain.label.lower() == domain_name:
# domain_name = domain.name
# break
self.env.temp_data['default_domain'] = self.env.domains.get(domain_name)
default_domain = self.env.domains.get(domain_name)
self.env.current_document.default_domain = default_domain
return []


Expand Down
7 changes: 4 additions & 3 deletions sphinx/directives/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def run(self) -> list[Node]:
linenothreshold = self.options.get('linenothreshold', sys.maxsize)
force = 'force' in self.options

self.env.temp_data['highlight_language'] = language
self.env.current_document.highlight_language = language
return [
addnodes.highlightlang(
lang=language, force=force, linenothreshold=linenothreshold
Expand Down Expand Up @@ -159,8 +159,9 @@ def run(self) -> list[Node]:
# no highlight language specified. Then this directive refers the current
# highlight setting via ``highlight`` directive or ``highlight_language``
# configuration.
literal['language'] = self.env.temp_data.get(
'highlight_language', self.config.highlight_language
literal['language'] = (
self.env.current_document.highlight_language
or self.config.highlight_language
)
extra_args = literal['highlight_args'] = {}
if hl_lines is not None:
Expand Down
5 changes: 3 additions & 2 deletions sphinx/directives/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ def run(self) -> list[Node]:
# no highlight language specified. Then this directive refers the current
# highlight setting via ``highlight`` directive or ``highlight_language``
# configuration.
node['language'] = self.env.temp_data.get(
'highlight_language', self.config.highlight_language
node['language'] = (
self.env.current_document.highlight_language
or self.config.highlight_language
)

if 'number-lines' in self.options:
Expand Down
48 changes: 24 additions & 24 deletions sphinx/domains/c/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,19 +207,19 @@ def describe_signature(self, signode: TextElement, ast: ASTDeclaration,

def run(self) -> list[Node]:
env = self.state.document.settings.env # from ObjectDescription.run
if 'c:parent_symbol' not in env.temp_data:
if 'c:parent_symbol' not in env.current_document:
root = env.domaindata['c']['root_symbol']
env.temp_data['c:parent_symbol'] = root
env.current_document['c:parent_symbol'] = root
env.ref_context['c:parent_key'] = root.get_lookup_key()

# When multiple declarations are made in the same directive
# they need to know about each other to provide symbol lookup for function parameters.
# We use last_symbol to store the latest added declaration in a directive.
env.temp_data['c:last_symbol'] = None
env.current_document['c:last_symbol'] = None
return super().run()

def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration:
parentSymbol: Symbol = self.env.temp_data['c:parent_symbol']
parentSymbol: 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
Expand All @@ -239,7 +239,7 @@ def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration:
# the possibly inner declarations.
name = _make_phony_error_name()
symbol = parentSymbol.add_name(name)
self.env.temp_data['c:last_symbol'] = symbol
self.env.current_document['c:last_symbol'] = symbol
raise ValueError from e

try:
Expand All @@ -248,15 +248,15 @@ def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration:
# append the new declaration to the sibling list
assert symbol.siblingAbove is None
assert symbol.siblingBelow is None
symbol.siblingAbove = self.env.temp_data['c:last_symbol']
symbol.siblingAbove = self.env.current_document['c:last_symbol']
if symbol.siblingAbove is not None:
assert symbol.siblingAbove.siblingBelow is None
symbol.siblingAbove.siblingBelow = symbol
self.env.temp_data['c:last_symbol'] = symbol
self.env.current_document['c:last_symbol'] = symbol
except _DuplicateSymbolError as e:
# Assume we are actually in the old symbol,
# instead of the newly created duplicate.
self.env.temp_data['c:last_symbol'] = e.symbol
self.env.current_document['c:last_symbol'] = e.symbol
msg = __("Duplicate C declaration, also defined at %s:%s.\n"
"Declaration is '.. c:%s:: %s'.")
logger.warning(
Expand All @@ -278,15 +278,15 @@ def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration:
return ast

def before_content(self) -> None:
lastSymbol: Symbol = self.env.temp_data['c:last_symbol']
lastSymbol: Symbol = self.env.current_document['c:last_symbol']
assert lastSymbol
self.oldParentSymbol = self.env.temp_data['c:parent_symbol']
self.oldParentSymbol = self.env.current_document['c:parent_symbol']
self.oldParentKey: LookupKey = self.env.ref_context['c:parent_key']
self.env.temp_data['c:parent_symbol'] = lastSymbol
self.env.current_document['c:parent_symbol'] = lastSymbol
self.env.ref_context['c:parent_key'] = lastSymbol.get_lookup_key()

def after_content(self) -> None:
self.env.temp_data['c:parent_symbol'] = self.oldParentSymbol
self.env.current_document['c:parent_symbol'] = self.oldParentSymbol
self.env.ref_context['c:parent_key'] = self.oldParentKey


Expand Down Expand Up @@ -375,8 +375,8 @@ def run(self) -> list[Node]:
name = _make_phony_error_name()
symbol = rootSymbol.add_name(name)
stack = [symbol]
self.env.temp_data['c:parent_symbol'] = symbol
self.env.temp_data['c:namespace_stack'] = stack
self.env.current_document['c:parent_symbol'] = symbol
self.env.current_document['c:namespace_stack'] = stack
self.env.ref_context['c:parent_key'] = symbol.get_lookup_key()
return []

Expand All @@ -400,14 +400,14 @@ def run(self) -> list[Node]:
except DefinitionError as e:
logger.warning(e, location=self.get_location())
name = _make_phony_error_name()
oldParent = self.env.temp_data.get('c:parent_symbol', None)
oldParent = self.env.current_document.get('c:parent_symbol', None)
if not oldParent:
oldParent = self.env.domaindata['c']['root_symbol']
symbol = oldParent.add_name(name)
stack = self.env.temp_data.get('c:namespace_stack', [])
stack = self.env.current_document.get('c:namespace_stack', [])
stack.append(symbol)
self.env.temp_data['c:parent_symbol'] = symbol
self.env.temp_data['c:namespace_stack'] = stack
self.env.current_document['c:parent_symbol'] = symbol
self.env.current_document['c:namespace_stack'] = stack
self.env.ref_context['c:parent_key'] = symbol.get_lookup_key()
return []

Expand All @@ -420,7 +420,7 @@ class CNamespacePopObject(SphinxDirective):
option_spec: ClassVar[OptionSpec] = {}

def run(self) -> list[Node]:
stack = self.env.temp_data.get('c:namespace_stack', None)
stack = self.env.current_document.get('c:namespace_stack', None)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
stack = self.env.current_document.get('c:namespace_stack', None)
stack = self.env.current_document.get('c:namespace_stack')

if not stack or len(stack) == 0:
logger.warning("C namespace pop on empty stack. Defaulting to global scope.",
location=self.get_location())
Expand All @@ -431,8 +431,8 @@ def run(self) -> list[Node]:
symbol = stack[-1]
else:
symbol = self.env.domaindata['c']['root_symbol']
self.env.temp_data['c:parent_symbol'] = symbol
self.env.temp_data['c:namespace_stack'] = stack
self.env.current_document['c:parent_symbol'] = symbol
self.env.current_document['c:namespace_stack'] = stack
self.env.ref_context['c:parent_key'] = symbol.get_lookup_key()
return []

Expand All @@ -451,9 +451,9 @@ def __init__(
self.aliasOptions = aliasOptions
self.document = document
if env is not None:
if 'c:parent_symbol' not in env.temp_data:
if 'c:parent_symbol' not in env.current_document:
root = env.domaindata['c']['root_symbol']
env.temp_data['c:parent_symbol'] = root
env.current_document['c:parent_symbol'] = root
env.ref_context['c:parent_key'] = root.get_lookup_key()
self.parentKey = env.ref_context['c:parent_key']
else:
Expand Down Expand Up @@ -659,7 +659,7 @@ def run(self) -> tuple[list[Node], list[system_message]]:
location=self.get_location())
# see below
return [addnodes.desc_inline('c', text, text, classes=[self.class_type])], []
parentSymbol = self.env.temp_data.get('c:parent_symbol', None)
parentSymbol = self.env.current_document.get('c:parent_symbol', None)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parentSymbol = self.env.current_document.get('c:parent_symbol', None)
parentSymbol = self.env.current_document.get('c:parent_symbol')

if parentSymbol is None:
parentSymbol = self.env.domaindata['c']['root_symbol']
# ...most if not all of these classes should really apply to the individual references,
Expand Down
2 changes: 1 addition & 1 deletion sphinx/domains/changeset.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def changesets(self) -> dict[str, list[ChangeSet]]:
def note_changeset(self, node: addnodes.versionmodified) -> None:
version = node['version']
module = self.env.ref_context.get('py:module')
objname = self.env.temp_data.get('object', '')
objname = self.env.current_document.obj_desc_name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now this can be None instead of ''. Wouldn't it still be acceptable as an input to the ChangeSet object constructed just after?

changeset = ChangeSet(node['type'], self.env.docname, node.line, # type: ignore[arg-type]
module, objname, node.astext())
self.changesets.setdefault(version, []).append(changeset)
Expand Down
Loading
Loading