From 49e7400123a10ad833af8997df58e98634091e4e Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Mon, 14 Sep 2020 21:01:41 +0200 Subject: [PATCH 01/17] C, fix names in get_objects() Fixes sphinx-doc/sphinx#8160 --- CHANGES | 1 + sphinx/domains/c.py | 26 ++++++++++++-------------- tests/test_domain_c.py | 18 +++++++++++------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/CHANGES b/CHANGES index ae9505742..564eb799a 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,7 @@ Bugs fixed * #8188: C, add missing items to internal object types dictionary, e.g., preventing intersphinx from resolving them. +* C, fix anon objects in intersphinx. Testing diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index c759dacbd..4d88bd8e8 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -12,7 +12,6 @@ import re from typing import ( Any, Callable, Dict, Generator, Iterator, List, Type, TypeVar, Tuple, Union ) -from typing import cast from docutils import nodes from docutils.nodes import Element, Node, TextElement, system_message @@ -3153,10 +3152,6 @@ class CObject(ObjectDescription): self.state.document.note_explicit_target(signode) - domain = cast(CDomain, self.env.get_domain('c')) - if name not in domain.objects: - domain.objects[name] = (domain.env.docname, newestId, self.objtype) - if 'noindexentry' not in self.options: indexText = self.get_index_text(name) self.indexnode['entries'].append(('single', indexText, newestId, '', None)) @@ -3649,10 +3644,6 @@ class CDomain(Domain): 'objects': {}, # fullname -> docname, node_id, objtype } # type: Dict[str, Union[Symbol, Dict[str, Tuple[str, str, str]]]] - @property - def objects(self) -> Dict[str, Tuple[str, str, str]]: - return self.data.setdefault('objects', {}) # fullname -> docname, node_id, objtype - def clear_doc(self, docname: str) -> None: if Symbol.debug_show_tree: print("clear_doc:", docname) @@ -3668,9 +3659,6 @@ class CDomain(Domain): print(self.data['root_symbol'].dump(1)) print("\tafter end") print("clear_doc end:", docname) - for fullname, (fn, _id, _l) in list(self.objects.items()): - if fn == docname: - del self.objects[fullname] def process_doc(self, env: BuildEnvironment, docname: str, document: nodes.document) -> None: @@ -3756,8 +3744,18 @@ class CDomain(Domain): return [] def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]: - for refname, (docname, node_id, objtype) in list(self.objects.items()): - yield (refname, refname, objtype, docname, node_id, 1) + rootSymbol = self.data['root_symbol'] + for symbol in rootSymbol.get_all_symbols(): + if symbol.declaration is None: + continue + assert symbol.docname + fullNestedName = symbol.get_full_nested_name() + name = str(fullNestedName).lstrip('.') + dispname = fullNestedName.get_display_string().lstrip('.') + objectType = symbol.declaration.objectType + docname = symbol.docname + newestId = symbol.declaration.get_newest_id() + yield (name, dispname, objectType, docname, newestId, 1) def setup(app: Sphinx) -> Dict[str, Any]: diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index b6f72287e..7509b1aa9 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -562,6 +562,13 @@ def test_build_domain_c_semicolon(app, status, warning): assert len(ws) == 0 +def _get_obj(app, queryName): + domain = app.env.get_domain('c') + for name, dispname, objectType, docname, anchor, prio in domain.get_objects(): + if name == queryName: + return (docname, anchor, objectType) + return (queryName, "not", "found") + def test_cfunction(app): text = (".. c:function:: PyObject* " "PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)") @@ -569,8 +576,7 @@ def test_cfunction(app): assert_node(doctree[1], addnodes.desc, desctype="function", domain="c", objtype="function", noindex=False) - domain = app.env.get_domain('c') - entry = domain.objects.get('PyType_GenericAlloc') + entry = _get_obj(app, 'PyType_GenericAlloc') assert entry == ('index', 'c.PyType_GenericAlloc', 'function') @@ -580,8 +586,7 @@ def test_cmember(app): assert_node(doctree[1], addnodes.desc, desctype="member", domain="c", objtype="member", noindex=False) - domain = app.env.get_domain('c') - entry = domain.objects.get('PyTypeObject.tp_bases') + entry = _get_obj(app, 'PyTypeObject.tp_bases') assert entry == ('index', 'c.PyTypeObject.tp_bases', 'member') @@ -591,9 +596,8 @@ def test_cvar(app): assert_node(doctree[1], addnodes.desc, desctype="var", domain="c", objtype="var", noindex=False) - domain = app.env.get_domain('c') - entry = domain.objects.get('PyClass_Type') - assert entry == ('index', 'c.PyClass_Type', 'var') + entry = _get_obj(app, 'PyClass_Type') + assert entry == ('index', 'c.PyClass_Type', 'member') def test_noindexentry(app): From 6f5d45ffff68c2c1892f1a29af0ea1bd5e4e4796 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 3 Oct 2020 15:20:41 +0200 Subject: [PATCH 02/17] C++, improve warning and debug messages --- sphinx/domains/cpp.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 7b10f8166..e5a6201e5 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -4107,7 +4107,7 @@ class Symbol: Symbol.debug_print("self:") print(self.to_string(Symbol.debug_indent + 1), end="") Symbol.debug_print("nestedName: ", nestedName) - Symbol.debug_print("templateDecls: ", templateDecls) + Symbol.debug_print("templateDecls: ", ",".join(str(t) for t in templateDecls)) Symbol.debug_print("strictTemplateParamArgLists:", strictTemplateParamArgLists) Symbol.debug_print("ancestorLookupType:", ancestorLookupType) Symbol.debug_print("templateShorthand: ", templateShorthand) @@ -4231,7 +4231,7 @@ class Symbol: Symbol.debug_indent += 1 Symbol.debug_print("_add_symbols:") Symbol.debug_indent += 1 - Symbol.debug_print("tdecls:", templateDecls) + Symbol.debug_print("tdecls:", ",".join(str(t) for t in templateDecls)) Symbol.debug_print("nn: ", nestedName) Symbol.debug_print("decl: ", declaration) Symbol.debug_print("doc: ", docname) @@ -4370,7 +4370,11 @@ class Symbol: # if there is an empty symbol, fill that one if len(noDecl) == 0: if Symbol.debug_lookup: - Symbol.debug_print("no match, no empty, candSybmol is not None?:", candSymbol is not None) # NOQA + Symbol.debug_print("no match, no empty") + if candSymbol is not None: + Symbol.debug_print("result is already created candSymbol") + else: + Symbol.debug_print("result is makeCandSymbol()") Symbol.debug_indent -= 2 if candSymbol is not None: return candSymbol @@ -6814,10 +6818,12 @@ class CPPObject(ObjectDescription): parentSymbol = env.temp_data['cpp:parent_symbol'] parentDecl = parentSymbol.declaration if parentDecl is not None and parentDecl.objectType == 'function': - logger.warning("C++ declarations inside functions are not supported." + - " Parent function is " + - str(parentSymbol.get_full_nested_name()), - location=self.get_source_info()) + msg = "C++ declarations inside functions are not supported." \ + " Parent function: {}\nDirective name: {}\nDirective arg: {}" + logger.warning(msg.format( + str(parentSymbol.get_full_nested_name()), + self.name, self.arguments[0] + ), location=self.get_source_info()) name = _make_phony_error_name() symbol = parentSymbol.add_name(name) env.temp_data['cpp:last_symbol'] = symbol From a555e3db8a2e38dc7f79c3dd8ac6cd406f70c9e1 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 12 Sep 2020 21:15:20 +0900 Subject: [PATCH 03/17] Fix #8200: autodoc: type aliases break type formatting The annotation option is shared between auto directives unexpectedly. It causes supression of type annotations for objects after GenericAlias definition. --- CHANGES | 1 + sphinx/ext/autodoc/__init__.py | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index df70ab46e..5637f66d6 100644 --- a/CHANGES +++ b/CHANGES @@ -34,6 +34,7 @@ Bugs fixed by string not ending with blank lines * #8142: autodoc: Wrong constructor signature for the class derived from typing.Generic +* #8200: autodoc: type aliases break type formatting of autoattribute * #8192: napoleon: description is disappeared when it contains inline literals * #8142: napoleon: Potential of regex denial of service in google style docs * #8169: LaTeX: pxjahyper loaded even when latex_engine is not platex diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index d7c5d2242..83c44c8df 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1731,7 +1731,8 @@ class GenericAliasDocumenter(DataDocumenter): return inspect.isgenericalias(member) def add_directive_header(self, sig: str) -> None: - self.options.annotation = SUPPRESS # type: ignore + self.options = Options(self.options) + self.options['annotation'] = SUPPRESS super().add_directive_header(sig) def add_content(self, more_content: Any, no_docstring: bool = False) -> None: @@ -1755,7 +1756,8 @@ class TypeVarDocumenter(DataDocumenter): return isinstance(member, TypeVar) and isattr # type: ignore def add_directive_header(self, sig: str) -> None: - self.options.annotation = SUPPRESS # type: ignore + self.options = Options(self.options) + self.options['annotation'] = SUPPRESS super().add_directive_header(sig) def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]: From 777bcb43fa26aeaf34329c56cb1e316da0a1d9e6 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 3 Oct 2020 16:00:28 +0200 Subject: [PATCH 04/17] C++, properly reject functions as duplicates Fixes sphinx-doc/sphinx#8270 --- CHANGES | 2 ++ sphinx/domains/cpp.py | 5 +++++ tests/test_domain_cpp.py | 15 +++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/CHANGES b/CHANGES index 564eb799a..93cc5fb8c 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,8 @@ Bugs fixed * #8188: C, add missing items to internal object types dictionary, e.g., preventing intersphinx from resolving them. * C, fix anon objects in intersphinx. +* #8270, C++, properly reject functions as duplicate declarations if a + non-function declaration of the same name already exists. Testing diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index e5a6201e5..c43c55d5b 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -4360,6 +4360,11 @@ class Symbol: if Symbol.debug_lookup: Symbol.debug_print("candId:", candId) for symbol in withDecl: + # but all existing must be functions as well, + # otherwise we declare it to be a duplicate + if symbol.declaration.objectType != 'function': + handleDuplicateDeclaration(symbol, candSymbol) + # (not reachable) oldId = symbol.declaration.get_newest_id() if Symbol.debug_lookup: Symbol.debug_print("oldId: ", oldId) diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 558d69911..ec7a2b262 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -1236,3 +1236,18 @@ def test_noindexentry(app): assert_node(doctree, (addnodes.index, desc, addnodes.index, desc)) assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C++ function)', '_CPPv41fv', '', None)]) assert_node(doctree[2], addnodes.index, entries=[]) + + +def test_mix_decl_duplicate(app, warning): + # Issue 8270 + text = (".. cpp:struct:: A\n" + ".. cpp:function:: void A()\n" + ".. cpp:struct:: A\n") + restructuredtext.parse(app, text) + ws = warning.getvalue().split("\n") + assert len(ws) == 5 + assert "index.rst:2: WARNING: Duplicate C++ declaration, also defined in 'index'." in ws[0] + assert "Declaration is 'void A()'." in ws[1] + assert "index.rst:3: WARNING: Duplicate C++ declaration, also defined in 'index'." in ws[2] + assert "Declaration is 'A'." in ws[3] + assert ws[4] == "" \ No newline at end of file From 7f7a207626cfa62871fb315c70e88ea690f85250 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 4 Oct 2020 14:13:03 +0900 Subject: [PATCH 05/17] docs: Add documentation for "override" flag of app API --- sphinx/application.py | 51 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/sphinx/application.py b/sphinx/application.py index f91027bf7..daab09058 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -471,8 +471,10 @@ class Sphinx: def add_builder(self, builder: "Type[Builder]", override: bool = False) -> None: """Register a new builder. - *builder* must be a class that inherits from - :class:`~sphinx.builders.Builder`. + *builder* must be a class that inherits from :class:`~sphinx.builders.Builder`. + + If *override* is True, the given *builder* is forcedly installed even if + a builder having the same name is already installed. .. versionchanged:: 1.8 Add *override* keyword. @@ -529,6 +531,9 @@ class Sphinx: builtin translator. This allows extensions to use custom translator and define custom nodes for the translator (see :meth:`add_node`). + If *override* is True, the given *translator_class* is forcedly installed even if + a translator for *name* is already installed. + .. versionadded:: 1.3 .. versionchanged:: 1.8 Add *override* keyword. @@ -563,6 +568,9 @@ class Sphinx: Obviously, translators for which you don't specify visitor methods will choke on the node when encountered in a document to translate. + If *override* is True, the given *node* is forcedly installed even if + a node having the same name is already installed. + .. versionchanged:: 0.5 Added the support for keyword arguments giving visit functions. """ @@ -598,6 +606,9 @@ class Sphinx: Other keyword arguments are used for node visitor functions. See the :meth:`.Sphinx.add_node` for details. + If *override* is True, the given *node* is forcedly installed even if + a node having the same name is already installed. + .. versionadded:: 1.4 """ self.registry.add_enumerable_node(node, figtype, title_getter, override=override) @@ -633,6 +644,9 @@ class Sphinx: add_directive('literalinclude', LiteralIncludeDirective) + If *override* is True, the given *cls* is forcedly installed even if + a directive named as *name* is already installed. + .. versionchanged:: 0.6 Docutils 0.5-style directive classes are now supported. .. deprecated:: 1.8 @@ -655,6 +669,9 @@ class Sphinx: `_ for more information. + If *override* is True, the given *role* is forcedly installed even if + a role named as *name* is already installed. + .. versionchanged:: 1.8 Add *override* keyword. """ @@ -670,6 +687,9 @@ class Sphinx: Register a Docutils role that does nothing but wrap its contents in the node given by *nodeclass*. + If *override* is True, the given *nodeclass* is forcedly installed even if + a role named as *name* is already installed. + .. versionadded:: 0.6 .. versionchanged:: 1.8 Add *override* keyword. @@ -689,6 +709,9 @@ class Sphinx: Make the given *domain* (which must be a class; more precisely, a subclass of :class:`~sphinx.domains.Domain`) known to Sphinx. + If *override* is True, the given *domain* is forcedly installed even if + a domain having the same name is already installed. + .. versionadded:: 1.0 .. versionchanged:: 1.8 Add *override* keyword. @@ -702,6 +725,9 @@ class Sphinx: Like :meth:`add_directive`, but the directive is added to the domain named *domain*. + If *override* is True, the given *directive* is forcedly installed even if + a directive named as *name* is already installed. + .. versionadded:: 1.0 .. versionchanged:: 1.8 Add *override* keyword. @@ -715,6 +741,9 @@ class Sphinx: Like :meth:`add_role`, but the role is added to the domain named *domain*. + If *override* is True, the given *role* is forcedly installed even if + a role named as *name* is already installed. + .. versionadded:: 1.0 .. versionchanged:: 1.8 Add *override* keyword. @@ -728,6 +757,9 @@ class Sphinx: Add a custom *index* class to the domain named *domain*. *index* must be a subclass of :class:`~sphinx.domains.Index`. + If *override* is True, the given *index* is forcedly installed even if + an index having the same name is already installed. + .. versionadded:: 1.0 .. versionchanged:: 1.8 Add *override* keyword. @@ -791,6 +823,9 @@ class Sphinx: For the role content, you have the same syntactical possibilities as for standard Sphinx roles (see :ref:`xref-syntax`). + If *override* is True, the given object_type is forcedly installed even if + an object_type having the same name is already installed. + .. versionchanged:: 1.8 Add *override* keyword. """ @@ -827,6 +862,9 @@ class Sphinx: (Of course, the element following the ``topic`` directive needn't be a section.) + If *override* is True, the given crossref_type is forcedly installed even if + a crossref_type having the same name is already installed. + .. versionchanged:: 1.8 Add *override* keyword. """ @@ -1022,6 +1060,9 @@ class Sphinx: new types of objects. See the source of the autodoc module for examples on how to subclass :class:`Documenter`. + If *override* is True, the given *cls* is forcedly installed even if + a documenter having the same name is already installed. + .. todo:: Add real docs for Documenter and subclassing .. versionadded:: 0.6 @@ -1070,6 +1111,9 @@ class Sphinx: Same as :confval:`source_suffix`. The users can override this using the setting. + If *override* is True, the given *suffix* is forcedly installed even if + a same suffix is already installed. + .. versionadded:: 1.8 """ self.registry.add_source_suffix(suffix, filetype, override=override) @@ -1077,6 +1121,9 @@ class Sphinx: def add_source_parser(self, *args: Any, **kwargs: Any) -> None: """Register a parser class. + If *override* is True, the given *parser* is forcedly installed even if + a parser for the same suffix is already installed. + .. versionadded:: 1.4 .. versionchanged:: 1.8 *suffix* argument is deprecated. It only accepts *parser* argument. From 129e09c6e3c591e1376c164672220ced3f3cee12 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 4 Oct 2020 14:15:41 +0900 Subject: [PATCH 06/17] refactor: Change signature of app.add_source_parser() To make compatible with old versions, app.add_source_parser() have taken two types of arguments. But the compatibility was no longer needed since 3.0. So it would be better to use clearer signature. --- sphinx/application.py | 5 +++-- sphinx/registry.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sphinx/application.py b/sphinx/application.py index f91027bf7..1c433107e 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -22,6 +22,7 @@ from typing import Any, Callable, Dict, IO, List, Optional, Tuple, Union from docutils import nodes from docutils.nodes import Element, TextElement +from docutils.parsers import Parser from docutils.parsers.rst import Directive, roles from docutils.transforms import Transform from pygments.lexer import Lexer @@ -1074,7 +1075,7 @@ class Sphinx: """ self.registry.add_source_suffix(suffix, filetype, override=override) - def add_source_parser(self, *args: Any, **kwargs: Any) -> None: + def add_source_parser(self, parser: "Type[Parser]", override: bool = False) -> None: """Register a parser class. .. versionadded:: 1.4 @@ -1084,7 +1085,7 @@ class Sphinx: .. versionchanged:: 1.8 Add *override* keyword. """ - self.registry.add_source_parser(*args, **kwargs) + self.registry.add_source_parser(parser, override=override) def add_env_collector(self, collector: "Type[EnvironmentCollector]") -> None: """Register an environment collector class. diff --git a/sphinx/registry.py b/sphinx/registry.py index 8c468796a..d0c00b85f 100644 --- a/sphinx/registry.py +++ b/sphinx/registry.py @@ -259,12 +259,12 @@ class SphinxComponentRegistry: else: self.source_suffix[suffix] = filetype - def add_source_parser(self, parser: "Type[Parser]", **kwargs: Any) -> None: + def add_source_parser(self, parser: "Type[Parser]", override: bool = False) -> None: logger.debug('[app] adding search source_parser: %r', parser) # create a map from filetype to parser for filetype in parser.supported: - if filetype in self.source_parsers and not kwargs.get('override'): + if filetype in self.source_parsers and not override: raise ExtensionError(__('source_parser for %r is already registered') % filetype) else: From 7b395f6b25ce1fa78480508a1de4be2510fd2189 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 4 Oct 2020 14:22:02 +0900 Subject: [PATCH 07/17] docs: Fix an example for add_directive() --- sphinx/application.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sphinx/application.py b/sphinx/application.py index f91027bf7..d06986764 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -611,14 +611,14 @@ class Sphinx: details, see `the Docutils docs `_ . - For example, the (already existing) :rst:dir:`literalinclude` directive - would be added like this: + For example, a custom directive named ``my-directive`` would be added + like this: .. code-block:: python from docutils.parsers.rst import Directive, directives - class LiteralIncludeDirective(Directive): + class MyDirective(Directive): has_content = True required_arguments = 1 optional_arguments = 0 @@ -631,7 +631,8 @@ class Sphinx: def run(self): ... - add_directive('literalinclude', LiteralIncludeDirective) + def setup(app): + add_directive('my-directive', MyDirective) .. versionchanged:: 0.6 Docutils 0.5-style directive classes are now supported. From 0b32e72635f6e117824b5cba3b8b38254a6a2644 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 4 Oct 2020 01:45:47 +0900 Subject: [PATCH 08/17] pycode: ast.unparse() construct number literals using source code Developers can write number literals in several ways. For example, decimal (1234), hexadecimal (0x1234), octal decimal (0o1234) and so on. But, AST module don't mind how the numbers written in the code. As a result, ast.unparse() could not reproduce the original form of number literals. This allows to construct number literals as possible using original source code. Note: This is only available in Python 3.8+. --- sphinx/pycode/ast.py | 11 +++++++++-- tests/test_pycode_ast.py | 14 +++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/sphinx/pycode/ast.py b/sphinx/pycode/ast.py index 2583448d5..17d78f4eb 100644 --- a/sphinx/pycode/ast.py +++ b/sphinx/pycode/ast.py @@ -58,17 +58,19 @@ def parse(code: str, mode: str = 'exec') -> "ast.AST": return ast.parse(code, mode=mode) -def unparse(node: Optional[ast.AST]) -> Optional[str]: +def unparse(node: Optional[ast.AST], code: str = '') -> Optional[str]: """Unparse an AST to string.""" if node is None: return None elif isinstance(node, str): return node - return _UnparseVisitor().visit(node) + return _UnparseVisitor(code).visit(node) # a greatly cut-down version of `ast._Unparser` class _UnparseVisitor(ast.NodeVisitor): + def __init__(self, code: str = '') -> None: + self.code = code def _visit_op(self, node: ast.AST) -> str: return OPERATORS[node.__class__] @@ -195,6 +197,11 @@ class _UnparseVisitor(ast.NodeVisitor): def visit_Constant(self, node: ast.Constant) -> str: if node.value is Ellipsis: return "..." + elif isinstance(node.value, (int, float, complex)): + if self.code and sys.version_info > (3, 8): + return ast.get_source_segment(self.code, node) + else: + return repr(node.value) else: return repr(node.value) diff --git a/tests/test_pycode_ast.py b/tests/test_pycode_ast.py index 32a784b74..bbff64dd0 100644 --- a/tests/test_pycode_ast.py +++ b/tests/test_pycode_ast.py @@ -58,7 +58,7 @@ from sphinx.pycode import ast ]) def test_unparse(source, expected): module = ast.parse(source) - assert ast.unparse(module.body[0].value) == expected + assert ast.unparse(module.body[0].value, source) == expected def test_unparse_None(): @@ -66,8 +66,12 @@ def test_unparse_None(): @pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.') -def test_unparse_py38(): - source = "lambda x=0, /, y=1, *args, z, **kwargs: x + y + z" - expected = "lambda x=0, /, y=1, *args, z, **kwargs: ..." +@pytest.mark.parametrize('source,expected', [ + ("lambda x=0, /, y=1, *args, z, **kwargs: x + y + z", + "lambda x=0, /, y=1, *args, z, **kwargs: ..."), # posonlyargs + ("0x1234", "0x1234"), # Constant + ("1_000_000", "1_000_000"), # Constant +]) +def test_unparse_py38(source, expected): module = ast.parse(source) - assert ast.unparse(module.body[0].value) == expected + assert ast.unparse(module.body[0].value, source) == expected From cc941db40b946534da8897a29631325d96313a6e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 4 Oct 2020 10:31:02 +0900 Subject: [PATCH 09/17] Fix #8255: py domain: number in defarg is changed to decimal Number literals in default argument value is converted to decimal form unexpectedly by AST module. This fixes the signature parsing code to recosntruct it correctly. Note: This is only available in Python 3.8+. --- CHANGES | 2 ++ sphinx/util/inspect.py | 25 +++++++++++++------------ tests/test_domain_py.py | 13 +++++++++++++ 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/CHANGES b/CHANGES index 600efc466..c907992dc 100644 --- a/CHANGES +++ b/CHANGES @@ -49,6 +49,8 @@ Bugs fixed * #8277: sphinx-build: missing and redundant spacing (and etc) for console output on building * #7973: imgconverter: Check availability of imagemagick many times +* #8255: py domain: number in default argument value is changed from hexadecimal + to decimal * #8093: The highlight warning has wrong location in some builders (LaTeX, singlehtml and so on) * #8239: Failed to refer a token in productionlist if it is indented diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 378174993..f2cd8070b 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -600,13 +600,14 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True, def signature_from_str(signature: str) -> inspect.Signature: """Create a Signature object from string.""" - module = ast.parse('def func' + signature + ': pass') + code = 'def func' + signature + ': pass' + module = ast.parse(code) function = cast(ast.FunctionDef, module.body[0]) # type: ignore - return signature_from_ast(function) + return signature_from_ast(function, code) -def signature_from_ast(node: ast.FunctionDef) -> inspect.Signature: +def signature_from_ast(node: ast.FunctionDef, code: str = '') -> inspect.Signature: """Create a Signature object from AST *node*.""" args = node.args defaults = list(args.defaults) @@ -626,9 +627,9 @@ def signature_from_ast(node: ast.FunctionDef) -> inspect.Signature: if defaults[i] is Parameter.empty: default = Parameter.empty else: - default = ast_unparse(defaults[i]) + default = ast_unparse(defaults[i], code) - annotation = ast_unparse(arg.annotation) or Parameter.empty + annotation = ast_unparse(arg.annotation, code) or Parameter.empty params.append(Parameter(arg.arg, Parameter.POSITIONAL_ONLY, default=default, annotation=annotation)) @@ -636,29 +637,29 @@ def signature_from_ast(node: ast.FunctionDef) -> inspect.Signature: if defaults[i + posonlyargs] is Parameter.empty: default = Parameter.empty else: - default = ast_unparse(defaults[i + posonlyargs]) + default = ast_unparse(defaults[i + posonlyargs], code) - annotation = ast_unparse(arg.annotation) or Parameter.empty + annotation = ast_unparse(arg.annotation, code) or Parameter.empty params.append(Parameter(arg.arg, Parameter.POSITIONAL_OR_KEYWORD, default=default, annotation=annotation)) if args.vararg: - annotation = ast_unparse(args.vararg.annotation) or Parameter.empty + annotation = ast_unparse(args.vararg.annotation, code) or Parameter.empty params.append(Parameter(args.vararg.arg, Parameter.VAR_POSITIONAL, annotation=annotation)) for i, arg in enumerate(args.kwonlyargs): - default = ast_unparse(args.kw_defaults[i]) or Parameter.empty - annotation = ast_unparse(arg.annotation) or Parameter.empty + default = ast_unparse(args.kw_defaults[i], code) or Parameter.empty + annotation = ast_unparse(arg.annotation, code) or Parameter.empty params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default, annotation=annotation)) if args.kwarg: - annotation = ast_unparse(args.kwarg.annotation) or Parameter.empty + annotation = ast_unparse(args.kwarg.annotation, code) or Parameter.empty params.append(Parameter(args.kwarg.arg, Parameter.VAR_KEYWORD, annotation=annotation)) - return_annotation = ast_unparse(node.returns) or Parameter.empty + return_annotation = ast_unparse(node.returns, code) or Parameter.empty return inspect.Signature(params, return_annotation=return_annotation) diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index b98f37912..8040af9cc 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -386,6 +386,19 @@ def test_pyfunction_signature_full_py38(app): [desc_parameter, desc_sig_operator, "/"])]) +@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.') +def test_pyfunction_with_number_literals(app): + text = ".. py:function:: hello(age=0x10, height=1_6_0)" + doctree = restructuredtext.parse(app, text) + assert_node(doctree[1][0][1], + [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "age"], + [desc_sig_operator, "="], + [nodes.inline, "0x10"])], + [desc_parameter, ([desc_sig_name, "height"], + [desc_sig_operator, "="], + [nodes.inline, "1_6_0"])])]) + + def test_optional_pyfunction_signature(app): text = ".. py:function:: compile(source [, filename [, symbol]]) -> ast object" doctree = restructuredtext.parse(app, text) From bd49c3c2efad25707eb95b7a68b19d886c9e4f7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Mon, 5 Oct 2020 08:32:26 +0200 Subject: [PATCH 10/17] Outdated comment in docs config for intersphinx Intersphinx is in use since 6a396c7eb85e4f2e2291652c454352bad1a397f4. --- doc/conf.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index f62e02a34..74e5a8b80 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -110,8 +110,6 @@ texinfo_documents = [ 1), ] -# We're not using intersphinx right now, but if we did, this would be part of -# the mapping: intersphinx_mapping = {'python': ('https://docs.python.org/3/', None)} # Sphinx document translation with sphinx gettext feature uses these settings: From 6dbe28a632c4c9c6c12beaf8e0f6a00d5fa163c6 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 4 Oct 2020 22:23:45 +0900 Subject: [PATCH 11/17] Fix #7786: autodoc: can't detect overloaded methods defined in other file --- CHANGES | 1 + sphinx/ext/autodoc/__init__.py | 30 +++++++++++-------- .../test-ext-autodoc/target/overload2.py | 5 ++++ tests/test_ext_autodoc.py | 16 ++++++++++ 4 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 tests/roots/test-ext-autodoc/target/overload2.py diff --git a/CHANGES b/CHANGES index 600efc466..3ee075353 100644 --- a/CHANGES +++ b/CHANGES @@ -42,6 +42,7 @@ Bugs fixed * #8157: autodoc: TypeError is raised when annotation has invalid __args__ * #7964: autodoc: Tuple in default value is wrongly rendered * #8200: autodoc: type aliases break type formatting of autoattribute +* #7786: autodoc: can't detect overloaded methods defined in other file * #8192: napoleon: description is disappeared when it contains inline literals * #8142: napoleon: Potential of regex denial of service in google style docs * #8169: LaTeX: pxjahyper loaded even when latex_engine is not platex diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 434fc40e9..b68fc73c9 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1471,22 +1471,14 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: return '' sig = super().format_signature() - - overloaded = False - qualname = None - # TODO: recreate analyzer for the module of class (To be clear, owner of the method) - if self._signature_class and self._signature_method_name and self.analyzer: - qualname = '.'.join([self._signature_class.__qualname__, - self._signature_method_name]) - if qualname in self.analyzer.overloads: - overloaded = True - sigs = [] - if overloaded: + + overloads = self.get_overloaded_signatures() + if overloads: # Use signatures for overloaded methods instead of the implementation method. method = safe_getattr(self._signature_class, self._signature_method_name, None) __globals__ = safe_getattr(method, '__globals__', {}) - for overload in self.analyzer.overloads.get(qualname): + for overload in overloads: overload = evaluate_signature(overload, __globals__, self.env.config.autodoc_type_aliases) @@ -1500,6 +1492,20 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: return "\n".join(sigs) + def get_overloaded_signatures(self) -> List[Signature]: + if self._signature_class and self._signature_method_name: + for cls in self._signature_class.__mro__: + try: + analyzer = ModuleAnalyzer.for_module(cls.__module__) + analyzer.parse() + qualname = '.'.join([cls.__qualname__, self._signature_method_name]) + if qualname in analyzer.overloads: + return analyzer.overloads.get(qualname) + except PycodeError: + pass + + return [] + def add_directive_header(self, sig: str) -> None: sourcename = self.get_sourcename() diff --git a/tests/roots/test-ext-autodoc/target/overload2.py b/tests/roots/test-ext-autodoc/target/overload2.py new file mode 100644 index 000000000..e901f791b --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/overload2.py @@ -0,0 +1,5 @@ +from target.overload import Bar + + +class Baz(Bar): + pass diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 7fff09bb6..9cb54de5b 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -2002,6 +2002,22 @@ def test_overload(app): ] +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_overload2(app): + options = {"members": None} + actual = do_autodoc(app, 'module', 'target.overload2', options) + assert list(actual) == [ + '', + '.. py:module:: target.overload2', + '', + '', + '.. py:class:: Baz(x: int, y: int)', + ' Baz(x: str, y: str)', + ' :module: target.overload2', + '', + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_pymodule_for_ModuleLevelDocumenter(app): app.env.ref_context['py:module'] = 'target.classes' From d10802fa6ab7ca6f0e48c67b36da888da291d011 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 7 Oct 2020 14:23:02 -0400 Subject: [PATCH 12/17] TST: Add test --- tests/test_domain_py.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 8040af9cc..ceea51508 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -313,7 +313,7 @@ def test_pyfunction_signature(app): def test_pyfunction_signature_full(app): text = (".. py:function:: hello(a: str, b = 1, *args: str, " - "c: bool = True, **kwargs: str) -> str") + "c: bool = True, d: tuple = (1, 2), **kwargs: str) -> str") doctree = restructuredtext.parse(app, text) assert_node(doctree, (addnodes.index, [desc, ([desc_signature, ([desc_name, "hello"], @@ -343,6 +343,14 @@ def test_pyfunction_signature_full(app): [desc_sig_operator, "="], " ", [nodes.inline, "True"])], + [desc_parameter, ([desc_sig_name, "d"], + [desc_sig_punctuation, ":"], + " ", + [desc_sig_name, pending_xref, "tuple"], + " ", + [desc_sig_operator, "="], + " ", + [nodes.inline, "(1, 2)"])], [desc_parameter, ([desc_sig_operator, "**"], [desc_sig_name, "kwargs"], [desc_sig_punctuation, ":"], From 15251574a97ffa13b9c62255733296c2a3ab50b4 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 13 Oct 2020 12:30:13 +0200 Subject: [PATCH 13/17] C, fix links to function parameters --- CHANGES | 2 + sphinx/domains/c.py | 38 +++++++++++++++---- sphinx/domains/cpp.py | 2 +- .../test-domain-c/function_param_target.rst | 5 +++ tests/test_domain_c.py | 34 +++++++++++++++++ 5 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 tests/roots/test-domain-c/function_param_target.rst diff --git a/CHANGES b/CHANGES index 93cc5fb8c..a8426dff8 100644 --- a/CHANGES +++ b/CHANGES @@ -21,6 +21,8 @@ Bugs fixed * C, fix anon objects in intersphinx. * #8270, C++, properly reject functions as duplicate declarations if a non-function declaration of the same name already exists. +* C, fix references to function parameters. + Link to the function instead of a non-existing anchor. Testing diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 4d88bd8e8..16f2c876f 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -10,7 +10,7 @@ import re from typing import ( - Any, Callable, Dict, Generator, Iterator, List, Type, TypeVar, Tuple, Union + Any, Callable, cast, Dict, Generator, Iterator, List, Type, TypeVar, Tuple, Union ) from docutils import nodes @@ -46,6 +46,11 @@ from sphinx.util.nodes import make_refnode logger = logging.getLogger(__name__) T = TypeVar('T') +DeclarationType = Union[ + "ASTStruct", "ASTUnion", "ASTEnum", "ASTEnumerator", + "ASTType", "ASTTypeWithInit", "ASTMacro", +] + # https://en.cppreference.com/w/c/keyword _keywords = [ 'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double', @@ -635,6 +640,10 @@ class ASTFunctionParameter(ASTBase): self.arg = arg self.ellipsis = ellipsis + def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str: + # the anchor will be our parent + return symbol.parent.declaration.get_id(version, prefixed=False) + def _stringify(self, transform: StringifyTransform) -> str: if self.ellipsis: return '...' @@ -1148,6 +1157,9 @@ class ASTType(ASTBase): def name(self) -> ASTNestedName: return self.decl.name + def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str: + return symbol.get_full_nested_name().get_id(version) + @property def function_params(self) -> List[ASTFunctionParameter]: return self.decl.function_params @@ -1190,6 +1202,9 @@ class ASTTypeWithInit(ASTBase): def name(self) -> ASTNestedName: return self.type.name + def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str: + return self.type.get_id(version, objectType, symbol) + def _stringify(self, transform: StringifyTransform) -> str: res = [] res.append(transform(self.type)) @@ -1241,6 +1256,9 @@ class ASTMacro(ASTBase): def name(self) -> ASTNestedName: return self.ident + def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str: + return symbol.get_full_nested_name().get_id(version) + def _stringify(self, transform: StringifyTransform) -> str: res = [] res.append(transform(self.ident)) @@ -1341,7 +1359,8 @@ class ASTEnumerator(ASTBase): class ASTDeclaration(ASTBaseBase): - def __init__(self, objectType: str, directiveType: str, declaration: Any, + def __init__(self, objectType: str, directiveType: str, + declaration: Union[DeclarationType, ASTFunctionParameter], semicolon: bool = False) -> None: self.objectType = objectType self.directiveType = directiveType @@ -1358,18 +1377,20 @@ class ASTDeclaration(ASTBaseBase): @property def name(self) -> ASTNestedName: - return self.declaration.name + decl = cast(DeclarationType, self.declaration) + return decl.name @property def function_params(self) -> List[ASTFunctionParameter]: if self.objectType != 'function': return None - return self.declaration.function_params + decl = cast(ASTType, self.declaration) + return decl.function_params def get_id(self, version: int, prefixed: bool = True) -> str: if self.objectType == 'enumerator' and self.enumeratorScopedSymbol: return self.enumeratorScopedSymbol.declaration.get_id(version, prefixed) - id_ = self.symbol.get_full_nested_name().get_id(version) + id_ = self.declaration.get_id(version, self.objectType, self.symbol) if prefixed: return _id_prefix[version] + id_ else: @@ -1412,7 +1433,8 @@ class ASTDeclaration(ASTBaseBase): elif self.objectType == 'enumerator': mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ') elif self.objectType == 'type': - prefix = self.declaration.get_type_declaration_prefix() + decl = cast(ASTType, self.declaration) + prefix = decl.get_type_declaration_prefix() prefix += ' ' mainDeclNode += addnodes.desc_annotation(prefix, prefix) else: @@ -2982,7 +3004,7 @@ class DefinitionParser(BaseParser): def parse_pre_v3_type_definition(self) -> ASTDeclaration: self.skip_ws() - declaration = None # type: Any + declaration = None # type: DeclarationType if self.skip_word('struct'): typ = 'struct' declaration = self._parse_struct() @@ -3005,7 +3027,7 @@ class DefinitionParser(BaseParser): 'macro', 'struct', 'union', 'enum', 'enumerator', 'type'): raise Exception('Internal error, unknown directiveType "%s".' % directiveType) - declaration = None # type: Any + declaration = None # type: DeclarationType if objectType == 'member': declaration = self._parse_type_with_init(named=True, outer='member') elif objectType == 'function': diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index c43c55d5b..0e996532f 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -1836,7 +1836,7 @@ class ASTFunctionParameter(ASTBase): # this is not part of the normal name mangling in C++ if symbol: # the anchor will be our parent - return symbol.parent.declaration.get_id(version, prefixed=None) + return symbol.parent.declaration.get_id(version, prefixed=False) # else, do the usual if self.ellipsis: return 'z' diff --git a/tests/roots/test-domain-c/function_param_target.rst b/tests/roots/test-domain-c/function_param_target.rst new file mode 100644 index 000000000..05de01445 --- /dev/null +++ b/tests/roots/test-domain-c/function_param_target.rst @@ -0,0 +1,5 @@ +.. c:function:: void f(int i) + + - :c:var:`i` + +- :c:var:`f.i` diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 7509b1aa9..43d71f74e 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -9,6 +9,8 @@ """ import pytest +from xml.etree import ElementTree + from sphinx import addnodes from sphinx.addnodes import desc from sphinx.domains.c import DefinitionParser, DefinitionError @@ -529,6 +531,25 @@ def filter_warnings(warning, file): return res +def extract_role_links(app, filename): + t = (app.outdir / filename).read_text() + lis = [l for l in t.split('\n') if l.startswith(" Date: Sun, 18 Oct 2020 06:36:11 +0800 Subject: [PATCH 14/17] Correct a typo in texinfo.py --- sphinx/writers/texinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index 5ad2831dd..a33d3363f 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -369,7 +369,7 @@ class TexinfoTranslator(SphinxTranslator): """Return an escaped string suitable for use as an argument to a Texinfo command.""" s = self.escape(s) - # commas are the argument delimeters + # commas are the argument delimiters s = s.replace(',', '@comma{}') # normalize white space s = ' '.join(s.split()).strip() From ed1f26ba47dd675c8dfb55f5679c73a95af5f04d Mon Sep 17 00:00:00 2001 From: Tetsuo Koyama Date: Thu, 22 Oct 2020 12:11:07 +0900 Subject: [PATCH 15/17] :new: OpenFAST in EXAMPLES --- EXAMPLES | 1 + 1 file changed, 1 insertion(+) diff --git a/EXAMPLES b/EXAMPLES index 19f23172f..74fa36510 100644 --- a/EXAMPLES +++ b/EXAMPLES @@ -230,6 +230,7 @@ Documentation using sphinx_rtd_theme * `MyHDL `__ * `Nextflow `__ * `NICOS `__ (customized) +* `OpenFAST `__ * `Pelican `__ * `picamera `__ * `Pillow `__ From ccc77b8305a6347f61398c35ee743561a893cbf4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 24 Oct 2020 14:14:11 +0900 Subject: [PATCH 16/17] Fix mypy violations (with mypy-0.790) --- setup.py | 2 +- sphinx/ext/autodoc/__init__.py | 2 +- sphinx/search/__init__.py | 10 +++++----- sphinx/util/typing.py | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/setup.py b/setup.py index a404f1fa5..8505d2679 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ extras_require = { 'lint': [ 'flake8>=3.5.0', 'flake8-import-order', - 'mypy>=0.780', + 'mypy>=0.790', 'docutils-stubs', ], 'test': [ diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index b61a96c84..23cdc4b28 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1725,7 +1725,7 @@ class TypeVarDocumenter(DataDocumenter): @classmethod def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any ) -> bool: - return isinstance(member, TypeVar) and isattr # type: ignore + return isinstance(member, TypeVar) and isattr def add_directive_header(self, sig: str) -> None: self.options.annotation = SUPPRESS # type: ignore diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 4534dd333..048b333d5 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -297,8 +297,8 @@ class IndexBuilder: frozen.get('envversion') != self.env.version: raise ValueError('old format') index2fn = frozen['docnames'] - self._filenames = dict(zip(index2fn, frozen['filenames'])) # type: ignore - self._titles = dict(zip(index2fn, frozen['titles'])) # type: ignore + self._filenames = dict(zip(index2fn, frozen['filenames'])) + self._titles = dict(zip(index2fn, frozen['titles'])) def load_terms(mapping: Dict[str, Any]) -> Dict[str, Set[str]]: rv = {} @@ -359,13 +359,13 @@ class IndexBuilder: def get_terms(self, fn2index: Dict) -> Tuple[Dict[str, List[str]], Dict[str, List[str]]]: rvs = {}, {} # type: Tuple[Dict[str, List[str]], Dict[str, List[str]]] for rv, mapping in zip(rvs, (self._mapping, self._title_mapping)): - for k, v in mapping.items(): # type: ignore + for k, v in mapping.items(): if len(v) == 1: fn, = v if fn in fn2index: - rv[k] = fn2index[fn] # type: ignore + rv[k] = fn2index[fn] else: - rv[k] = sorted([fn2index[fn] for fn in v if fn in fn2index]) # type: ignore # NOQA + rv[k] = sorted([fn2index[fn] for fn in v if fn in fn2index]) return rvs def freeze(self) -> Dict[str, Any]: diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index d71ca1b2d..23812db96 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -57,14 +57,14 @@ Inventory = Dict[str, Dict[str, Tuple[str, str, str, str]]] def is_system_TypeVar(typ: Any) -> bool: """Check *typ* is system defined TypeVar.""" modname = getattr(typ, '__module__', '') - return modname == 'typing' and isinstance(typ, TypeVar) # type: ignore + return modname == 'typing' and isinstance(typ, TypeVar) def stringify(annotation: Any) -> str: """Stringify type annotation object.""" if isinstance(annotation, str): return annotation - elif isinstance(annotation, TypeVar): # type: ignore + elif isinstance(annotation, TypeVar): return annotation.__name__ elif not annotation: return repr(annotation) From b62f635f425773653932e82b80167b0da32ff660 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 24 Oct 2020 16:00:00 +0900 Subject: [PATCH 17/17] Disable testing with nightly python temporarily --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index d73be03ec..47a8e7c7a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,9 +24,6 @@ jobs: env: - TOXENV=du15 - PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg" - - python: 'nightly' - env: - - TOXENV=du16 - language: node_js node_js: '10.7'