diff --git a/CHANGES b/CHANGES index befd9ef75..9899d77a9 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,7 @@ Deprecated * ``sphinx.ext.autodoc.importer.MockLoader`` * ``sphinx.ext.autodoc.importer.mock()`` * ``sphinx.ext.autosummary.autolink_role()`` +* ``sphinx.util.node.find_source_node()`` * ``sphinx.util.i18n.find_catalog()`` * ``sphinx.util.i18n.find_catalog_files()`` * ``sphinx.util.i18n.find_catalog_source_files()`` @@ -27,6 +28,8 @@ For more details, see :ref:`deprecation APIs list `. Features added -------------- +* Add a helper method ``SphinxDirective.set_source_info()`` + Bugs fixed ---------- diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index 44bea4845..a8c016d0f 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -259,6 +259,11 @@ The following is a list of deprecated interfaces. - 4.0 - ``sphinx.ext.autosummary.AutoLink`` + * - ``sphinx.util.node.find_source_node()`` + - 2.1 + - 4.0 + - ``sphinx.util.node.get_node_source()`` + * - ``sphinx.util.i18n.find_catalog()`` - 2.1 - 4.0 diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index 73afac4ba..75ab61796 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -20,7 +20,6 @@ from sphinx.locale import __ from sphinx.util import logging from sphinx.util import parselinenos from sphinx.util.docutils import SphinxDirective -from sphinx.util.nodes import set_source_info if False: # For type annotation @@ -173,7 +172,7 @@ class CodeBlock(SphinxDirective): extra_args['hl_lines'] = hl_lines if 'lineno-start' in self.options: extra_args['linenostart'] = self.options['lineno-start'] - set_source_info(self, literal) + self.set_source_info(literal) caption = self.options.get('caption') if caption: @@ -446,7 +445,7 @@ class LiteralInclude(SphinxDirective): text, lines = reader.read(location=location) retnode = nodes.literal_block(text, text, source=filename) # type: nodes.Element - set_source_info(self, retnode) + self.set_source_info(retnode) if self.options.get('diff'): # if diff is set, set udiff retnode['language'] = 'udiff' elif 'language' in self.options: diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index 2efca0722..cc2268a41 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -21,8 +21,7 @@ from sphinx.locale import _ from sphinx.util import url_re, docname_join from sphinx.util.docutils import SphinxDirective from sphinx.util.matching import Matcher, patfilter -from sphinx.util.nodes import explicit_title_re, set_source_info, \ - process_index_entry +from sphinx.util.nodes import explicit_title_re, process_index_entry if False: # For type annotation @@ -77,7 +76,7 @@ class TocTree(SphinxDirective): subnode['includehidden'] = 'includehidden' in self.options subnode['numbered'] = self.options.get('numbered', 0) subnode['titlesonly'] = 'titlesonly' in self.options - set_source_info(self, subnode) + self.set_source_info(subnode) wrappernode = nodes.compound(classes=['toctree-wrapper']) wrappernode.append(subnode) self.add_name(wrappernode) @@ -204,7 +203,7 @@ class Index(SphinxDirective): indexnode = addnodes.index() indexnode['entries'] = [] indexnode['inline'] = False - set_source_info(self, indexnode) + self.set_source_info(indexnode) for entry in arguments: indexnode['entries'].extend(process_index_entry(entry, targetid)) return [indexnode, targetnode] @@ -231,7 +230,7 @@ class TabularColumns(SphinxDirective): # type: () -> List[nodes.Node] node = addnodes.tabular_col_spec() node['spec'] = self.arguments[0] - set_source_info(self, node) + self.set_source_info(node) return [node] @@ -330,7 +329,7 @@ class Only(SphinxDirective): # type: () -> List[nodes.Node] node = addnodes.only() node.document = self.state.document - set_source_info(self, node) + self.set_source_info(node) node['expr'] = self.arguments[0] # Same as util.nested_parse_with_titles but try to handle nested diff --git a/sphinx/directives/patches.py b/sphinx/directives/patches.py index 3c31571ac..0f6883609 100644 --- a/sphinx/directives/patches.py +++ b/sphinx/directives/patches.py @@ -132,7 +132,7 @@ class MathDirective(SphinxDirective): label=self.options.get('label'), nowrap='nowrap' in self.options) ret = [node] # type: List[nodes.Node] - set_source_info(self, node) + self.set_source_info(node) self.add_target(ret) return ret diff --git a/sphinx/domains/changeset.py b/sphinx/domains/changeset.py index 030d88ad3..aeea645c1 100644 --- a/sphinx/domains/changeset.py +++ b/sphinx/domains/changeset.py @@ -19,7 +19,6 @@ from sphinx.deprecation import DeprecatedDict, RemovedInSphinx30Warning from sphinx.domains import Domain from sphinx.locale import _ from sphinx.util.docutils import SphinxDirective -from sphinx.util.nodes import set_source_info if False: @@ -68,7 +67,7 @@ class VersionChange(SphinxDirective): # type: () -> List[nodes.Node] node = addnodes.versionmodified() node.document = self.state.document - set_source_info(self, node) + self.set_source_info(node) node['type'] = self.name node['version'] = self.arguments[0] text = versionlabels[self.name] % self.arguments[0] @@ -76,7 +75,7 @@ class VersionChange(SphinxDirective): inodes, messages = self.state.inline_text(self.arguments[1], self.lineno + 1) para = nodes.paragraph(self.arguments[1], '', *inodes, translatable=False) - set_source_info(self, para) + self.set_source_info(para) node.append(para) else: messages = [] diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index a47d05b55..d7a18ba17 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -29,7 +29,6 @@ from sphinx.locale import __ from sphinx.util import logging from sphinx.util.console import bold # type: ignore from sphinx.util.docutils import SphinxDirective -from sphinx.util.nodes import set_source_info from sphinx.util.osutil import relpath if False: @@ -104,7 +103,7 @@ class TestDirective(SphinxDirective): else: groups = ['default'] node = nodetype(code, code, testnodetype=self.name, groups=groups) - set_source_info(self, node) + self.set_source_info(node) if test is not None: # only save if it differs from code node['test'] = test diff --git a/sphinx/ext/ifconfig.py b/sphinx/ext/ifconfig.py index 4fd5fa391..04653ebc4 100644 --- a/sphinx/ext/ifconfig.py +++ b/sphinx/ext/ifconfig.py @@ -23,7 +23,6 @@ from docutils import nodes import sphinx from sphinx.util.docutils import SphinxDirective -from sphinx.util.nodes import set_source_info if False: # For type annotation @@ -47,7 +46,7 @@ class IfConfig(SphinxDirective): # type: () -> List[nodes.Node] node = ifconfig() node.document = self.state.document - set_source_info(self, node) + self.set_source_info(node) node['expr'] = self.arguments[0] self.state.nested_parse(self.content, self.content_offset, node, match_titles=True) diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py index 8f4144142..3939f8ff8 100644 --- a/sphinx/ext/todo.py +++ b/sphinx/ext/todo.py @@ -22,7 +22,6 @@ from sphinx.errors import NoUri from sphinx.locale import _, __ from sphinx.util import logging from sphinx.util.docutils import SphinxDirective -from sphinx.util.nodes import set_source_info from sphinx.util.texescape import tex_escape_map if False: @@ -68,7 +67,7 @@ class Todo(BaseAdmonition, SphinxDirective): return [todo] elif isinstance(todo, todo_node): todo.insert(0, nodes.title(text=_('Todo'))) - set_source_info(self, todo) + self.set_source_info(todo) targetid = 'index-%s' % self.env.new_serialno('index') # Stash the target to be retrieved later in latex_visit_todo_node. diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index 393f97abf..6a580d1ae 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -383,6 +383,11 @@ class SphinxDirective(Directive): """Reference to the :class:`.Config` object.""" return self.env.config + def set_source_info(self, node): + # type: (nodes.Node) -> None + """Set source and line number to the node.""" + node.source, node.line = self.state_machine.get_source_and_line(self.lineno) + class SphinxRole: """A base class for Sphinx roles. diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index b0dd13b38..3e83c218d 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -9,11 +9,13 @@ """ import re +import warnings from typing import Any, cast from docutils import nodes from sphinx import addnodes +from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.locale import __ from sphinx.util import logging @@ -152,7 +154,7 @@ def apply_source_workaround(node): # workaround: literal_block under bullet list (#4913) if isinstance(node, nodes.literal_block) and node.source is None: - node.source = find_source_node(node) + node.source = get_node_source(node) # workaround: recommonmark-0.2.0 doesn't set rawsource attribute if not node.rawsource: @@ -170,7 +172,7 @@ def apply_source_workaround(node): ))): logger.debug('[i18n] PATCH: %r to have source and line: %s', get_full_module_name(node), repr_domxml(node)) - node.source = find_source_node(node) + node.source = get_node_source(node) node.line = 0 # need fix docutils to get `node.line` return @@ -278,6 +280,13 @@ def extract_messages(doctree): def find_source_node(node): + # type: (nodes.Element) -> str + warnings.warn('find_source_node() is deprecated.', + RemovedInSphinx40Warning) + return get_node_source(node) + + +def get_node_source(node): # type: (nodes.Element) -> str for pnode in traverse_parent(node): if pnode.source: @@ -467,6 +476,12 @@ def set_role_source_info(inliner, lineno, node): node.source, node.line = inliner.reporter.get_source_and_line(lineno) # type: ignore +def copy_source_info(src, dst): + # type: (nodes.Element, nodes.Element) -> None + dst.source = get_node_source(src) + dst.line = get_node_line(src) + + NON_SMARTQUOTABLE_PARENT_NODES = ( nodes.FixedTextElement, nodes.literal,