From 9d7171899a7621592397be7d428971f049294bc6 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 29 Apr 2020 18:54:45 +0100 Subject: [PATCH 01/14] ext.napoleon: Emit type annotations at the start of `.. attribute::` --- sphinx/ext/napoleon/__init__.py | 3 +-- sphinx/ext/napoleon/docstring.py | 5 ++--- tests/test_ext_napoleon_docstring.py | 15 +++++---------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py index 10b1ff3a3..9b41152fc 100644 --- a/sphinx/ext/napoleon/__init__.py +++ b/sphinx/ext/napoleon/__init__.py @@ -168,11 +168,10 @@ class Config: **If False**:: .. attribute:: attr1 + :type: int Description of `attr1` - :type: int - napoleon_use_param : :obj:`bool` (Defaults to True) True to use a ``:param:`` role for each function parameter. False to use a single ``:parameters:`` role for all the parameters. diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 820de6ee4..0c373c668 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -586,13 +586,12 @@ class GoogleDocstring: lines.append('.. attribute:: ' + _name) if self._opt and 'noindex' in self._opt: lines.append(' :noindex:') + if _type: + lines.extend(self._indent([':type: %s' % _type], 3)) lines.append('') fields = self._format_field('', '', _desc) lines.extend(self._indent(fields, 3)) - if _type: - lines.append('') - lines.extend(self._indent([':type: %s' % _type], 3)) lines.append('') if self._config.napoleon_use_ivar: lines.append('') diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py index 160079a50..dc36dec7a 100644 --- a/tests/test_ext_napoleon_docstring.py +++ b/tests/test_ext_napoleon_docstring.py @@ -53,22 +53,19 @@ class NamedtupleSubclassTest(BaseDocstringTest): Sample namedtuple subclass .. attribute:: attr1 + :type: Arbitrary type Quick description of attr1 - :type: Arbitrary type - .. attribute:: attr2 + :type: Another arbitrary type Quick description of attr2 - :type: Another arbitrary type - .. attribute:: attr3 + :type: Type Adds a newline after the type - - :type: Type """ self.assertEqual(expected, actual) @@ -390,10 +387,9 @@ Attributes: actual = str(GoogleDocstring(docstring)) expected = """\ .. attribute:: in_attr + :type: :class:`numpy.ndarray` super-dooper attribute - - :type: :class:`numpy.ndarray` """ self.assertEqual(expected, actual) @@ -405,10 +401,9 @@ Attributes: actual = str(GoogleDocstring(docstring)) expected = """\ .. attribute:: in_attr + :type: numpy.ndarray super-dooper attribute - - :type: numpy.ndarray """ self.assertEqual(expected, actual) From d612ef8f0ba9d608a2aace13b96f3d6d4054c4fc Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 30 Apr 2020 22:54:49 +0900 Subject: [PATCH 02/14] Fix #6857: autodoc: failed to detect a classmethod on Enum class --- CHANGES | 1 + sphinx/ext/autodoc/importer.py | 3 ++- tests/roots/test-ext-autodoc/target/enum.py | 5 +++++ tests/test_autodoc.py | 15 ++++++++------- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 44be3fbf9..7e1ac3aac 100644 --- a/CHANGES +++ b/CHANGES @@ -74,6 +74,7 @@ Bugs fixed * #6588: autodoc: Decorated inherited method has no documentation * #7469: autodoc: The change of autodoc-process-docstring for variables is cached unexpectedly +* #6857: autodoc: failed to detect a classmethod on Enum class * #7535: sphinx-autogen: crashes when custom template uses inheritance * #7536: sphinx-autogen: crashes when template uses i18n feature * #2785: html: Bad alignment of equation links diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py index e98b97915..432aa0c85 100644 --- a/sphinx/ext/autodoc/importer.py +++ b/sphinx/ext/autodoc/importer.py @@ -142,8 +142,9 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable, members[name] = Attribute(name, True, value) superclass = subject.__mro__[1] - for name, value in obj_dict.items(): + for name in obj_dict: if name not in superclass.__dict__: + value = safe_getattr(subject, name) members[name] = Attribute(name, True, value) # members in __slots__ diff --git a/tests/roots/test-ext-autodoc/target/enum.py b/tests/roots/test-ext-autodoc/target/enum.py index d0a59c71c..c69455fb7 100644 --- a/tests/roots/test-ext-autodoc/target/enum.py +++ b/tests/roots/test-ext-autodoc/target/enum.py @@ -16,3 +16,8 @@ class EnumCls(enum.Enum): def say_hello(self): """a method says hello to you.""" pass + + @classmethod + def say_goodbye(cls): + """a classmethod says good-bye to you.""" + pass diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index cbbdbb787..aedaa75cf 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -1112,8 +1112,7 @@ def test_slots(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_enum_class(app): - options = {"members": None, - "undoc-members": True} + options = {"members": None} actual = do_autodoc(app, 'class', 'target.enum.EnumCls', options) assert list(actual) == [ '', @@ -1123,6 +1122,13 @@ def test_enum_class(app): ' this is enum class', '', '', + ' .. py:method:: EnumCls.say_goodbye()', + ' :module: target.enum', + ' :classmethod:', + '', + ' a classmethod says good-bye to you.', + '', + '', ' .. py:method:: EnumCls.say_hello()', ' :module: target.enum', '', @@ -1149,11 +1155,6 @@ def test_enum_class(app): '', ' doc for val3', '', - '', - ' .. py:attribute:: EnumCls.val4', - ' :module: target.enum', - ' :value: 34', - '' ] # checks for an attribute of EnumClass From 56772395bb945099a6d5331501c179dd2831fa1c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 2 May 2020 12:35:53 +0900 Subject: [PATCH 03/14] Update CHANGES for PR #7583 --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index c930f4c75..abd7c1dd1 100644 --- a/CHANGES +++ b/CHANGES @@ -67,6 +67,7 @@ Features added * C++, parse trailing return types. * #7143: py domain: Add ``:final:`` option to :rst:dir:`py:class:`, :rst:dir:`py:exception:` and :rst:dir:`py:method:` directives +* #7582: napoleon: a type for attribute are represented like type annotation Bugs fixed ---------- From 911a3b9809259aeb1b301bc2e534c57807baece1 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 2 May 2020 22:48:02 +0900 Subject: [PATCH 04/14] refactor: Rename parameter to avoid conflict --- sphinx/util/nodes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index bca6e0bfc..a6333e782 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -62,8 +62,8 @@ class NodeMatcher: # => [, , ...] """ - def __init__(self, *classes: "Type[Node]", **attrs: Any) -> None: - self.classes = classes + def __init__(self, *node_classes: "Type[Node]", **attrs: Any) -> None: + self.classes = node_classes self.attrs = attrs def match(self, node: Node) -> bool: From a148a8e7cb7ff3bd4b350f491b48fda3cb25df56 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 30 Apr 2020 21:48:53 +0900 Subject: [PATCH 05/14] Close #7530: html: Support nested elements --- CHANGES | 1 + sphinx/builders/html/__init__.py | 3 ++ sphinx/builders/html/transforms.py | 69 ++++++++++++++++++++++++++++++ tests/test_markup.py | 28 ++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 sphinx/builders/html/transforms.py diff --git a/CHANGES b/CHANGES index abd7c1dd1..a10de4a17 100644 --- a/CHANGES +++ b/CHANGES @@ -49,6 +49,7 @@ Features added to generate stub files recursively * #4030: autosummary: Add :confval:`autosummary_context` to add template variables for custom templates +* #7530: html: Support nested elements * #7481: html theme: Add right margin to footnote/citation labels * #7482: html theme: CSS spacing for code blocks with captions and line numbers * #7443: html theme: Add new options :confval:`globaltoc_collapse` and diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index 320c7feb6..7ad87ffa9 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -1243,6 +1243,9 @@ def setup(app: Sphinx) -> Dict[str, Any]: # load default math renderer app.setup_extension('sphinx.ext.mathjax') + # load transforms for HTML builder + app.setup_extension('sphinx.builders.html.transforms') + return { 'version': 'builtin', 'parallel_read_safe': True, diff --git a/sphinx/builders/html/transforms.py b/sphinx/builders/html/transforms.py new file mode 100644 index 000000000..c91da57e9 --- /dev/null +++ b/sphinx/builders/html/transforms.py @@ -0,0 +1,69 @@ +""" + sphinx.builders.html.transforms + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Transforms for HTML builder. + + :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +from typing import Any, Dict + +from docutils import nodes + +from sphinx.application import Sphinx +from sphinx.transforms.post_transforms import SphinxPostTransform +from sphinx.util.nodes import NodeMatcher + + +class KeyboardTransform(SphinxPostTransform): + """Transform :kbd: role to more detailed form. + + Before:: + + + Control-x + + After:: + + + + Control + - + + x + """ + default_priority = 400 + builders = ('html',) + pattern = re.compile(r'(-|\+|\^|\s+)') + + def run(self, **kwargs: Any) -> None: + matcher = NodeMatcher(nodes.literal, classes=["kbd"]) + for node in self.document.traverse(matcher): # type: nodes.literal + parts = self.pattern.split(node[-1].astext()) + if len(parts) == 1: + continue + + node.pop() + while parts: + key = parts.pop(0) + node += nodes.literal('', key, classes=["kbd"]) + + try: + # key separator (ex. -, +, ^) + sep = parts.pop(0) + node += nodes.Text(sep) + except IndexError: + pass + + +def setup(app: Sphinx) -> Dict[str, Any]: + app.add_post_transform(KeyboardTransform) + + return { + 'version': 'builtin', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } diff --git a/tests/test_markup.py b/tests/test_markup.py index b6d99db90..1d5c81bfa 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -16,6 +16,7 @@ from docutils.parsers.rst import Parser as RstParser from docutils.transforms.universal import SmartQuotes from sphinx import addnodes +from sphinx.builders.html.transforms import KeyboardTransform from sphinx.builders.latex import LaTeXBuilder from sphinx.roles import XRefRole from sphinx.testing.util import Struct, assert_node @@ -94,6 +95,7 @@ class ForgivingLaTeXTranslator(LaTeXTranslator, ForgivingTranslator): def verify_re_html(app, parse): def verify(rst, html_expected): document = parse(rst) + KeyboardTransform(document).apply() html_translator = ForgivingHTMLTranslator(document, app.builder) document.walkabout(html_translator) html_translated = ''.join(html_translator.fragment).strip() @@ -237,6 +239,32 @@ def get_verifier(verify, verify_re): '

space

', '\\sphinxkeyboard{\\sphinxupquote{space}}', ), + ( + # kbd role + 'verify', + ':kbd:`Control+X`', + ('

' + 'Control' + '+' + 'X' + '

'), + '\\sphinxkeyboard{\\sphinxupquote{Control+X}}', + ), + ( + # kbd role + 'verify', + ':kbd:`M-x M-s`', + ('

' + 'M' + '-' + 'x' + ' ' + 'M' + '-' + 's' + '

'), + '\\sphinxkeyboard{\\sphinxupquote{M\\sphinxhyphen{}x M\\sphinxhyphen{}s}}', + ), ( # non-interpolation of dashes in option role 'verify_re', From c7a63b455c6b26c08702928cd826da508210c89f Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 2 May 2020 18:36:42 +0900 Subject: [PATCH 06/14] refactor: autodoc: Update type annotations --- sphinx/ext/autodoc/__init__.py | 2 +- sphinx/ext/autodoc/importer.py | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index eae199b33..29bfb791c 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -849,7 +849,7 @@ class ModuleDocumenter(Documenter): if self.options.deprecated: self.add_line(' :deprecated:', sourcename) - def get_object_members(self, want_all: bool) -> Tuple[bool, List[Tuple[str, object]]]: + def get_object_members(self, want_all: bool) -> Tuple[bool, List[Tuple[str, Any]]]: if want_all: if (self.options.ignore_module_all or not hasattr(self.object, '__all__')): diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py index 432aa0c85..cdccf710d 100644 --- a/sphinx/ext/autodoc/importer.py +++ b/sphinx/ext/autodoc/importer.py @@ -11,10 +11,10 @@ import importlib import traceback import warnings -from collections import namedtuple -from typing import Any, Callable, Dict, List, Mapping, Tuple +from typing import Any, Callable, Dict, List, Mapping, NamedTuple, Tuple from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias +from sphinx.pycode import ModuleAnalyzer from sphinx.util import logging from sphinx.util.inspect import isclass, isenumclass, safe_getattr @@ -122,11 +122,13 @@ def get_module_members(module: Any) -> List[Tuple[str, Any]]: return sorted(list(members.values())) -Attribute = namedtuple('Attribute', ['name', 'directly_defined', 'value']) +Attribute = NamedTuple('Attribute', [('name', str), + ('directly_defined', bool), + ('value', Any)]) def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable, - analyzer: Any = None) -> Dict[str, Attribute]: + analyzer: ModuleAnalyzer = None) -> Dict[str, Attribute]: """Get members and attributes of target object.""" from sphinx.ext.autodoc import INSTANCEATTR From daed2ec1ae2e51ca090b8d04ddd6a249fb308eb3 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 2 May 2020 23:16:38 +0900 Subject: [PATCH 07/14] Update CHANGES for PR #7562 --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index abd7c1dd1..6eef931e3 100644 --- a/CHANGES +++ b/CHANGES @@ -79,6 +79,8 @@ Bugs fixed cached unexpectedly * #7559: autodoc: misdetects a sync function is async * #6857: autodoc: failed to detect a classmethod on Enum class +* #7562: autodoc: a typehint contains spaces is wrongly rendered under + autodoc_typehints='description' mode * #7535: sphinx-autogen: crashes when custom template uses inheritance * #7536: sphinx-autogen: crashes when template uses i18n feature * #2785: html: Bad alignment of equation links From 1ba671a6771be858078207d5d8848f7962417b07 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 3 May 2020 01:59:47 +0900 Subject: [PATCH 08/14] Deprecate ignore parameter for Documenter.get_doc() --- CHANGES | 2 ++ doc/extdev/deprecated.rst | 10 ++++++++++ sphinx/ext/autodoc/__init__.py | 16 ++++++++++++---- sphinx/util/docstrings.py | 11 ++++++++++- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 6eef931e3..24c30fbac 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,7 @@ Deprecated been changed to Sphinx object * ``sphinx.ext.autosummary.generate.AutosummaryRenderer`` takes an object type as an argument +* The ``ignore`` argument of ``sphinx.ext.autodoc.Documenter.get_doc()`` * The ``template_dir`` argument of ``sphinx.ext.autosummary.generate. AutosummaryRenderer`` * The ``module`` argument of ``sphinx.ext.autosummary.generate. @@ -24,6 +25,7 @@ Deprecated generate_autosummary_docs()`` * The ``template_dir`` argument of ``sphinx.ext.autosummary.generate. generate_autosummary_docs()`` +* The ``ignore`` argument of ``sphinx.util.docstring.prepare_docstring()`` * ``sphinx.ext.autosummary.generate.AutosummaryRenderer.exists()`` Features added diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst index 35c7ec96a..9d05f894b 100644 --- a/doc/extdev/deprecated.rst +++ b/doc/extdev/deprecated.rst @@ -39,6 +39,11 @@ The following is a list of deprecated interfaces. - 5.0 - N/A + * - The ``ignore`` argument of ``sphinx.ext.autodoc.Documenter.get_doc()`` + - 3.1 + - 5.0 + - N/A + * - The ``template_dir`` argument of ``sphinx.ext.autosummary.generate.AutosummaryRenderer`` - 3.1 @@ -68,6 +73,11 @@ The following is a list of deprecated interfaces. - 5.0 - N/A + * - The ``ignore`` argument of ``sphinx.util.docstring.prepare_docstring()`` + - 3.1 + - 5.0 + - N/A + * - ``desc_signature['first']`` - - 3.0 diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index eae199b33..15050746f 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -429,12 +429,16 @@ class Documenter: # etc. don't support a prepended module name self.add_line(' :module: %s' % self.modname, sourcename) - def get_doc(self, encoding: str = None, ignore: int = 1) -> List[List[str]]: + def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]: """Decode and return lines of the docstring(s) for the object.""" if encoding is not None: warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated." % self.__class__.__name__, RemovedInSphinx40Warning) + if ignore is not None: + warnings.warn("The 'ignore' argument to autodoc.%s.get_doc() is deprecated." + % self.__class__.__name__, + RemovedInSphinx50Warning, stacklevel=2) docstring = getdoc(self.object, self.get_attr, self.env.config.autodoc_inherit_docstrings, self.parent, self.object_name) @@ -979,7 +983,7 @@ class DocstringSignatureMixin: break return result - def get_doc(self, encoding: str = None, ignore: int = 1) -> List[List[str]]: + def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]: if encoding is not None: warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated." % self.__class__.__name__, @@ -1239,7 +1243,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: self.add_line(' ' + _('Bases: %s') % ', '.join(bases), sourcename) - def get_doc(self, encoding: str = None, ignore: int = 1) -> List[List[str]]: + def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]: if encoding is not None: warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated." % self.__class__.__name__, @@ -1736,8 +1740,12 @@ class SlotsAttributeDocumenter(AttributeDocumenter): self.env.note_reread() return False - def get_doc(self, encoding: str = None, ignore: int = 1) -> List[List[str]]: + def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]: """Decode and return lines of the docstring(s) for the object.""" + if ignore is not None: + warnings.warn("The 'ignore' argument to autodoc.%s.get_doc() is deprecated." + % self.__class__.__name__, + RemovedInSphinx50Warning, stacklevel=2) name = self.objpath[-1] __slots__ = safe_getattr(self.parent, '__slots__', []) if isinstance(__slots__, dict) and isinstance(__slots__.get(name), str): diff --git a/sphinx/util/docstrings.py b/sphinx/util/docstrings.py index 7b3f011d1..64fdbf1d7 100644 --- a/sphinx/util/docstrings.py +++ b/sphinx/util/docstrings.py @@ -10,10 +10,13 @@ import re import sys +import warnings from typing import Dict, List from docutils.parsers.rst.states import Body +from sphinx.deprecation import RemovedInSphinx50Warning + field_list_item_re = re.compile(Body.patterns['field_marker']) @@ -42,7 +45,7 @@ def extract_metadata(s: str) -> Dict[str, str]: return metadata -def prepare_docstring(s: str, ignore: int = 1, tabsize: int = 8) -> List[str]: +def prepare_docstring(s: str, ignore: int = None, tabsize: int = 8) -> List[str]: """Convert a docstring into lines of parseable reST. Remove common leading indentation, where the indentation of a given number of lines (usually just one) is ignored. @@ -51,6 +54,12 @@ def prepare_docstring(s: str, ignore: int = 1, tabsize: int = 8) -> List[str]: ViewList (used as argument of nested_parse().) An empty line is added to act as a separator between this docstring and following content. """ + if ignore is None: + ignore = 1 + else: + warnings.warn("The 'ignore' argument to parepare_docstring() is deprecated.", + RemovedInSphinx50Warning, stacklevel=2) + lines = s.expandtabs(tabsize).splitlines() # Find minimum indentation of any non-blank lines after ignored lines. margin = sys.maxsize From ae57962b989030d39242eba3c16b8657962f365e Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 3 May 2020 12:15:45 +0100 Subject: [PATCH 09/14] Report junit pytest results to CircleCI --- .circleci/config.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 04c319340..7f4de4ae0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,4 +8,7 @@ jobs: - checkout - run: /python3.6/bin/pip install -U pip setuptools - run: /python3.6/bin/pip install -U .[test] - - run: make test PYTHON=/python3.6/bin/python + - run: mkdir -p test-reports/pytest + - run: make test PYTHON=/python3.6/bin/python TEST=--junitxml=test-reports/pytest/results.xml + - store_test_results: + path: test-reports From f3dd6c0bfa43ad3d7bb23aa014dde2ff0ff8d016 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 3 May 2020 12:50:28 +0100 Subject: [PATCH 10/14] autodoc: Show full traceback and exception type in warnings --- sphinx/ext/autodoc/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 609928422..f7aae50c2 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -397,8 +397,8 @@ class Documenter: # retry without arguments for old documenters args = self.format_args() except Exception as err: - logger.warning(__('error while formatting arguments for %s: %s') % - (self.fullname, err), type='autodoc') + logger.warning(__('error while formatting arguments for %s:') % + self.fullname, type='autodoc', exc_info=True) args = None retann = self.retann @@ -751,7 +751,7 @@ class Documenter: # be cached anyway) self.analyzer.find_attr_docs() except PycodeError as err: - logger.debug('[autodoc] module analyzer failed: %s', err) + logger.debug('[autodoc] module analyzer failed:', excinfo=True) # no source file -- e.g. for builtin and C modules self.analyzer = None # at least add the module.__file__ as a dependency From b787e4ab8d410df75abfcd65b6a5b80ce56fed3d Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 3 May 2020 14:02:11 +0100 Subject: [PATCH 11/14] Update sphinx/ext/autodoc/__init__.py --- sphinx/ext/autodoc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index f7aae50c2..89698057a 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -751,7 +751,7 @@ class Documenter: # be cached anyway) self.analyzer.find_attr_docs() except PycodeError as err: - logger.debug('[autodoc] module analyzer failed:', excinfo=True) + logger.debug('[autodoc] module analyzer failed:', exc_info=True) # no source file -- e.g. for builtin and C modules self.analyzer = None # at least add the module.__file__ as a dependency From 8268d38d51391a3d864503fbc95ac97f8e43e71a Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 3 May 2020 14:18:22 +0100 Subject: [PATCH 12/14] Apply suggestions from code review --- sphinx/ext/autodoc/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 89698057a..6031ac51c 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -396,7 +396,7 @@ class Documenter: except TypeError: # retry without arguments for old documenters args = self.format_args() - except Exception as err: + except Exception: logger.warning(__('error while formatting arguments for %s:') % self.fullname, type='autodoc', exc_info=True) args = None @@ -750,7 +750,7 @@ class Documenter: # parse right now, to get PycodeErrors on parsing (results will # be cached anyway) self.analyzer.find_attr_docs() - except PycodeError as err: + except PycodeError: logger.debug('[autodoc] module analyzer failed:', exc_info=True) # no source file -- e.g. for builtin and C modules self.analyzer = None From 5ed31be51fe3f00642527bc9781cab3d52e97641 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 3 May 2020 14:58:19 +0100 Subject: [PATCH 13/14] Preserve exception info in raised SphinxWarning objects --- sphinx/util/logging.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py index 53053faaf..5889f3860 100644 --- a/sphinx/util/logging.py +++ b/sphinx/util/logging.py @@ -412,9 +412,13 @@ class WarningIsErrorFilter(logging.Filter): message = record.msg # use record.msg itself if location: - raise SphinxWarning(location + ":" + str(message)) + exc = SphinxWarning(location + ":" + str(message)) else: - raise SphinxWarning(message) + exc = SphinxWarning(message) + if record.exc_info is not None: + raise exc from record.exc_info[1] + else: + raise exc else: return True From 41b4a77dea51cf5eb16f9a3fdc27426827313d54 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 3 May 2020 22:40:19 +0900 Subject: [PATCH 14/14] Add stacklevel parameter to warnings.warn() call --- sphinx/application.py | 2 +- sphinx/builders/_epub_base.py | 2 +- sphinx/builders/applehelp.py | 2 +- sphinx/builders/html/__init__.py | 2 +- sphinx/builders/htmlhelp.py | 2 +- sphinx/builders/latex/__init__.py | 2 +- sphinx/cmd/quickstart.py | 2 +- sphinx/config.py | 2 +- sphinx/domains/math.py | 4 ++-- sphinx/domains/python.py | 15 +++++++++------ sphinx/domains/std.py | 12 ++++++------ sphinx/environment/__init__.py | 4 ++-- sphinx/environment/collectors/indexentries.py | 2 +- sphinx/ext/apidoc.py | 8 ++++---- sphinx/ext/autodoc/__init__.py | 10 +++++----- sphinx/ext/autodoc/directive.py | 2 +- sphinx/ext/autosummary/__init__.py | 2 +- sphinx/ext/autosummary/generate.py | 12 ++++++------ sphinx/ext/doctest.py | 2 +- sphinx/ext/todo.py | 9 +++++---- sphinx/parsers.py | 2 +- sphinx/pycode/__init__.py | 4 ++-- sphinx/roles.py | 2 +- sphinx/search/__init__.py | 2 +- sphinx/util/__init__.py | 4 ++-- sphinx/util/compat.py | 2 +- sphinx/util/docfields.py | 4 ++-- sphinx/util/inspect.py | 6 +++--- sphinx/util/nodes.py | 2 +- sphinx/util/osutil.py | 4 ++-- sphinx/writers/latex.py | 8 ++++---- 31 files changed, 71 insertions(+), 67 deletions(-) diff --git a/sphinx/application.py b/sphinx/application.py index d2fd776ff..4014a2efa 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -990,7 +990,7 @@ class Sphinx: if isinstance(lexer, Lexer): warnings.warn('app.add_lexer() API changed; ' 'Please give lexer class instead instance', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) lexers[alias] = lexer else: lexer_classes[alias] = lexer diff --git a/sphinx/builders/_epub_base.py b/sphinx/builders/_epub_base.py index b2d8413a8..f26a1ba9f 100644 --- a/sphinx/builders/_epub_base.py +++ b/sphinx/builders/_epub_base.py @@ -173,7 +173,7 @@ class EpubBuilder(StandaloneHTMLBuilder): """Replace all characters not allowed in text an attribute values.""" warnings.warn( '%s.esc() is deprecated. Use html.escape() instead.' % self.__class__.__name__, - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) name = name.replace('&', '&') name = name.replace('<', '<') name = name.replace('>', '>') diff --git a/sphinx/builders/applehelp.py b/sphinx/builders/applehelp.py index f081f9fe5..759ba66da 100644 --- a/sphinx/builders/applehelp.py +++ b/sphinx/builders/applehelp.py @@ -32,7 +32,7 @@ deprecated_alias('sphinx.builders.applehelp', def setup(app: Sphinx) -> Dict[str, Any]: warnings.warn('sphinx.builders.applehelp has been moved to sphinxcontrib-applehelp.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) app.setup_extension('sphinxcontrib.applehelp') return { diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index 320c7feb6..85767b14e 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -882,7 +882,7 @@ class StandaloneHTMLBuilder(Builder): 'The %s.feed() method signature is deprecated. Update to ' '%s.feed(docname, filename, title, doctree).' % ( indexer_name, indexer_name), - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) def _get_local_toctree(self, docname: str, collapse: bool = True, **kwargs: Any) -> str: if 'includehidden' not in kwargs: diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index 8732de7fd..1d6304d38 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -32,7 +32,7 @@ deprecated_alias('sphinx.builders.htmlhelp', def setup(app: Sphinx) -> Dict[str, Any]: warnings.warn('sphinx.builders.htmlhelp has been moved to sphinxcontrib-htmlhelp.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) app.setup_extension('sphinxcontrib.htmlhelp') return { diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index b1fe4b73e..225975c20 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -363,7 +363,7 @@ class LaTeXBuilder(Builder): def apply_transforms(self, doctree: nodes.document) -> None: warnings.warn('LaTeXBuilder.apply_transforms() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) def finish(self) -> None: self.copy_image_files() diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py index 8f8ae58a1..29d2e8187 100644 --- a/sphinx/cmd/quickstart.py +++ b/sphinx/cmd/quickstart.py @@ -187,7 +187,7 @@ def do_prompt(text: str, default: str = None, validator: Callable[[str], Any] = def convert_python_source(source: str, rex: Pattern = re.compile(r"[uU]('.*?')")) -> str: # remove Unicode literal prefixes warnings.warn('convert_python_source() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) return rex.sub('\\1', source) diff --git a/sphinx/config.py b/sphinx/config.py index 19a8f6c4f..6e6c256c5 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -444,7 +444,7 @@ def check_unicode(config: Config) -> None: since that can result in UnicodeErrors all over the place """ warnings.warn('sphinx.config.check_unicode() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) nonascii_re = re.compile(br'[\x80-\xff]') diff --git a/sphinx/domains/math.py b/sphinx/domains/math.py index 88b6e4eb8..e77bd0ed7 100644 --- a/sphinx/domains/math.py +++ b/sphinx/domains/math.py @@ -142,7 +142,7 @@ class MathDomain(Domain): def add_equation(self, env: BuildEnvironment, docname: str, labelid: str) -> int: warnings.warn('MathDomain.add_equation() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) if labelid in self.equations: path = env.doc2path(self.equations[labelid][0]) msg = __('duplicate label of equation %s, other instance in %s') % (labelid, path) @@ -154,7 +154,7 @@ class MathDomain(Domain): def get_next_equation_number(self, docname: str) -> int: warnings.warn('MathDomain.get_next_equation_number() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) targets = [eq for eq in self.equations.values() if eq[0] == docname] return len(targets) + 1 diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 99339ccbd..39c7de142 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -530,10 +530,11 @@ class PyModulelevel(PyObject): if cls.__name__ != 'DirectiveAdapter': warnings.warn('PyModulelevel is deprecated. ' 'Please check the implementation of %s' % cls, - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) break else: - warnings.warn('PyModulelevel is deprecated', RemovedInSphinx40Warning) + warnings.warn('PyModulelevel is deprecated', + RemovedInSphinx40Warning, stacklevel=2) return super().run() @@ -675,10 +676,11 @@ class PyClassmember(PyObject): if cls.__name__ != 'DirectiveAdapter': warnings.warn('PyClassmember is deprecated. ' 'Please check the implementation of %s' % cls, - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) break else: - warnings.warn('PyClassmember is deprecated', RemovedInSphinx40Warning) + warnings.warn('PyClassmember is deprecated', + RemovedInSphinx40Warning, stacklevel=2) return super().run() @@ -896,10 +898,11 @@ class PyDecoratorMixin: if cls.__name__ != 'DirectiveAdapter': warnings.warn('PyDecoratorMixin is deprecated. ' 'Please check the implementation of %s' % cls, - RemovedInSphinx50Warning) + RemovedInSphinx50Warning, stacklevel=2) break else: - warnings.warn('PyDecoratorMixin is deprecated', RemovedInSphinx50Warning) + warnings.warn('PyDecoratorMixin is deprecated', + RemovedInSphinx50Warning, stacklevel=2) ret = super().handle_signature(sig, signode) # type: ignore signode.insert(0, addnodes.desc_addname('@', '@')) diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 74e901779..6dc597022 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -292,7 +292,7 @@ def make_glossary_term(env: "BuildEnvironment", textnodes: Iterable[Node], index document.note_explicit_target(term) else: warnings.warn('make_glossary_term() expects document is passed as an argument.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) gloss_entries = env.temp_data.setdefault('gloss_entries', set()) node_id = nodes.make_id('term-' + termtext) if node_id == 'term': @@ -660,7 +660,7 @@ class StandardDomain(Domain): def add_object(self, objtype: str, name: str, docname: str, labelid: str) -> None: warnings.warn('StandardDomain.add_object() is deprecated.', - RemovedInSphinx50Warning) + RemovedInSphinx50Warning, stacklevel=2) self.objects[objtype, name] = (docname, labelid) @property @@ -786,7 +786,7 @@ class StandardDomain(Domain): resolver = self._resolve_option_xref elif typ == 'citation': warnings.warn('pending_xref(domain=std, type=citation) is deprecated: %r' % node, - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) domain = env.get_domain('citation') return domain.resolve_xref(env, fromdocname, builder, typ, target, node, contnode) elif typ == 'term': @@ -1082,15 +1082,15 @@ class StandardDomain(Domain): def note_citations(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA warnings.warn('StandardDomain.note_citations() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) def note_citation_refs(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA warnings.warn('StandardDomain.note_citation_refs() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) def note_labels(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA warnings.warn('StandardDomain.note_labels() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) def setup(app: "Sphinx") -> Dict[str, Any]: diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index c8735461d..6584ac6d8 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -331,10 +331,10 @@ class BuildEnvironment: """ if suffix: warnings.warn('The suffix argument for doc2path() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) if base not in (True, False, None): warnings.warn('The string style base argument for doc2path() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) pathname = self.project.doc2path(docname, base is True) if suffix: diff --git a/sphinx/environment/collectors/indexentries.py b/sphinx/environment/collectors/indexentries.py index 2ef59909b..a4c0450d2 100644 --- a/sphinx/environment/collectors/indexentries.py +++ b/sphinx/environment/collectors/indexentries.py @@ -29,7 +29,7 @@ class IndexEntriesCollector(EnvironmentCollector): def __init__(self) -> None: warnings.warn('IndexEntriesCollector is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None: env.indexentries.pop(docname, None) diff --git a/sphinx/ext/apidoc.py b/sphinx/ext/apidoc.py index 23be0a00a..b01604617 100644 --- a/sphinx/ext/apidoc.py +++ b/sphinx/ext/apidoc.py @@ -54,7 +54,7 @@ template_dir = path.join(package_dir, 'templates', 'apidoc') def makename(package: str, module: str) -> str: """Join package and module with a dot.""" warnings.warn('makename() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) # Both package and module can be None/empty. if package: name = package @@ -112,7 +112,7 @@ def write_file(name: str, text: str, opts: Any) -> None: def format_heading(level: int, text: str, escape: bool = True) -> str: """Create a heading of [1, 2 or 3 supported].""" warnings.warn('format_warning() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) if escape: text = rst.escape(text) underlining = ['=', '-', '~', ][level - 1] * len(text) @@ -122,7 +122,7 @@ def format_heading(level: int, text: str, escape: bool = True) -> str: def format_directive(module: str, package: str = None) -> str: """Create the automodule directive and add the options.""" warnings.warn('format_directive() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) directive = '.. automodule:: %s\n' % module_join(package, module) for option in OPTIONS: directive += ' :%s:\n' % option @@ -209,7 +209,7 @@ def create_modules_toc_file(modules: List[str], opts: Any, name: str = 'modules' def shall_skip(module: str, opts: Any, excludes: List[str] = []) -> bool: """Check if we want to skip this module.""" warnings.warn('shall_skip() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) # skip if the file doesn't exist and not using implicit namespaces if not opts.implicit_namespaces and not path.exists(module): return True diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 609928422..36e0b6455 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -434,7 +434,7 @@ class Documenter: if encoding is not None: warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated." % self.__class__.__name__, - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) if ignore is not None: warnings.warn("The 'ignore' argument to autodoc.%s.get_doc() is deprecated." % self.__class__.__name__, @@ -953,7 +953,7 @@ class DocstringSignatureMixin: if encoding is not None: warnings.warn("The 'encoding' argument to autodoc.%s._find_signature() is " "deprecated." % self.__class__.__name__, - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) docstrings = self.get_doc() self._new_docstrings = docstrings[:] result = None @@ -987,7 +987,7 @@ class DocstringSignatureMixin: if encoding is not None: warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated." % self.__class__.__name__, - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) lines = getattr(self, '_new_docstrings', None) if lines is not None: return lines @@ -1247,7 +1247,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: if encoding is not None: warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated." % self.__class__.__name__, - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) lines = getattr(self, '_new_docstrings', None) if lines is not None: return lines @@ -1757,7 +1757,7 @@ class SlotsAttributeDocumenter(AttributeDocumenter): def get_documenters(app: Sphinx) -> Dict[str, "Type[Documenter]"]: """Returns registered Documenter classes""" - warnings.warn("get_documenters() is deprecated.", RemovedInSphinx50Warning) + warnings.warn("get_documenters() is deprecated.", RemovedInSphinx50Warning, stacklevel=2) return app.registry.documenters diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py index 3be19f089..9a3428f5d 100644 --- a/sphinx/ext/autodoc/directive.py +++ b/sphinx/ext/autodoc/directive.py @@ -66,7 +66,7 @@ class DocumenterBridge: else: # create fake object for self.state.document.settings.tab_width warnings.warn('DocumenterBridge requires a state object on instantiation.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) settings = Struct(tab_width=8) document = Struct(settings=settings) self.state = Struct(document=document) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 57f775272..ed49c56df 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -659,7 +659,7 @@ def autolink_role(typ: str, rawtext: str, etext: str, lineno: int, inliner: Inli Expands to ':obj:`text`' if `text` is an object that can be imported; otherwise expands to '*text*'. """ - warnings.warn('autolink_role() is deprecated.', RemovedInSphinx40Warning) + warnings.warn('autolink_role() is deprecated.', RemovedInSphinx40Warning, stacklevel=2) env = inliner.document.settings.env pyobj_role = env.get_domain('py').role('obj') objects, msg = pyobj_role('obj', rawtext, etext, lineno, inliner, options, content) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 693f0e5b0..e5bdc0e30 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -126,7 +126,7 @@ class AutosummaryRenderer: RemovedInSphinx50Warning, stacklevel=2) if template_dir: warnings.warn('template_dir argument for AutosummaryRenderer is deprecated.', - RemovedInSphinx50Warning) + RemovedInSphinx50Warning, stacklevel=2) system_templates_path = [os.path.join(package_dir, 'ext', 'autosummary', 'templates')] loader = SphinxTemplateLoader(app.srcdir, app.config.templates_path, @@ -279,25 +279,25 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None, overwrite: bool = True) -> None: if info: warnings.warn('info argument for generate_autosummary_docs() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) _info = info else: _info = logger.info if warn: warnings.warn('warn argument for generate_autosummary_docs() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) _warn = warn else: _warn = logger.warning if builder: warnings.warn('builder argument for generate_autosummary_docs() is deprecated.', - RemovedInSphinx50Warning) + RemovedInSphinx50Warning, stacklevel=2) if template_dir: warnings.warn('template_dir argument for generate_autosummary_docs() is deprecated.', - RemovedInSphinx50Warning) + RemovedInSphinx50Warning, stacklevel=2) showed_sources = list(sorted(sources)) if len(showed_sources) > 20: @@ -390,7 +390,7 @@ def find_autosummary_in_docstring(name: str, module: str = None, filename: str = """ if module: warnings.warn('module argument for find_autosummary_in_docstring() is deprecated.', - RemovedInSphinx50Warning) + RemovedInSphinx50Warning, stacklevel=2) try: real_name, obj, parent, modname = import_by_name(name) diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index 645521f9b..26966016d 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -47,7 +47,7 @@ doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE) def doctest_encode(text: str, encoding: str) -> str: warnings.warn('doctest_encode() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) return text diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py index c5cacc437..bfb46903f 100644 --- a/sphinx/ext/todo.py +++ b/sphinx/ext/todo.py @@ -105,7 +105,7 @@ class TodoDomain(Domain): def process_todos(app: Sphinx, doctree: nodes.document) -> None: - warnings.warn('process_todos() is deprecated.', RemovedInSphinx40Warning) + warnings.warn('process_todos() is deprecated.', RemovedInSphinx40Warning, stacklevel=2) # collect all todos in the environment # this is not done in the directive itself because it some transformations # must have already been run, e.g. substitutions @@ -221,7 +221,8 @@ def process_todo_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str) - """Replace all todolist nodes with a list of the collected todos. Augment each todo with a backlink to the original location. """ - warnings.warn('process_todo_nodes() is deprecated.', RemovedInSphinx40Warning) + warnings.warn('process_todo_nodes() is deprecated.', + RemovedInSphinx40Warning, stacklevel=2) domain = cast(TodoDomain, app.env.get_domain('todo')) todos = sum(domain.todos.values(), []) # type: List[todo_node] @@ -273,7 +274,7 @@ def process_todo_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str) - def purge_todos(app: Sphinx, env: BuildEnvironment, docname: str) -> None: - warnings.warn('purge_todos() is deprecated.', RemovedInSphinx40Warning) + warnings.warn('purge_todos() is deprecated.', RemovedInSphinx40Warning, stacklevel=2) if not hasattr(env, 'todo_all_todos'): return env.todo_all_todos = [todo for todo in env.todo_all_todos # type: ignore @@ -282,7 +283,7 @@ def purge_todos(app: Sphinx, env: BuildEnvironment, docname: str) -> None: def merge_info(app: Sphinx, env: BuildEnvironment, docnames: Iterable[str], other: BuildEnvironment) -> None: - warnings.warn('merge_info() is deprecated.', RemovedInSphinx40Warning) + warnings.warn('merge_info() is deprecated.', RemovedInSphinx40Warning, stacklevel=2) if not hasattr(other, 'todo_all_todos'): return if not hasattr(env, 'todo_all_todos'): diff --git a/sphinx/parsers.py b/sphinx/parsers.py index 3974d1c66..6a07d1801 100644 --- a/sphinx/parsers.py +++ b/sphinx/parsers.py @@ -64,7 +64,7 @@ class Parser(docutils.parsers.Parser): @property def app(self) -> "Sphinx": - warnings.warn('parser.app is deprecated.', RemovedInSphinx50Warning) + warnings.warn('parser.app is deprecated.', RemovedInSphinx50Warning, stacklevel=2) return self._app diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py index 0a6ff5214..4879fb349 100644 --- a/sphinx/pycode/__init__.py +++ b/sphinx/pycode/__init__.py @@ -133,7 +133,7 @@ class ModuleAnalyzer: pos = source.tell() if not decoded: warnings.warn('decode option for ModuleAnalyzer is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) self._encoding, _ = tokenize.detect_encoding(source.readline) source.seek(pos) self.code = source.read().decode(self._encoding) @@ -185,5 +185,5 @@ class ModuleAnalyzer: @property def encoding(self) -> str: warnings.warn('ModuleAnalyzer.encoding is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) return self._encoding diff --git a/sphinx/roles.py b/sphinx/roles.py index ff24fcf55..57d11c269 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -574,7 +574,7 @@ def index_role(typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner, class Index(ReferenceRole): def run(self) -> Tuple[List[Node], List[system_message]]: - warnings.warn('Index role is deprecated.', RemovedInSphinx40Warning) + warnings.warn('Index role is deprecated.', RemovedInSphinx40Warning, stacklevel=2) target_id = 'index-%s' % self.env.new_serialno('index') if self.has_explicit_title: # if an explicit target is given, process it as a full entry diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index d9853ff06..f9bbe0590 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -203,7 +203,7 @@ class WordCollector(nodes.NodeVisitor): def is_meta_keywords(self, node: addnodes.meta, nodetype: Any = None) -> bool: if nodetype is not None: warnings.warn('"nodetype" argument for WordCollector.is_meta_keywords() ' - 'is deprecated.', RemovedInSphinx40Warning) + 'is deprecated.', RemovedInSphinx40Warning, stacklevel=2) if isinstance(node, addnodes.meta) and node.get('name') == 'keywords': meta_lang = node.get('lang') diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 12ae051f1..73d25a8f6 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -110,7 +110,7 @@ def get_matching_docs(dirname: str, suffixes: List[str], Exclude files and dirs matching a pattern in *exclude_patterns*. """ warnings.warn('get_matching_docs() is now deprecated. Use get_matching_files() instead.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) suffixpatterns = ['*' + s for s in suffixes] for filename in get_matching_files(dirname, exclude_matchers): for suffixpattern in suffixpatterns: @@ -315,7 +315,7 @@ _coding_re = re.compile(r'coding[:=]\s*([-\w.]+)') def detect_encoding(readline: Callable[[], bytes]) -> str: """Like tokenize.detect_encoding() from Py3k, but a bit simplified.""" warnings.warn('sphinx.util.detect_encoding() is deprecated', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) def read_or_stop() -> bytes: try: diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py index 0135899eb..4923343ae 100644 --- a/sphinx/util/compat.py +++ b/sphinx/util/compat.py @@ -46,7 +46,7 @@ class IndexEntriesMigrator(SphinxTransform): if len(entries) == 4: source, line = get_source_line(node) warnings.warn('An old styled index node found: %r at (%s:%s)' % - (node, source, line), RemovedInSphinx40Warning) + (node, source, line), RemovedInSphinx40Warning, stacklevel=2) node['entries'][i] = entries + (None,) diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py index 699738888..c07bc7f66 100644 --- a/sphinx/util/docfields.py +++ b/sphinx/util/docfields.py @@ -224,12 +224,12 @@ class DocFieldTransformer: except Exception: # for 3rd party extensions directly calls this transformer. warnings.warn('DocFieldTransformer expects given directive object is a subclass ' - 'of ObjectDescription.', RemovedInSphinx40Warning) + 'of ObjectDescription.', RemovedInSphinx40Warning, stacklevel=2) self.typemap = self.preprocess_fieldtypes(directive.__class__.doc_field_types) def preprocess_fieldtypes(self, types: List[Field]) -> Dict[str, Tuple[Field, bool]]: warnings.warn('DocFieldTransformer.preprocess_fieldtypes() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) typemap = {} for fieldtype in types: for name in fieldtype.names: diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 9eb05b29b..c81edc949 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -58,7 +58,7 @@ def getargspec(func: Callable) -> Any: """Like inspect.getfullargspec but supports bound methods, and wrapped methods.""" warnings.warn('sphinx.ext.inspect.getargspec() is deprecated', - RemovedInSphinx50Warning) + RemovedInSphinx50Warning, stacklevel=2) # On 3.5+, signature(int) or similar raises ValueError. On 3.4, it # succeeds with a bogus signature. We want a TypeError uniformly, to # match historical behavior. @@ -329,7 +329,7 @@ def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any: def safe_getmembers(object: Any, predicate: Callable[[str], bool] = None, attr_getter: Callable = safe_getattr) -> List[Tuple[str, Any]]: """A version of inspect.getmembers() that uses safe_getattr().""" - warnings.warn('safe_getmembers() is deprecated', RemovedInSphinx40Warning) + warnings.warn('safe_getmembers() is deprecated', RemovedInSphinx40Warning, stacklevel=2) results = [] # type: List[Tuple[str, Any]] for key in dir(object): @@ -555,7 +555,7 @@ class Signature: def __init__(self, subject: Callable, bound_method: bool = False, has_retval: bool = True) -> None: warnings.warn('sphinx.util.inspect.Signature() is deprecated', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) # check subject is not a built-in class (ex. int, str) if (isinstance(subject, type) and diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index bca6e0bfc..33ce8e4a9 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -280,7 +280,7 @@ def extract_messages(doctree: Element) -> Iterable[Tuple[Element, str]]: def find_source_node(node: Element) -> str: warnings.warn('find_source_node() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) return get_node_source(node) diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index 248abd0e7..23f5b0137 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -86,7 +86,7 @@ def ensuredir(path: str) -> None: def walk(top: str, topdown: bool = True, followlinks: bool = False) -> Iterator[Tuple[str, List[str], List[str]]]: # NOQA warnings.warn('sphinx.util.osutil.walk() is deprecated for removal. ' 'Please use os.walk() instead.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) return os.walk(top, topdown=topdown, followlinks=followlinks) @@ -178,7 +178,7 @@ def abspath(pathdir: str) -> str: def getcwd() -> str: warnings.warn('sphinx.util.osutil.getcwd() is deprecated. ' 'Please use os.getcwd() instead.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) return os.getcwd() diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 9390ba5de..e3ddedccf 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -96,7 +96,7 @@ class LaTeXWriter(writers.Writer): visitor = self.builder.create_translator(self.document, self.builder, self.theme) except TypeError: warnings.warn('LaTeXTranslator now takes 3rd argument; "theme".', - RemovedInSphinx50Warning) + RemovedInSphinx50Warning, stacklevel=2) visitor = self.builder.create_translator(self.document, self.builder) self.document.walkabout(visitor) @@ -292,7 +292,7 @@ class LaTeXTranslator(SphinxTranslator): if theme is None: warnings.warn('LaTeXTranslator now takes 3rd argument; "theme".', - RemovedInSphinx50Warning) + RemovedInSphinx50Warning, stacklevel=2) # flags self.in_title = 0 @@ -2072,7 +2072,7 @@ class LaTeXTranslator(SphinxTranslator): def babel_defmacro(self, name: str, definition: str) -> str: warnings.warn('babel_defmacro() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) if self.elements['babel']: prefix = '\\addto\\extras%s{' % self.babel.get_language() @@ -2085,7 +2085,7 @@ class LaTeXTranslator(SphinxTranslator): def generate_numfig_format(self, builder: "LaTeXBuilder") -> str: warnings.warn('generate_numfig_format() is deprecated.', - RemovedInSphinx40Warning) + RemovedInSphinx40Warning, stacklevel=2) ret = [] # type: List[str] figure = self.builder.config.numfig_format['figure'].split('%s', 1) if len(figure) == 1: