From c212f5842cdc85b22f6b3ce56a55173fe261863c Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 2 Aug 2020 14:05:09 +0200 Subject: allow registering custom get_documenter functions --- sphinx/application.py | 3 +++ sphinx/registry.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/sphinx/application.py b/sphinx/application.py index d84a2c975..493c40607 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -1045,6 +1045,9 @@ class Sphinx: logger.debug('[app] adding autodoc attrgetter: %r', (typ, getter)) self.registry.add_autodoc_attrgetter(typ, getter) + def add_autosummary_get_documenter(self, func: Callable) -> None: + self.registry.add_autosummary_get_documenter(func) + def add_search_language(self, cls: Any) -> None: """Register a new language for the HTML search index. diff --git a/sphinx/registry.py b/sphinx/registry.py index 0aec0a9fd..01203c031 100644 --- a/sphinx/registry.py +++ b/sphinx/registry.py @@ -61,6 +61,9 @@ class SphinxComponentRegistry: #: autodoc documenters; a dict of documenter name -> documenter class self.documenters = {} # type: Dict[str, Type[Documenter]] + #: autosummary documenter functions + self.custom_get_documenter_funcs = [] # type: List[Callable] + #: css_files; a list of tuple of filename and attributes self.css_files = [] # type: List[Tuple[str, Dict[str, str]]] @@ -360,6 +363,9 @@ class SphinxComponentRegistry: attrgetter: Callable[[Any, str, Any], Any]) -> None: self.autodoc_attrgettrs[typ] = attrgetter + def add_autosummary_get_documenter(self, func: Callable) -> None: + self.custom_get_documenter_funcs.append(func) + def add_css_files(self, filename: str, **attributes: str) -> None: self.css_files.append((filename, attributes)) -- cgit v1.2.1 From 2bdaa6c23ebc02d379bdd626730ce8da824f35cc Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 2 Aug 2020 14:05:36 +0200 Subject: implement the dispatching --- sphinx/ext/autosummary/__init__.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 350604387..44513f260 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -216,6 +216,26 @@ def get_documenter(app: Sphinx, obj: Any, parent: Any) -> "Type[Documenter]": return DataDocumenter +def dispatch_get_documenter(autosummary, obj, parent, real_name): + app = autosummary.env.app + documenters = [ + func(autosummary, real_name) + for func in app.custom_get_documenter_funcs + ] + sorted_documenters = sorted( + [ + documenter + for documenter in documenters + if documenter is not NotImplemented + ], + key=lambda d: d.priority, + ) + if sorted_documenters: + return sorted_documenters[-1] + else: + return get_documenter(app, obj, parent) + + # -- .. autosummary:: ---------------------------------------------------------- class Autosummary(SphinxDirective): @@ -317,7 +337,7 @@ class Autosummary(SphinxDirective): full_name = modname + '::' + full_name[len(modname) + 1:] # NB. using full_name here is important, since Documenters # handle module prefixes slightly differently - doccls = get_documenter(self.env.app, obj, parent) + doccls = dispatch_get_documenter(self, obj, parent, real_name) documenter = doccls(self.bridge, full_name) if not documenter.parse_name(): logger.warning(__('failed to parse name %s'), real_name, -- cgit v1.2.1 From 83905c61b071bc8f14c88217f74ee11b0408666b Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 2 Aug 2020 14:41:13 +0200 Subject: convert to a method --- sphinx/ext/autosummary/__init__.py | 39 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 44513f260..176daf597 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -216,26 +216,6 @@ def get_documenter(app: Sphinx, obj: Any, parent: Any) -> "Type[Documenter]": return DataDocumenter -def dispatch_get_documenter(autosummary, obj, parent, real_name): - app = autosummary.env.app - documenters = [ - func(autosummary, real_name) - for func in app.custom_get_documenter_funcs - ] - sorted_documenters = sorted( - [ - documenter - for documenter in documenters - if documenter is not NotImplemented - ], - key=lambda d: d.priority, - ) - if sorted_documenters: - return sorted_documenters[-1] - else: - return get_documenter(app, obj, parent) - - # -- .. autosummary:: ---------------------------------------------------------- class Autosummary(SphinxDirective): @@ -305,6 +285,25 @@ class Autosummary(SphinxDirective): return nodes + def dispatch_get_documenter(self, obj, parent, real_name): + app = self.env.app + documenters = [ + func(self, real_name) + for func in app.custom_get_documenter_funcs + ] + sorted_documenters = sorted( + [ + documenter + for documenter in documenters + if documenter is not NotImplemented + ], + key=lambda d: d.priority, + ) + if sorted_documenters: + return sorted_documenters[-1] + else: + return get_documenter(app, obj, parent) + def get_items(self, names: List[str]) -> List[Tuple[str, str, str, str]]: """Try to import the given names, and return a list of ``[(name, signature, summary_string, real_name), ...]``. -- cgit v1.2.1 From 95cb391bcf745725575d42a4e10ef10eaa34b93a Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 2 Aug 2020 14:48:14 +0200 Subject: actually use it as a method --- sphinx/ext/autosummary/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 96b19f689..8944927e3 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -348,7 +348,7 @@ class Autosummary(SphinxDirective): full_name = modname + '::' + full_name[len(modname) + 1:] # NB. using full_name here is important, since Documenters # handle module prefixes slightly differently - doccls = dispatch_get_documenter(self, obj, parent, real_name) + doccls = self.dispatch_get_documenter(obj, parent, real_name) documenter = doccls(self.bridge, full_name) if not documenter.parse_name(): logger.warning(__('failed to parse name %s'), real_name, -- cgit v1.2.1 From 95897349b9eb4728d6c14084fc8ac390153f6e3f Mon Sep 17 00:00:00 2001 From: Keewis Date: Sun, 2 Aug 2020 15:53:47 +0200 Subject: fix the calls of the custom documenter functions --- sphinx/ext/autosummary/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 8944927e3..22bca13bc 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -301,8 +301,8 @@ class Autosummary(SphinxDirective): def dispatch_get_documenter(self, obj, parent, real_name): app = self.env.app documenters = [ - func(self, real_name) - for func in app.custom_get_documenter_funcs + func(self, obj, parent, real_name) + for func in app.registry.custom_get_documenter_funcs ] sorted_documenters = sorted( [ -- cgit v1.2.1 From 78e4499435bee501eb3ebee8a6a347d8d2552c69 Mon Sep 17 00:00:00 2001 From: Keewis Date: Fri, 7 Aug 2020 14:10:12 +0200 Subject: revert the dispatcher changes --- sphinx/application.py | 3 --- sphinx/ext/autosummary/__init__.py | 21 +-------------------- sphinx/registry.py | 6 ------ 3 files changed, 1 insertion(+), 29 deletions(-) diff --git a/sphinx/application.py b/sphinx/application.py index 493c40607..d84a2c975 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -1045,9 +1045,6 @@ class Sphinx: logger.debug('[app] adding autodoc attrgetter: %r', (typ, getter)) self.registry.add_autodoc_attrgetter(typ, getter) - def add_autosummary_get_documenter(self, func: Callable) -> None: - self.registry.add_autosummary_get_documenter(func) - def add_search_language(self, cls: Any) -> None: """Register a new language for the HTML search index. diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 22bca13bc..855affa95 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -298,25 +298,6 @@ class Autosummary(SphinxDirective): raise exc # re-raise ImportError if instance attribute not found - def dispatch_get_documenter(self, obj, parent, real_name): - app = self.env.app - documenters = [ - func(self, obj, parent, real_name) - for func in app.registry.custom_get_documenter_funcs - ] - sorted_documenters = sorted( - [ - documenter - for documenter in documenters - if documenter is not NotImplemented - ], - key=lambda d: d.priority, - ) - if sorted_documenters: - return sorted_documenters[-1] - else: - return get_documenter(app, obj, parent) - def get_items(self, names: List[str]) -> List[Tuple[str, str, str, str]]: """Try to import the given names, and return a list of ``[(name, signature, summary_string, real_name), ...]``. @@ -348,7 +329,7 @@ class Autosummary(SphinxDirective): full_name = modname + '::' + full_name[len(modname) + 1:] # NB. using full_name here is important, since Documenters # handle module prefixes slightly differently - doccls = self.dispatch_get_documenter(obj, parent, real_name) + doccls = get_documenter(self.env.app, obj, parent) documenter = doccls(self.bridge, full_name) if not documenter.parse_name(): logger.warning(__('failed to parse name %s'), real_name, diff --git a/sphinx/registry.py b/sphinx/registry.py index 01203c031..0aec0a9fd 100644 --- a/sphinx/registry.py +++ b/sphinx/registry.py @@ -61,9 +61,6 @@ class SphinxComponentRegistry: #: autodoc documenters; a dict of documenter name -> documenter class self.documenters = {} # type: Dict[str, Type[Documenter]] - #: autosummary documenter functions - self.custom_get_documenter_funcs = [] # type: List[Callable] - #: css_files; a list of tuple of filename and attributes self.css_files = [] # type: List[Tuple[str, Dict[str, str]]] @@ -363,9 +360,6 @@ class SphinxComponentRegistry: attrgetter: Callable[[Any, str, Any], Any]) -> None: self.autodoc_attrgettrs[typ] = attrgetter - def add_autosummary_get_documenter(self, func: Callable) -> None: - self.custom_get_documenter_funcs.append(func) - def add_css_files(self, filename: str, **attributes: str) -> None: self.css_files.append((filename, attributes)) -- cgit v1.2.1 From 79a24710328e712c13dc65c5e5eb1f189c20db3d Mon Sep 17 00:00:00 2001 From: Keewis Date: Fri, 7 Aug 2020 14:11:32 +0200 Subject: add a create_documenter method wrapping get_documenter --- sphinx/ext/autosummary/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 855affa95..08accc3bb 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -298,6 +298,15 @@ class Autosummary(SphinxDirective): raise exc # re-raise ImportError if instance attribute not found + def create_documenter(self, app: Sphinx, obj: Any, + parent: Any, name: str) -> "Type[Documenter]": + """Get an autodoc.Documenter class suitable for documenting the given + object. + + Wraps get_documenter and is meant as a hook for extensions. + """ + return get_documenter(app, obj, parent) + def get_items(self, names: List[str]) -> List[Tuple[str, str, str, str]]: """Try to import the given names, and return a list of ``[(name, signature, summary_string, real_name), ...]``. @@ -329,7 +338,7 @@ class Autosummary(SphinxDirective): full_name = modname + '::' + full_name[len(modname) + 1:] # NB. using full_name here is important, since Documenters # handle module prefixes slightly differently - doccls = get_documenter(self.env.app, obj, parent) + doccls = self.create_documenter(self.env.app, obj, parent, real_name) documenter = doccls(self.bridge, full_name) if not documenter.parse_name(): logger.warning(__('failed to parse name %s'), real_name, -- cgit v1.2.1 From 9d60f3456f74cb98f2a3d442a2be13ad7099a4d4 Mon Sep 17 00:00:00 2001 From: Keewis Date: Fri, 7 Aug 2020 15:32:11 +0200 Subject: return a documenter instance instead of just the class --- sphinx/ext/autosummary/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 08accc3bb..d2fe4af68 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -299,13 +299,14 @@ class Autosummary(SphinxDirective): raise exc # re-raise ImportError if instance attribute not found def create_documenter(self, app: Sphinx, obj: Any, - parent: Any, name: str) -> "Type[Documenter]": + parent: Any, full_name: str) -> "Type[Documenter]": """Get an autodoc.Documenter class suitable for documenting the given object. Wraps get_documenter and is meant as a hook for extensions. """ - return get_documenter(app, obj, parent) + doccls = get_documenter(app, obj, parent) + return doccls(self.bridge, full_name) def get_items(self, names: List[str]) -> List[Tuple[str, str, str, str]]: """Try to import the given names, and return a list of @@ -338,8 +339,7 @@ class Autosummary(SphinxDirective): full_name = modname + '::' + full_name[len(modname) + 1:] # NB. using full_name here is important, since Documenters # handle module prefixes slightly differently - doccls = self.create_documenter(self.env.app, obj, parent, real_name) - documenter = doccls(self.bridge, full_name) + documenter = self.create_documenter(self.env.app, obj, parent, full_name) if not documenter.parse_name(): logger.warning(__('failed to parse name %s'), real_name, location=self.get_source_info()) -- cgit v1.2.1 From b4dec34ecfa36628452097911b182d0df6265e68 Mon Sep 17 00:00:00 2001 From: Keewis Date: Fri, 7 Aug 2020 15:41:17 +0200 Subject: update the type hint --- sphinx/ext/autosummary/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index d2fe4af68..739636d7e 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -299,7 +299,7 @@ class Autosummary(SphinxDirective): raise exc # re-raise ImportError if instance attribute not found def create_documenter(self, app: Sphinx, obj: Any, - parent: Any, full_name: str) -> "Type[Documenter]": + parent: Any, full_name: str) -> "Documenter": """Get an autodoc.Documenter class suitable for documenting the given object. -- cgit v1.2.1 From a4996f905471c37303748f543ded7e047b9fa7a4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 6 Aug 2020 02:07:11 +0900 Subject: Fix #7780: napoleon: multiple params declaration was wrongly recognized So far, napoleon converts multiple paramaters declrarations to single paramaeter fields (`:param:` and `:type:`) unexpectedly. As a result, the output seems broken. This converts it to multiple parameter fields (a pair of field for each parameter declration) to build parameter successfully. Note: The conversion rule is available only when napoleon_use_params=True. --- CHANGES | 2 ++ sphinx/ext/napoleon/docstring.py | 13 +++++++++---- tests/test_ext_napoleon_docstring.py | 26 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 235e34852..5d50da739 100644 --- a/CHANGES +++ b/CHANGES @@ -80,6 +80,8 @@ Bugs fixed module has submodules * #4258: napoleon: decorated special methods are not shown * #7799: napoleon: parameters are not escaped for combined params in numpydoc +* #7780: napoleon: multiple paramaters declaration in numpydoc was wrongly + recognized when napoleon_use_params=True * #7715: LaTeX: ``numfig_secnum_depth > 1`` leads to wrong figure links * #7846: html theme: XML-invalid files were generated * #7894: gettext: Wrong source info is shown when using rst_epilog diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index b3daa06f1..52abf9753 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -266,13 +266,16 @@ class GoogleDocstring: _descs = self.__class__(_descs, self._config).lines() return _name, _type, _descs - def _consume_fields(self, parse_type: bool = True, prefer_type: bool = False - ) -> List[Tuple[str, str, List[str]]]: + def _consume_fields(self, parse_type: bool = True, prefer_type: bool = False, + multiple: bool = False) -> List[Tuple[str, str, List[str]]]: self._consume_empty() fields = [] while not self._is_section_break(): _name, _type, _desc = self._consume_field(parse_type, prefer_type) - if _name or _type or _desc: + if multiple and _name: + for name in _name.split(","): + fields.append((name.strip(), _type, _desc)) + elif _name or _type or _desc: fields.append((_name, _type, _desc,)) return fields @@ -681,10 +684,12 @@ class GoogleDocstring: return self._format_fields(_('Other Parameters'), self._consume_fields()) def _parse_parameters_section(self, section: str) -> List[str]: - fields = self._consume_fields() if self._config.napoleon_use_param: + # Allow to declare multiple parameters at once (ex: x, y: int) + fields = self._consume_fields(multiple=True) return self._format_docutils_params(fields) else: + fields = self._consume_fields() return self._format_fields(_('Parameters'), fields) def _parse_raises_section(self, section: str) -> List[str]: diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py index 2f1e559b3..7eb908058 100644 --- a/tests/test_ext_napoleon_docstring.py +++ b/tests/test_ext_napoleon_docstring.py @@ -1337,6 +1337,32 @@ param1 : :class:`MyClass ` instance expected = """\ :param param1: :type param1: :class:`MyClass ` instance +""" + self.assertEqual(expected, actual) + + def test_multiple_parameters(self): + docstring = """\ +Parameters +---------- +x1, x2 : array_like + Input arrays, description of ``x1``, ``x2``. + +""" + + config = Config(napoleon_use_param=False) + actual = str(NumpyDocstring(docstring, config)) + expected = """\ +:Parameters: **x1, x2** (:class:`array_like`) -- Input arrays, description of ``x1``, ``x2``. +""" + self.assertEqual(expected, actual) + + config = Config(napoleon_use_param=True) + actual = str(NumpyDocstring(dedent(docstring), config)) + expected = """\ +:param x1: Input arrays, description of ``x1``, ``x2``. +:type x1: :class:`array_like` +:param x2: Input arrays, description of ``x1``, ``x2``. +:type x2: :class:`array_like` """ self.assertEqual(expected, actual) -- cgit v1.2.1 From 88b2ec6c34fb405cf468475e6f2a7754e2c705eb Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Aug 2020 01:41:24 +0900 Subject: Fix #8041: autodoc: An ivar on super class is not shown unexpectedly An annotated instance variable on super class is not documented when derived class has also other annotated instance variables because `obj.__annotations__` is overrided by derived class's type hints. To get annotations of the target class correctly, this scans MRO to get all of annotated instance variables. --- CHANGES | 2 + sphinx/ext/autodoc/__init__.py | 17 ++++++-- sphinx/ext/autodoc/importer.py | 30 ++++++++++++-- tests/roots/test-ext-autodoc/target/typed_vars.py | 2 +- tests/test_ext_autodoc.py | 48 ++++++++++++++++++++--- 5 files changed, 84 insertions(+), 15 deletions(-) diff --git a/CHANGES b/CHANGES index 5d50da739..aa27f5a6c 100644 --- a/CHANGES +++ b/CHANGES @@ -71,6 +71,8 @@ Bugs fixed when ``:inherited-members:`` option given * #8032: autodoc: A type hint for the instance variable defined at parent class is not shown in the document of the derived class +* #8041: autodoc: An annotated instance variable on super class is not + documented when derived class has other annotated instance variables * #7839: autosummary: cannot handle umlauts in function names * #7865: autosummary: Failed to extract summary line when abbreviations found * #7866: autosummary: Failed to extract correct summary line when docstring diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 832ee4a92..0a64b56e2 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -18,6 +18,7 @@ from types import ModuleType from typing import ( Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union ) +from typing import get_type_hints from docutils.statemachine import StringList @@ -1605,8 +1606,12 @@ class DataDocumenter(ModuleLevelDocumenter): sourcename = self.get_sourcename() if not self.options.annotation: # obtain annotation for this data - annotations = getattr(self.parent, '__annotations__', {}) - if annotations and self.objpath[-1] in annotations: + try: + annotations = get_type_hints(self.parent) + except TypeError: + annotations = {} + + if self.objpath[-1] in annotations: objrepr = stringify_typehint(annotations.get(self.objpath[-1])) self.add_line(' :type: ' + objrepr, sourcename) else: @@ -1971,8 +1976,12 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): sourcename = self.get_sourcename() if not self.options.annotation: # obtain type annotation for this attribute - annotations = getattr(self.parent, '__annotations__', {}) - if annotations and self.objpath[-1] in annotations: + try: + annotations = get_type_hints(self.parent) + except TypeError: + annotations = {} + + if self.objpath[-1] in annotations: objrepr = stringify_typehint(annotations.get(self.objpath[-1])) self.add_line(' :type: ' + objrepr, sourcename) else: diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py index 031911de2..133ce1439 100644 --- a/sphinx/ext/autodoc/importer.py +++ b/sphinx/ext/autodoc/importer.py @@ -18,6 +18,10 @@ from sphinx.pycode import ModuleAnalyzer from sphinx.util import logging from sphinx.util.inspect import isclass, isenumclass, safe_getattr +if False: + # For type annotation + from typing import Type # NOQA + logger = logging.getLogger(__name__) @@ -158,6 +162,24 @@ Attribute = NamedTuple('Attribute', [('name', str), ('value', Any)]) +def _getmro(obj: Any) -> Tuple["Type", ...]: + """Get __mro__ from given *obj* safely.""" + __mro__ = safe_getattr(obj, '__mro__', None) + if isinstance(__mro__, tuple): + return __mro__ + else: + return tuple() + + +def _getannotations(obj: Any) -> Mapping[str, Any]: + """Get __annotations__ from given *obj* safely.""" + __annotations__ = safe_getattr(obj, '__annotations__', None) + if isinstance(__annotations__, Mapping): + return __annotations__ + else: + return {} + + def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable, analyzer: ModuleAnalyzer = None) -> Dict[str, Attribute]: """Get members and attributes of target object.""" @@ -199,11 +221,11 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable, continue # annotation only member (ex. attr: int) - if hasattr(subject, '__annotations__') and isinstance(subject.__annotations__, Mapping): - for name in subject.__annotations__: - name = unmangle(subject, name) + for i, cls in enumerate(_getmro(subject)): + for name in _getannotations(cls): + name = unmangle(cls, name) if name and name not in members: - members[name] = Attribute(name, True, INSTANCEATTR) + members[name] = Attribute(name, i == 0, INSTANCEATTR) if analyzer: # append instance attributes (cf. self.attr1) if analyzer knows diff --git a/tests/roots/test-ext-autodoc/target/typed_vars.py b/tests/roots/test-ext-autodoc/target/typed_vars.py index 43c58deb0..ba9657f18 100644 --- a/tests/roots/test-ext-autodoc/target/typed_vars.py +++ b/tests/roots/test-ext-autodoc/target/typed_vars.py @@ -28,4 +28,4 @@ class Class: class Derived(Class): - pass + attr7: int diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index d6f973f49..15e1f3539 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -1580,12 +1580,7 @@ def test_autodoc_typed_instance_variables(app): ' :module: target.typed_vars', '', '', - ' .. py:attribute:: Derived.attr2', - ' :module: target.typed_vars', - ' :type: int', - '', - '', - ' .. py:attribute:: Derived.descr4', + ' .. py:attribute:: Derived.attr7', ' :module: target.typed_vars', ' :type: int', '', @@ -1615,6 +1610,47 @@ def test_autodoc_typed_instance_variables(app): ] +@pytest.mark.skipif(sys.version_info < (3, 6), reason='py36+ is available since python3.6.') +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_typed_inherited_instance_variables(app): + options = {"members": None, + "undoc-members": True, + "inherited-members": True} + actual = do_autodoc(app, 'class', 'target.typed_vars.Derived', options) + assert list(actual) == [ + '', + '.. py:class:: Derived()', + ' :module: target.typed_vars', + '', + '', + ' .. py:attribute:: Derived.attr1', + ' :module: target.typed_vars', + ' :type: int', + ' :value: 0', + '', + '', + ' .. py:attribute:: Derived.attr2', + ' :module: target.typed_vars', + ' :type: int', + '', + '', + ' .. py:attribute:: Derived.attr3', + ' :module: target.typed_vars', + ' :value: 0', + '', + '', + ' .. py:attribute:: Derived.attr7', + ' :module: target.typed_vars', + ' :type: int', + '', + '', + ' .. py:attribute:: Derived.descr4', + ' :module: target.typed_vars', + ' :type: int', + '', + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_GenericAlias(app): options = {"members": None, -- cgit v1.2.1 From 1bf7fe424e16625f2ab4ad5824ca4aab6cf0ae65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Thu, 6 Aug 2020 09:18:36 +0200 Subject: i18n: Add support for having single text domain The gettext_compact can now be a string which is then a single domain for all documents. Fixes #784 --- doc/usage/configuration.rst | 6 ++++++ sphinx/builders/gettext.py | 2 +- sphinx/util/i18n.py | 6 ++++-- tests/test_build_gettext.py | 18 ++++++++++++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index add78326b..3fc3a5306 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -756,9 +756,15 @@ documentation on :ref:`intl` for details. If true, a document's text domain is its docname if it is a top-level project file and its very base directory otherwise. + If set to string, all document's text domain is this string, making all + documents use single text domain. + By default, the document ``markup/code.rst`` ends up in the ``markup`` text domain. With this option set to ``False``, it is ``markup/code``. + .. versionchanged:: 3.3 + The string value is now accepted. + .. confval:: gettext_uuid If true, Sphinx generates uuid information for version tracking in message diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index 8e30762e9..f8a19c57a 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -316,7 +316,7 @@ class MessageCatalogBuilder(I18nBuilder): def setup(app: Sphinx) -> Dict[str, Any]: app.add_builder(MessageCatalogBuilder) - app.add_config_value('gettext_compact', True, 'gettext') + app.add_config_value('gettext_compact', True, 'gettext', Any) app.add_config_value('gettext_location', True, 'gettext') app.add_config_value('gettext_uuid', False, 'gettext') app.add_config_value('gettext_auto_build', True, 'env') diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py index 2177fc7b7..41407f4e1 100644 --- a/sphinx/util/i18n.py +++ b/sphinx/util/i18n.py @@ -14,7 +14,7 @@ import warnings from collections import namedtuple from datetime import datetime, timezone from os import path -from typing import Callable, Generator, List, Set, Tuple +from typing import Callable, Generator, List, Set, Tuple, Union import babel.dates from babel.messages.mofile import write_mo @@ -128,8 +128,10 @@ def find_catalog(docname: str, compaction: bool) -> str: return ret -def docname_to_domain(docname: str, compation: bool) -> str: +def docname_to_domain(docname: str, compation: Union[bool, str]) -> str: """Convert docname to domain for catalogs.""" + if isinstance(compation, str): + return compation if compation: return docname.split(SEP, 1)[0] else: diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 1c86b8daa..074602ea9 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -174,3 +174,21 @@ def test_gettext_template_msgid_order_in_sphinxpot(app): 'msgid "This is Template 2\\.".*'), result, flags=re.S) + + +@pytest.mark.sphinx( + 'gettext', srcdir='root-gettext', + confoverrides={'gettext_compact': 'documentation'}) +def test_build_single_pot(app): + app.builder.build_all() + + assert (app.outdir / 'documentation.pot').isfile() + + result = (app.outdir / 'documentation.pot').read_text() + assert re.search( + ('msgid "Todo".*' + 'msgid "Like footnotes.".*' + 'msgid "The minute.".*' + 'msgid "Generated section".*'), + result, + flags=re.S) -- cgit v1.2.1 From 2a59fbcdbeb9a623173c465b0f46335df86a05fb Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 7 Aug 2020 15:38:28 -0600 Subject: Make the toc.circular suppress_warnings flag apply to self referenced toctrees Fixes https://github.com/sphinx-doc/sphinx/issues/7410#issuecomment-670678193. --- sphinx/environment/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index cf3364494..8ce9fa365 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -594,7 +594,9 @@ class BuildEnvironment: def traverse_toctree(parent: str, docname: str) -> Iterator[Tuple[str, str]]: if parent == docname: - logger.warning(__('self referenced toctree found. Ignored.'), location=docname) + logger.warning(__('self referenced toctree found. Ignored.'), + location=docname, type='toc', + subtype='circular') return # traverse toctree by pre-order -- cgit v1.2.1 From 5fd89964c5b46f5dbaaa9b41427fdc89357fca6a Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 8 Aug 2020 17:15:09 +0900 Subject: Update CHANGES for PR #8071 --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index aa27f5a6c..ad1659496 100644 --- a/CHANGES +++ b/CHANGES @@ -56,6 +56,7 @@ Features added :confval:`c_warn_on_allowed_pre_v3`` to ``True``. The functionality is immediately deprecated. * #7999: C, add support for named variadic macro arguments. +* #8071: Allow to suppress "self referenced toctrees" warning Bugs fixed ---------- -- cgit v1.2.1 From b4efb8cd0e8943b232fe9cfbefe161efbffeb560 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 8 Aug 2020 18:09:04 +0900 Subject: Merge CHANGES of 3.1.3 (unreleased) to 3.2.0 --- CHANGES | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/CHANGES b/CHANGES index ad1659496..2b203b9b1 100644 --- a/CHANGES +++ b/CHANGES @@ -111,34 +111,9 @@ Bugs fixed * #7986: CSS: make "highlight" selector more robust * #7944: C++, parse non-type template parameters starting with a dependent qualified name. - -Testing --------- - -Release 3.1.3 (in development) -============================== - -Dependencies ------------- - -Incompatible changes --------------------- - -Deprecated ----------- - -Features added --------------- - -Bugs fixed ----------- - * C, don't deepcopy the entire symbol table and make a mess every time an enumerator is handled. -Testing --------- - Release 3.1.2 (released Jul 05, 2020) ===================================== -- cgit v1.2.1 From e91f8a7d05d43c9e3dc68f48ef4065aaa495df2c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 8 Aug 2020 18:09:52 +0900 Subject: Bump to 3.2.0 final --- CHANGES | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 2b203b9b1..4f029b0f7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,11 +1,5 @@ -Release 3.2.0 (in development) -============================== - -Dependencies ------------- - -Incompatible changes --------------------- +Release 3.2.0 (released Aug 08, 2020) +===================================== Deprecated ---------- -- cgit v1.2.1 From 0ff74155dfcd755ea9ef0f2b636d7b69029935d4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 8 Aug 2020 18:13:03 +0900 Subject: Bump version --- CHANGES | 21 +++++++++++++++++++++ sphinx/__init__.py | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 4f029b0f7..bb77ee140 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,24 @@ +Release 3.2.1 (in development) +============================== + +Dependencies +------------ + +Incompatible changes +-------------------- + +Deprecated +---------- + +Features added +-------------- + +Bugs fixed +---------- + +Testing +-------- + Release 3.2.0 (released Aug 08, 2020) ===================================== diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 52bbdcc65..1f1c80228 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -32,8 +32,8 @@ if 'PYTHONWARNINGS' not in os.environ: warnings.filterwarnings('ignore', "'U' mode is deprecated", DeprecationWarning, module='docutils.io') -__version__ = '3.2.0' -__released__ = '3.2.0' # used when Sphinx builds its own docs +__version__ = '3.2.1+' +__released__ = '3.2.1' # used when Sphinx builds its own docs #: Version info for better programmatic use. #: @@ -43,7 +43,7 @@ __released__ = '3.2.0' # used when Sphinx builds its own docs #: #: .. versionadded:: 1.2 #: Before version 1.2, check the string ``sphinx.__version__``. -version_info = (3, 2, 0, 'final', 0) +version_info = (3, 2, 1, 'beta', 0) package_dir = path.abspath(path.dirname(__file__)) -- cgit v1.2.1 From 40bdeb2c1603abc1d7b07b3e617904f8c3fbb1fe Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 8 Aug 2020 18:13:51 +0900 Subject: Bump version --- CHANGES | 21 +++++++++++++++++++++ sphinx/__init__.py | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index bb77ee140..5f645efa8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,24 @@ +Release 3.3.0 (in development) +============================== + +Dependencies +------------ + +Incompatible changes +-------------------- + +Deprecated +---------- + +Features added +-------------- + +Bugs fixed +---------- + +Testing +-------- + Release 3.2.1 (in development) ============================== diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 1f1c80228..fde2345a8 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -32,8 +32,8 @@ if 'PYTHONWARNINGS' not in os.environ: warnings.filterwarnings('ignore', "'U' mode is deprecated", DeprecationWarning, module='docutils.io') -__version__ = '3.2.1+' -__released__ = '3.2.1' # used when Sphinx builds its own docs +__version__ = '3.3.0+' +__released__ = '3.3.0' # used when Sphinx builds its own docs #: Version info for better programmatic use. #: @@ -43,7 +43,7 @@ __released__ = '3.2.1' # used when Sphinx builds its own docs #: #: .. versionadded:: 1.2 #: Before version 1.2, check the string ``sphinx.__version__``. -version_info = (3, 2, 1, 'beta', 0) +version_info = (3, 3, 0, 'beta', 0) package_dir = path.abspath(path.dirname(__file__)) -- cgit v1.2.1 From 51332c7b08f17a471706aa3a2b5a55d34c28a31d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 9 Aug 2020 00:44:04 +0900 Subject: Fix #8074: napoleon: Crashes during processing C-ext module inspect.getfile() raises TypeError if given object is a C-extension. This handles the exception not to be crashed. --- CHANGES | 2 ++ sphinx/ext/napoleon/docstring.py | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 5f645efa8..092673746 100644 --- a/CHANGES +++ b/CHANGES @@ -37,6 +37,8 @@ Features added Bugs fixed ---------- +* #8074: napoleon: Crashes during processing C-ext module + Testing -------- diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 52abf9753..29799cb06 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -1074,7 +1074,10 @@ class NumpyDocstring(GoogleDocstring): super().__init__(docstring, config, app, what, name, obj, options) def _get_location(self) -> str: - filepath = inspect.getfile(self._obj) if self._obj is not None else None + try: + filepath = inspect.getfile(self._obj) if self._obj is not None else None + except TypeError: + filepath = None name = self._name if filepath is None and name is None: -- cgit v1.2.1 From a4487f1762e722b1d7639ec13c531a7c2f8ce889 Mon Sep 17 00:00:00 2001 From: Yves Chevallier Date: Sun, 9 Aug 2020 11:44:41 +0200 Subject: Check if LaTeX package already added --- sphinx/registry.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sphinx/registry.py b/sphinx/registry.py index 0aec0a9fd..4d1e9eb72 100644 --- a/sphinx/registry.py +++ b/sphinx/registry.py @@ -367,7 +367,14 @@ class SphinxComponentRegistry: logger.debug('[app] adding js_file: %r, %r', filename, attributes) self.js_files.append((filename, attributes)) + def has_latex_package(self, name: str) -> bool: + packages = self.latex_packages + self.latex_packages_after_hyperref + return bool([x for x in packages if x[0] == name]) + def add_latex_package(self, name: str, options: str, after_hyperref: bool = False) -> None: + if self.has_latex_package(name): + logger.warn("latex package '%s' already included" % name) + logger.debug('[app] adding latex package: %r', name) if after_hyperref: self.latex_packages_after_hyperref.append((name, options)) -- cgit v1.2.1 From 5f82825e273dfc5df3c4ef499876def8b024fa81 Mon Sep 17 00:00:00 2001 From: Yves Chevallier Date: Sun, 9 Aug 2020 13:33:42 +0200 Subject: Customize jinja comment-tag --- sphinx/util/template.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sphinx/util/template.py b/sphinx/util/template.py index 18047d687..bb078a2a2 100644 --- a/sphinx/util/template.py +++ b/sphinx/util/template.py @@ -84,6 +84,8 @@ class LaTeXRenderer(SphinxRenderer): self.env.variable_end_string = '%>' self.env.block_start_string = '<%' self.env.block_end_string = '%>' + self.env.comment_start_string = '<#' + self.env.comment_end_string = '<#' class ReSTRenderer(SphinxRenderer): -- cgit v1.2.1 From f7431b927cab199682a44b11849f347f692f6eca Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 9 Aug 2020 23:40:29 +0900 Subject: Fix #8084: autodoc: KeyError is raised on documenting a broken attribute ``typing.get_type_hints()`` raises KeyError when a class having invalid __module__ was given. This handles the exception not to crash on build documents. --- CHANGES | 3 +++ sphinx/ext/autodoc/__init__.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGES b/CHANGES index 5f645efa8..3724c65ca 100644 --- a/CHANGES +++ b/CHANGES @@ -37,6 +37,9 @@ Features added Bugs fixed ---------- +* #8084: autodoc: KeyError is raised on documenting an attribute of the broken + class + Testing -------- diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 0a64b56e2..47f6bcb25 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1610,6 +1610,9 @@ class DataDocumenter(ModuleLevelDocumenter): annotations = get_type_hints(self.parent) except TypeError: annotations = {} + except KeyError: + # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084) + annotations = {} if self.objpath[-1] in annotations: objrepr = stringify_typehint(annotations.get(self.objpath[-1])) @@ -1980,6 +1983,9 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): annotations = get_type_hints(self.parent) except TypeError: annotations = {} + except KeyError: + # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084) + annotations = {} if self.objpath[-1] in annotations: objrepr = stringify_typehint(annotations.get(self.objpath[-1])) -- cgit v1.2.1 From 6cf1301c9e479bc886a237ece76d0b8ae8675a41 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 10 Aug 2020 13:18:45 +0900 Subject: Remove pylint task At present, pylint has no longer been used in this project. This removes the tasks and files for pylint from repository. --- Makefile | 4 - tox.ini | 10 -- utils/pylintrc | 301 --------------------------------------------------------- 3 files changed, 315 deletions(-) delete mode 100644 utils/pylintrc diff --git a/Makefile b/Makefile index d37d8546e..9cff012d4 100644 --- a/Makefile +++ b/Makefile @@ -64,10 +64,6 @@ type-check: doclinter: python utils/doclinter.py CHANGES *.rst doc/ -.PHONY: pylint -pylint: - @pylint --rcfile utils/pylintrc sphinx - .PHONY: test test: @$(PYTHON) -m pytest -v $(TEST) diff --git a/tox.ini b/tox.ini index bddd822a6..547fe7c40 100644 --- a/tox.ini +++ b/tox.ini @@ -40,16 +40,6 @@ extras = commands = flake8 {posargs} -[testenv:pylint] -basepython = python3 -description = - Run source code analyzer. -deps = - pylint - {[testenv]deps} -commands = - pylint --rcfile utils/pylintrc sphinx - [testenv:coverage] basepython = python3 description = diff --git a/utils/pylintrc b/utils/pylintrc deleted file mode 100644 index c2b338b79..000000000 --- a/utils/pylintrc +++ /dev/null @@ -1,301 +0,0 @@ -# lint Python modules using external checkers. -# -# This is the main checker controlling the other ones and the reports -# generation. It is itself both a raw checker and an astng checker in order -# to: -# * handle message activation / deactivation at the module level -# * handle some basic but necessary stats'data (number of classes, methods...) -# -[MASTER] - -# Specify a configuration file. -#rcfile= - -# Profiled execution. -profile=no - -# Add to the black list. It should be a base name, not a -# path. You may set this option multiple times. -ignore=.svn - -# Pickle collected data for later comparisons. -persistent=yes - -# Set the cache size for astng objects. -cache-size=500 - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - - -[MESSAGES CONTROL] - -# Enable only checker(s) with the given id(s). This option conflict with the -# disable-checker option -#enable-checker= - -# Enable all checker(s) except those with the given id(s). This option conflict -# with the disable-checker option -#disable-checker= - -# Enable all messages in the listed categories. -#enable-msg-cat= - -# Disable all messages in the listed categories. -#disable-msg-cat= - -# Enable the message(s) with the given id(s). -#enable-msg= - -# Disable the message(s) with the given id(s). -disable-msg=C0323,W0142,C0301,C0103,C0111,E0213,C0302,C0203,W0703,R0201,W0613,W0612,W0622 - - -[REPORTS] - -# set the output format. Available formats are text, parseable, colorized and -# html -output-format=colorized - -# Include message's id in output -include-ids=yes - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - -# Tells wether to display a full report or only the messages -reports=yes - -# Python expression which should return a note less than 10 (10 is the highest -# note).You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (R0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Add a comment according to your evaluation note. This is used by the global -# evaluation report (R0004). -comment=no - -# Enable the report(s) with the given id(s). -#enable-report= - -# Disable the report(s) with the given id(s). -#disable-report= - - -# checks for -# * unused variables / imports -# * undefined variables -# * redefinition of variable from builtins or from an outer scope -# * use of variable before assigment -# -[VARIABLES] - -# Tells wether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching names used for dummy variables (i.e. not used). -dummy-variables-rgx=_|dummy - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - - -# try to find bugs in the code using type inference -# -[TYPECHECK] - -# Tells wether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# When zope mode is activated, consider the acquired-members option to ignore -# access to some undefined attributes. -zope=no - -# List of members which are usually get through zope's acquisition mecanism and -# so shouldn't trigger E0201 when accessed (need zope=yes to be considered). -acquired-members=REQUEST,acl_users,aq_parent - - -# checks for : -# * doc strings -# * modules / classes / functions / methods / arguments / variables name -# * number of arguments, local variables, branchs, returns and statements in -# functions, methods -# * required module attributes -# * dangerous default values as arguments -# * redefinition of function / method / class -# * uses of the global statement -# -[BASIC] - -# Required attributes for module, separated by a comma -required-attributes= - -# Regular expression which should only match functions or classes name which do -# not require a docstring -no-docstring-rgx=__.*__ - -# Regular expression which should only match correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression which should only match correct module level names -const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$ - -# Regular expression which should only match correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression which should only match correct function names -function-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct method names -method-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct instance attribute names -attr-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct variable names -variable-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct list comprehension / -# generator expression variable names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Good variable names which should always be accepted, separated by a comma -good-names=i,j,k,ex,Run,_ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# List of builtins function names that should not be used, separated by a comma -bad-functions=apply,input - - -# checks for sign of poor/misdesign: -# * number of methods, attributes, local variables... -# * size, complexity of functions, methods -# -[DESIGN] - -# Maximum number of arguments for function / method -max-args=12 - -# Maximum number of locals for function / method body -max-locals=30 - -# Maximum number of return / yield for function / method body -max-returns=12 - -# Maximum number of branch for function / method body -max-branchs=30 - -# Maximum number of statements in function / method body -max-statements=60 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of attributes for a class (see R0902). -max-attributes=20 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=0 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - - -# checks for -# * external modules dependencies -# * relative / wildcard imports -# * cyclic imports -# * uses of deprecated modules -# -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,string,TERMIOS,Bastion,rexec - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report R0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report R0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report R0402 must -# not be disabled) -int-import-graph= - - -# checks for : -# * methods without self as first argument -# * overridden methods signature -# * access only to existant members via self -# * attributes not defined in the __init__ method -# * supported interfaces implementation -# * unreachable code -# -[CLASSES] - -# List of interface methods to ignore, separated by a comma. This is used for -# instance to not check methods defines in Zope's Interface base class. -ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - - -# checks for similarities and duplicated code. This computation may be -# memory / CPU intensive, so you should disable it if you experiments some -# problems. -# -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=10 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - - -# checks for: -# * warning notes in the code -# * PEP 263: source code with non ascii character but no encoding declaration -# -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO - - -# checks for : -# * unauthorized constructions -# * strict indentation -# * line length -# * use of <> instead of != -# -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=90 - -# Maximum number of lines in a module -max-module-lines=1000 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' -- cgit v1.2.1 From bb09f9215449fcd1524a8c2d3b5cc56009f98a9a Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 10 Aug 2020 16:09:47 +0900 Subject: Fix #8091: autodoc: AttributeError is raised on documenting an attribute Until Python 3.5.2, typing.get_type_hints() raises AttributeError if given object does not have `__code__` attribute. This handles the exception not to crash building documents. Note: The AttributeError was fixed at 3.5.3 refs: https://github.com/python/cpython/commit/991d14fee1805e17647940a2a8cbf4f62f0f09ea --- CHANGES | 2 ++ sphinx/ext/autodoc/__init__.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGES b/CHANGES index d0e4e6316..1d523f3fd 100644 --- a/CHANGES +++ b/CHANGES @@ -40,6 +40,8 @@ Bugs fixed * #8074: napoleon: Crashes during processing C-ext module * #8084: autodoc: KeyError is raised on documenting an attribute of the broken class +* #8091: autodoc: AttributeError is raised on documenting an attribute on Python + 3.5.2 Testing -------- diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 47f6bcb25..f3820f715 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1613,6 +1613,9 @@ class DataDocumenter(ModuleLevelDocumenter): except KeyError: # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084) annotations = {} + except AttributeError: + # AttributeError is raised on 3.5.2 (fixed by 3.5.3) + annotations = {} if self.objpath[-1] in annotations: objrepr = stringify_typehint(annotations.get(self.objpath[-1])) @@ -1986,6 +1989,9 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): except KeyError: # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084) annotations = {} + except AttributeError: + # AttributeError is raised on 3.5.2 (fixed by 3.5.3) + annotations = {} if self.objpath[-1] in annotations: objrepr = stringify_typehint(annotations.get(self.objpath[-1])) -- cgit v1.2.1 From 2d180e49c268fa5f9574a1f594e7e253e4c196b7 Mon Sep 17 00:00:00 2001 From: Keewis Date: Mon, 10 Aug 2020 13:03:50 +0200 Subject: add a setting to disable the type preprocessor --- sphinx/ext/napoleon/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py index 6cab63c9f..793384dd2 100644 --- a/sphinx/ext/napoleon/__init__.py +++ b/sphinx/ext/napoleon/__init__.py @@ -41,6 +41,7 @@ class Config: napoleon_use_param = True napoleon_use_rtype = True napoleon_use_keyword = True + napoleon_preprocess_types = True napoleon_type_aliases = None napoleon_custom_sections = None @@ -237,9 +238,12 @@ class Config: :returns: *bool* -- True if successful, False otherwise + napoleon_preprocess_types : :obj:`bool` (Defaults to True) + Enable the type preprocessor for numpy style docstrings. + napoleon_type_aliases : :obj:`dict` (Defaults to None) Add a mapping of strings to string, translating types in numpy - style docstrings. + style docstrings. Only works if ``napoleon_preprocess_types = True``. napoleon_custom_sections : :obj:`list` (Defaults to None) Add a list of custom sections to include, expanding the list of parsed sections. @@ -268,6 +272,7 @@ class Config: 'napoleon_use_param': (True, 'env'), 'napoleon_use_rtype': (True, 'env'), 'napoleon_use_keyword': (True, 'env'), + 'napoleon_preprocess_types': (True, 'env'), 'napoleon_type_aliases': (None, 'env'), 'napoleon_custom_sections': (None, 'env') } -- cgit v1.2.1 From 27c252ccbab0a266ed9588355be9af261105b29e Mon Sep 17 00:00:00 2001 From: Keewis Date: Mon, 10 Aug 2020 13:04:21 +0200 Subject: only preprocess if the preprocessor is enabled --- sphinx/ext/napoleon/docstring.py | 11 ++++++----- tests/test_ext_napoleon_docstring.py | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 29799cb06..a629f147f 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -1104,11 +1104,12 @@ class NumpyDocstring(GoogleDocstring): _name, _type = line, '' _name, _type = _name.strip(), _type.strip() _name = self._escape_args_and_kwargs(_name) - _type = _convert_numpy_type_spec( - _type, - location=self._get_location(), - translations=self._config.napoleon_type_aliases or {}, - ) + if self._config.napoleon_enable_type_preprocessor: + _type = _convert_numpy_type_spec( + _type, + location=self._get_location(), + translations=self._config.napoleon_type_aliases or {}, + ) if prefer_type and not _type: _type, _name = _name, _type diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py index 7eb908058..06a4a0c1a 100644 --- a/tests/test_ext_napoleon_docstring.py +++ b/tests/test_ext_napoleon_docstring.py @@ -1317,6 +1317,27 @@ class NumpyDocstringTest(BaseDocstringTest): expected = dedent(expected) self.assertEqual(expected, actual) + def test_type_preprocessor(self): + docstring = dedent(""" + Single line summary + + Parameters + ---------- + arg1:str + Extended + description of arg1 + """) + + config = Config(napoleon_preprocess_types=False, napoleon_use_param=False) + actual = str(NumpyDocstring(docstring, config)) + expected = dedent(""" + Single line summary + + :Parameters: **arg1** (*str*) -- Extended + description of arg1 + """) + self.assertEqual(expected, actual) + def test_parameters_with_class_reference(self): docstring = """\ Parameters -- cgit v1.2.1 From be65bded764597476474bdeb7862707ae965ee5b Mon Sep 17 00:00:00 2001 From: Keewis Date: Mon, 10 Aug 2020 23:10:27 +0200 Subject: fix a typo --- sphinx/ext/napoleon/docstring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index a629f147f..4397d0e41 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -1104,7 +1104,7 @@ class NumpyDocstring(GoogleDocstring): _name, _type = line, '' _name, _type = _name.strip(), _type.strip() _name = self._escape_args_and_kwargs(_name) - if self._config.napoleon_enable_type_preprocessor: + if self._config.napoleon_preprocess_types: _type = _convert_numpy_type_spec( _type, location=self._get_location(), -- cgit v1.2.1 From 53a82a56303ae2e0be1e5613db0da3611b1aabb1 Mon Sep 17 00:00:00 2001 From: Yves Chevallier Date: Wed, 12 Aug 2020 16:29:26 +0200 Subject: Use node as location --- sphinx/writers/html.py | 2 +- sphinx/writers/html5.py | 2 +- sphinx/writers/latex.py | 6 +++--- sphinx/writers/texinfo.py | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 4375bf326..8813c2d12 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -450,7 +450,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): highlighted = self.highlighter.highlight_block( node.rawsource, lang, opts=opts, linenos=linenos, - location=(self.builder.current_docname, node.line), **highlight_args + location=node, **highlight_args ) starttag = self.starttag(node, 'div', suffix='', CLASS='highlight-%s notranslate' % lang) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index c1252f4d0..4ceeaafdf 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -402,7 +402,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): highlighted = self.highlighter.highlight_block( node.rawsource, lang, opts=opts, linenos=linenos, - location=(self.builder.current_docname, node.line), **highlight_args + location=node, **highlight_args ) starttag = self.starttag(node, 'div', suffix='', CLASS='highlight-%s notranslate' % lang) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 73a22a76b..b77202951 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -651,7 +651,7 @@ class LaTeXTranslator(SphinxTranslator): if len(node.children) != 1 and not isinstance(node.children[0], nodes.Text): logger.warning(__('document title is not a single Text node'), - location=(self.curfilestack[-1], node.line)) + location=node) if not self.elements['title']: # text needs to be escaped since it is inserted into # the output literally @@ -684,7 +684,7 @@ class LaTeXTranslator(SphinxTranslator): else: logger.warning(__('encountered title node not in section, topic, table, ' 'admonition or sidebar'), - location=(self.curfilestack[-1], node.line or '')) + location=node) self.body.append('\\sphinxstyleothertitle{') self.context.append('}\n') self.in_title = 1 @@ -1762,7 +1762,7 @@ class LaTeXTranslator(SphinxTranslator): hlcode = self.highlighter.highlight_block( node.rawsource, lang, opts=opts, linenos=linenos, - location=(self.curfilestack[-1], node.line), **highlight_args + location=node, **highlight_args ) if self.in_footnote: self.body.append('\n\\sphinxSetupCodeBlockInFootnote') diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index 8c52a0868..5ad2831dd 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -628,7 +628,7 @@ class TexinfoTranslator(SphinxTranslator): elif not isinstance(parent, nodes.section): logger.warning(__('encountered title node not in section, topic, table, ' 'admonition or sidebar'), - location=(self.curfilestack[-1], node.line)) + location=node) self.visit_rubric(node) else: try: @@ -1186,7 +1186,7 @@ class TexinfoTranslator(SphinxTranslator): self.body.append('\n@caption{') else: logger.warning(__('caption not inside a figure.'), - location=(self.curfilestack[-1], node.line)) + location=node) def depart_caption(self, node: Element) -> None: if (isinstance(node.parent, nodes.figure) or @@ -1262,11 +1262,11 @@ class TexinfoTranslator(SphinxTranslator): def unimplemented_visit(self, node: Element) -> None: logger.warning(__("unimplemented node type: %r"), node, - location=(self.curfilestack[-1], node.line)) + location=node) def unknown_visit(self, node: Node) -> None: logger.warning(__("unknown node type: %r"), node, - location=(self.curfilestack[-1], node.line)) + location=node) def unknown_departure(self, node: Node) -> None: pass -- cgit v1.2.1 From 4baa7ce99b6a4a79c203140555743eaa50aed28c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 13 Aug 2020 01:53:30 +0900 Subject: Update CHANGES for PR #8102 --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index d0e4e6316..b0b8de1ea 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,9 @@ Features added Bugs fixed ---------- +* #8093: The highlight warning has wrong location in some builders (LaTeX, + singlehtml and so on) + Testing -------- -- cgit v1.2.1 From 1c388241b463c70f3191aacad2b0867c53245f49 Mon Sep 17 00:00:00 2001 From: Keewis Date: Wed, 12 Aug 2020 19:48:07 +0200 Subject: default to False and update the tests --- sphinx/ext/napoleon/__init__.py | 6 ++-- tests/test_ext_napoleon_docstring.py | 60 +++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py index 793384dd2..e62ef7152 100644 --- a/sphinx/ext/napoleon/__init__.py +++ b/sphinx/ext/napoleon/__init__.py @@ -41,7 +41,7 @@ class Config: napoleon_use_param = True napoleon_use_rtype = True napoleon_use_keyword = True - napoleon_preprocess_types = True + napoleon_preprocess_types = False napoleon_type_aliases = None napoleon_custom_sections = None @@ -238,7 +238,7 @@ class Config: :returns: *bool* -- True if successful, False otherwise - napoleon_preprocess_types : :obj:`bool` (Defaults to True) + napoleon_preprocess_types : :obj:`bool` (Defaults to False) Enable the type preprocessor for numpy style docstrings. napoleon_type_aliases : :obj:`dict` (Defaults to None) @@ -272,7 +272,7 @@ class Config: 'napoleon_use_param': (True, 'env'), 'napoleon_use_rtype': (True, 'env'), 'napoleon_use_keyword': (True, 'env'), - 'napoleon_preprocess_types': (True, 'env'), + 'napoleon_preprocess_types': (False, 'env'), 'napoleon_type_aliases': (None, 'env'), 'napoleon_custom_sections': (None, 'env') } diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py index 06a4a0c1a..bbc075edd 100644 --- a/tests/test_ext_napoleon_docstring.py +++ b/tests/test_ext_napoleon_docstring.py @@ -66,19 +66,19 @@ Sample namedtuple subclass Quick description of attr1 - :type: :class:`Arbitrary type` + :type: Arbitrary type .. attribute:: attr2 Quick description of attr2 - :type: :class:`Another arbitrary type` + :type: Another arbitrary type .. attribute:: attr3 Adds a newline after the type - :type: :class:`Type` + :type: Type """ self.assertEqual(expected, actual) @@ -1311,7 +1311,8 @@ class NumpyDocstringTest(BaseDocstringTest): config = Config( napoleon_use_param=False, napoleon_use_rtype=False, - napoleon_use_keyword=False) + napoleon_use_keyword=False, + napoleon_preprocess_types=True) for docstring, expected in self.docstrings: actual = str(NumpyDocstring(dedent(docstring), config)) expected = dedent(expected) @@ -1373,7 +1374,7 @@ x1, x2 : array_like config = Config(napoleon_use_param=False) actual = str(NumpyDocstring(docstring, config)) expected = """\ -:Parameters: **x1, x2** (:class:`array_like`) -- Input arrays, description of ``x1``, ``x2``. +:Parameters: **x1, x2** (*array_like*) -- Input arrays, description of ``x1``, ``x2``. """ self.assertEqual(expected, actual) @@ -1381,9 +1382,9 @@ x1, x2 : array_like actual = str(NumpyDocstring(dedent(docstring), config)) expected = """\ :param x1: Input arrays, description of ``x1``, ``x2``. -:type x1: :class:`array_like` +:type x1: array_like :param x2: Input arrays, description of ``x1``, ``x2``. -:type x2: :class:`array_like` +:type x2: array_like """ self.assertEqual(expected, actual) @@ -1398,7 +1399,7 @@ param1 : MyClass instance config = Config(napoleon_use_param=False) actual = str(NumpyDocstring(docstring, config)) expected = """\ -:Parameters: **param1** (:class:`MyClass instance`) +:Parameters: **param1** (*MyClass instance*) """ self.assertEqual(expected, actual) @@ -1406,7 +1407,7 @@ param1 : MyClass instance actual = str(NumpyDocstring(dedent(docstring), config)) expected = """\ :param param1: -:type param1: :class:`MyClass instance` +:type param1: MyClass instance """ self.assertEqual(expected, actual) @@ -1495,7 +1496,7 @@ arg_ : type expected = """ :ivar arg_: some description -:vartype arg_: :class:`type` +:vartype arg_: type """ config = Config(napoleon_use_ivar=True) @@ -1515,7 +1516,7 @@ arg_ : type expected = """ :ivar arg\\_: some description -:vartype arg\\_: :class:`type` +:vartype arg\\_: type """ config = Config(napoleon_use_ivar=True) @@ -1883,59 +1884,59 @@ definition_after_normal_text : int expected = """One line summary. :param no_list: -:type no_list: :class:`int` +:type no_list: int :param one_bullet_empty: * -:type one_bullet_empty: :class:`int` +:type one_bullet_empty: int :param one_bullet_single_line: - first line -:type one_bullet_single_line: :class:`int` +:type one_bullet_single_line: int :param one_bullet_two_lines: + first line continued -:type one_bullet_two_lines: :class:`int` +:type one_bullet_two_lines: int :param two_bullets_single_line: - first line - second line -:type two_bullets_single_line: :class:`int` +:type two_bullets_single_line: int :param two_bullets_two_lines: * first line continued * second line continued -:type two_bullets_two_lines: :class:`int` +:type two_bullets_two_lines: int :param one_enumeration_single_line: 1. first line -:type one_enumeration_single_line: :class:`int` +:type one_enumeration_single_line: int :param one_enumeration_two_lines: 1) first line continued -:type one_enumeration_two_lines: :class:`int` +:type one_enumeration_two_lines: int :param two_enumerations_one_line: (iii) first line (iv) second line -:type two_enumerations_one_line: :class:`int` +:type two_enumerations_one_line: int :param two_enumerations_two_lines: a. first line continued b. second line continued -:type two_enumerations_two_lines: :class:`int` +:type two_enumerations_two_lines: int :param one_definition_one_line: item 1 first line -:type one_definition_one_line: :class:`int` +:type one_definition_one_line: int :param one_definition_two_lines: item 1 first line continued -:type one_definition_two_lines: :class:`int` +:type one_definition_two_lines: int :param two_definitions_one_line: item 1 first line item 2 second line -:type two_definitions_one_line: :class:`int` +:type two_definitions_one_line: int :param two_definitions_two_lines: item 1 first line @@ -1943,14 +1944,14 @@ definition_after_normal_text : int item 2 second line continued -:type two_definitions_two_lines: :class:`int` +:type two_definitions_two_lines: int :param one_definition_blank_line: item 1 first line extra first line -:type one_definition_blank_line: :class:`int` +:type one_definition_blank_line: int :param two_definitions_blank_lines: item 1 @@ -1963,12 +1964,12 @@ definition_after_normal_text : int second line extra second line -:type two_definitions_blank_lines: :class:`int` +:type two_definitions_blank_lines: int :param definition_after_normal_text: text line item 1 first line -:type definition_after_normal_text: :class:`int` +:type definition_after_normal_text: int """ config = Config(napoleon_use_param=True) actual = str(NumpyDocstring(docstring, config)) @@ -2062,7 +2063,7 @@ definition_after_normal_text : int item 1 first line """ - config = Config(napoleon_use_param=False) + config = Config(napoleon_use_param=False, napoleon_preprocess_types=True) actual = str(NumpyDocstring(docstring, config)) self.assertEqual(expected, actual) @@ -2243,6 +2244,7 @@ definition_after_normal_text : int config = Config( napoleon_use_param=True, napoleon_use_rtype=True, + napoleon_preprocess_types=True, napoleon_type_aliases=translations, ) actual = str(NumpyDocstring(docstring, config)) -- cgit v1.2.1 From 99e36398fcb5fd4e07dd0dcad1c36afaa7e75800 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 13 Aug 2020 22:38:57 +0900 Subject: Update CHANGES for PR #8095 --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 1d523f3fd..b852b6992 100644 --- a/CHANGES +++ b/CHANGES @@ -34,10 +34,15 @@ Deprecated Features added -------------- +* #8095: napoleon: Add :confval:`napoleon_preprocess_types` to enable the type + preprocessor for numpy style docstrings + Bugs fixed ---------- * #8074: napoleon: Crashes during processing C-ext module +* #8088: napoleon: "Inline literal start-string without end-string" warning in + Numpy style Parameters section * #8084: autodoc: KeyError is raised on documenting an attribute of the broken class * #8091: autodoc: AttributeError is raised on documenting an attribute on Python -- cgit v1.2.1 From 611fff975e9c1b3972fd9e2dfd7a96d3d8d8cdb7 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 13 Aug 2020 00:05:27 +0900 Subject: Fix #8099: autodoc: NameError is raised when script uses TYPE_CHECKING `typing.get_type_hints()` raises NameError when the target object contains unresolavable type annotation (ex. TYPE_CHECKING). This handles the exception and use unresolved annotations for type hints. --- CHANGES | 1 + sphinx/ext/autodoc/__init__.py | 6 ++++++ .../roots/test-ext-autodoc/target/TYPE_CHECKING.py | 8 ++++++++ tests/test_ext_autodoc.py | 22 ++++++++++++++++++++++ 4 files changed, 37 insertions(+) create mode 100644 tests/roots/test-ext-autodoc/target/TYPE_CHECKING.py diff --git a/CHANGES b/CHANGES index b852b6992..0615b3108 100644 --- a/CHANGES +++ b/CHANGES @@ -47,6 +47,7 @@ Bugs fixed class * #8091: autodoc: AttributeError is raised on documenting an attribute on Python 3.5.2 +* #8099: autodoc: NameError is raised when target code uses ``TYPE_CHECKING`` Testing -------- diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index f3820f715..b61a96c84 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1608,6 +1608,9 @@ class DataDocumenter(ModuleLevelDocumenter): # obtain annotation for this data try: annotations = get_type_hints(self.parent) + except NameError: + # Failed to evaluate ForwardRef (maybe TYPE_CHECKING) + annotations = safe_getattr(self.parent, '__annotations__', {}) except TypeError: annotations = {} except KeyError: @@ -1984,6 +1987,9 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # obtain type annotation for this attribute try: annotations = get_type_hints(self.parent) + except NameError: + # Failed to evaluate ForwardRef (maybe TYPE_CHECKING) + annotations = safe_getattr(self.parent, '__annotations__', {}) except TypeError: annotations = {} except KeyError: diff --git a/tests/roots/test-ext-autodoc/target/TYPE_CHECKING.py b/tests/roots/test-ext-autodoc/target/TYPE_CHECKING.py new file mode 100644 index 000000000..aa7eb99a6 --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/TYPE_CHECKING.py @@ -0,0 +1,8 @@ +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from io import StringIO + + +class Foo: + attr1: "StringIO" diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 15e1f3539..90a2ec95a 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -1740,6 +1740,28 @@ def test_autodoc_Annotated(app): ] +@pytest.mark.skipif(sys.version_info < (3, 6), reason='py36+ is required.') +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_TYPE_CHECKING(app): + options = {"members": None, + "undoc-members": None} + actual = do_autodoc(app, 'module', 'target.TYPE_CHECKING', options) + assert list(actual) == [ + '', + '.. py:module:: target.TYPE_CHECKING', + '', + '', + '.. py:class:: Foo()', + ' :module: target.TYPE_CHECKING', + '', + '', + ' .. py:attribute:: Foo.attr1', + ' :module: target.TYPE_CHECKING', + ' :type: StringIO', + '', + ] + + @pytest.mark.sphinx('html', testroot='pycode-egg') def test_autodoc_for_egged_code(app): options = {"members": None, -- cgit v1.2.1 From d72fedb69d7a90eebf8ca92cbaa4c678c381e11a Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Thu, 13 Aug 2020 17:08:39 +0200 Subject: C++, fix template template parameter parsing https://github.com/sphinx-doc/sphinx/pull/8037#issuecomment-673511490 --- CHANGES | 1 + sphinx/domains/cpp.py | 1 + tests/test_domain_cpp.py | 1 + 3 files changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index b852b6992..282646967 100644 --- a/CHANGES +++ b/CHANGES @@ -47,6 +47,7 @@ Bugs fixed class * #8091: autodoc: AttributeError is raised on documenting an attribute on Python 3.5.2 +* C++, fix parsing of template template paramters, broken by the fix of #7944 Testing -------- diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 46708b846..0b3c02e83 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -6251,6 +6251,7 @@ class DefinitionParser(BaseParser): # ========================================================================== def _parse_template_paramter(self) -> ASTTemplateParam: + self.skip_ws() if self.skip_word('template'): # declare a tenplate template parameter nestedParams = self._parse_template_parameter_list() diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 118227cd7..513205cab 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -764,6 +764,7 @@ def test_templates(): check('class', "template typename> {key}A", {2: "II0E0E1A"}) check('class', "template typename ...T> {key}A", {2: "II0EDpE1A"}) check('class', "template typename...> {key}A", {2: "II0EDpE1A"}) + check('class', "template typename...> {key}A", {2: "I0I0EDpE1A"}) check('class', "template {key}A", {2: "I_iE1A"}) check('class', "template {key}A", {2: "I_iE1A"}) -- cgit v1.2.1 From 2f971a61f3fda7ead81f1434913ed0d2f52d82cf Mon Sep 17 00:00:00 2001 From: harupy <17039389+harupy@users.noreply.github.com> Date: Fri, 14 Aug 2020 00:41:17 +0900 Subject: Color pytest output log --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index bddd822a6..e2bad0293 100644 --- a/tox.ini +++ b/tox.ini @@ -55,7 +55,7 @@ basepython = python3 description = Run code coverage checks. setenv = - PYTEST_ADDOPTS = --cov sphinx --cov-config {toxinidir}/setup.cfg + PYTEST_ADDOPTS = --cov sphinx --cov-config {toxinidir}/setup.cfg --color yes commands = {[testenv]commands} coverage report -- cgit v1.2.1 From 2a632598c3c93222bcfcf2654593c08ee2c9271c Mon Sep 17 00:00:00 2001 From: harupy <17039389+harupy@users.noreply.github.com> Date: Fri, 14 Aug 2020 00:48:41 +0900 Subject: fix --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index e2bad0293..119944d6e 100644 --- a/tox.ini +++ b/tox.ini @@ -26,6 +26,7 @@ extras = test setenv = PYTHONWARNINGS = all,ignore::ImportWarning:importlib._bootstrap_external,ignore::DeprecationWarning:site,ignore::DeprecationWarning:distutils + PYTEST_ADDOPTS = --color yes commands= pytest --durations 25 {posargs} @@ -55,7 +56,7 @@ basepython = python3 description = Run code coverage checks. setenv = - PYTEST_ADDOPTS = --cov sphinx --cov-config {toxinidir}/setup.cfg --color yes + PYTEST_ADDOPTS = --cov sphinx --cov-config {toxinidir}/setup.cfg commands = {[testenv]commands} coverage report -- cgit v1.2.1 From 52140be6b8d32795aaf99d4535121eaea6d8b3b3 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Thu, 13 Aug 2020 18:39:28 +0200 Subject: C and C++, parsing function attributes Fixes sphinx-doc/sphinx#8114 --- CHANGES | 1 + sphinx/domains/c.py | 21 ++++++++++++++++++--- sphinx/domains/cpp.py | 19 +++++++++++++++++-- sphinx/util/cfamily.py | 2 +- tests/test_domain_c.py | 9 ++++----- tests/test_domain_cpp.py | 6 +++--- 6 files changed, 44 insertions(+), 14 deletions(-) diff --git a/CHANGES b/CHANGES index 282646967..50008f519 100644 --- a/CHANGES +++ b/CHANGES @@ -36,6 +36,7 @@ Features added * #8095: napoleon: Add :confval:`napoleon_preprocess_types` to enable the type preprocessor for numpy style docstrings +* #8114: C and C++, parse function attributes after parameters and qualifiers. Bugs fixed ---------- diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 65786b5de..d8ccc2e3d 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -32,7 +32,7 @@ from sphinx.transforms import SphinxTransform from sphinx.transforms.post_transforms import ReferencesResolver from sphinx.util import logging from sphinx.util.cfamily import ( - NoOldIdError, ASTBaseBase, ASTBaseParenExprList, + NoOldIdError, ASTBaseBase, ASTAttribute, ASTBaseParenExprList, verify_description_mode, StringifyTransform, BaseParser, DefinitionError, UnsupportedMultiCharacterCharLiteral, identifier_re, anon_identifier_re, integer_literal_re, octal_literal_re, @@ -652,8 +652,9 @@ class ASTFunctionParameter(ASTBase): class ASTParameters(ASTBase): - def __init__(self, args: List[ASTFunctionParameter]) -> None: + def __init__(self, args: List[ASTFunctionParameter], attrs: List[ASTAttribute]) -> None: self.args = args + self.attrs = attrs @property def function_params(self) -> List[ASTFunctionParameter]: @@ -669,6 +670,9 @@ class ASTParameters(ASTBase): first = False res.append(str(a)) res.append(')') + for attr in self.attrs: + res.append(' ') + res.append(transform(attr)) return ''.join(res) def describe_signature(self, signode: TextElement, mode: str, @@ -683,6 +687,9 @@ class ASTParameters(ASTBase): arg.describe_signature(param, 'markType', env, symbol=symbol) paramlist += param signode += paramlist + for attr in self.attrs: + signode += nodes.Text(' ') + attr.describe_signature(signode) class ASTDeclSpecsSimple(ASTBaseBase): @@ -2572,7 +2579,15 @@ class DefinitionParser(BaseParser): self.fail( 'Expecting "," or ")" in parameters, ' 'got "%s".' % self.current_char) - return ASTParameters(args) + + attrs = [] + while True: + attr = self._parse_attribute() + if attr is None: + break + attrs.append(attr) + + return ASTParameters(args, attrs) def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple: """Just parse the simple ones.""" diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 0b3c02e83..92d578427 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -1879,7 +1879,8 @@ class ASTNoexceptSpec(ASTBase): class ASTParametersQualifiers(ASTBase): def __init__(self, args: List[ASTFunctionParameter], volatile: bool, const: bool, refQual: str, exceptionSpec: ASTNoexceptSpec, trailingReturn: "ASTType", - override: bool, final: bool, initializer: str) -> None: + override: bool, final: bool, attrs: List[ASTAttribute], + initializer: str) -> None: self.args = args self.volatile = volatile self.const = const @@ -1888,6 +1889,7 @@ class ASTParametersQualifiers(ASTBase): self.trailingReturn = trailingReturn self.override = override self.final = final + self.attrs = attrs self.initializer = initializer @property @@ -1947,6 +1949,9 @@ class ASTParametersQualifiers(ASTBase): res.append(' final') if self.override: res.append(' override') + for attr in self.attrs: + res.append(' ') + res.append(transform(attr)) if self.initializer: res.append(' = ') res.append(self.initializer) @@ -1988,6 +1993,9 @@ class ASTParametersQualifiers(ASTBase): _add_anno(signode, 'final') if self.override: _add_anno(signode, 'override') + for attr in self.attrs: + signode += nodes.Text(' ') + attr.describe_signature(signode) if self.initializer: _add_text(signode, '= ' + str(self.initializer)) @@ -5709,6 +5717,13 @@ class DefinitionParser(BaseParser): override = self.skip_word_and_ws( 'override') # they can be permuted + attrs = [] + while True: + attr = self._parse_attribute() + if attr is None: + break + attrs.append(attr) + self.skip_ws() initializer = None if self.skip_string('='): @@ -5725,7 +5740,7 @@ class DefinitionParser(BaseParser): return ASTParametersQualifiers( args, volatile, const, refQual, exceptionSpec, trailingReturn, - override, final, initializer) + override, final, attrs, initializer) def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple: """Just parse the simple ones.""" diff --git a/sphinx/util/cfamily.py b/sphinx/util/cfamily.py index a67b2a0ad..0edea128c 100644 --- a/sphinx/util/cfamily.py +++ b/sphinx/util/cfamily.py @@ -391,7 +391,7 @@ class BaseParser: % startPos) return self.definition[startPos:self.pos] - def _parse_attribute(self) -> ASTAttribute: + def _parse_attribute(self) -> Optional[ASTAttribute]: self.skip_ws() # try C++11 style startPos = self.pos diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 71bf251e9..b6f72287e 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -497,17 +497,16 @@ def test_attributes(): parse('member', 'paren_attr({]}) int f') # position: decl specs - check('function', 'static inline __attribute__(()) void f()', - {1: 'f'}, + check('function', 'static inline __attribute__(()) void f()', {1: 'f'}, output='__attribute__(()) static inline void f()') - check('function', '[[attr1]] [[attr2]] void f()', - {1: 'f'}, - output='[[attr1]] [[attr2]] void f()') + check('function', '[[attr1]] [[attr2]] void f()', {1: 'f'}) # position: declarator check('member', 'int *[[attr]] i', {1: 'i'}) check('member', 'int *const [[attr]] volatile i', {1: 'i'}, output='int *[[attr]] volatile const i') check('member', 'int *[[attr]] *i', {1: 'i'}) + # position: parameters + check('function', 'void f() [[attr1]] [[attr2]]', {1: 'f'}) # issue michaeljones/breathe#500 check('function', 'LIGHTGBM_C_EXPORT int LGBM_BoosterFree(int handle)', diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 513205cab..558d69911 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -938,15 +938,15 @@ def test_attributes(): check('function', 'static inline __attribute__(()) void f()', {1: 'f', 2: '1fv'}, output='__attribute__(()) static inline void f()') - check('function', '[[attr1]] [[attr2]] void f()', - {1: 'f', 2: '1fv'}, - output='[[attr1]] [[attr2]] void f()') + check('function', '[[attr1]] [[attr2]] void f()', {1: 'f', 2: '1fv'}) # position: declarator check('member', 'int *[[attr]] i', {1: 'i__iP', 2: '1i'}) check('member', 'int *const [[attr]] volatile i', {1: 'i__iPVC', 2: '1i'}, output='int *[[attr]] volatile const i') check('member', 'int &[[attr]] i', {1: 'i__iR', 2: '1i'}) check('member', 'int *[[attr]] *i', {1: 'i__iPP', 2: '1i'}) + # position: parameters and qualifiers + check('function', 'void f() [[attr1]] [[attr2]]', {1: 'f', 2: '1fv'}) def test_xref_parsing(): -- cgit v1.2.1 From ec7e8aa5ebc4f1ace46c7ba45360314335910e7a Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 14 Aug 2020 14:23:03 +0900 Subject: Update CHANGES for PR #8058 --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index b0b8de1ea..e808cdfde 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,7 @@ Features added Bugs fixed ---------- +* #8085: i18n: Add support for having single text domain * #8093: The highlight warning has wrong location in some builders (LaTeX, singlehtml and so on) -- cgit v1.2.1 From 359794281065646a81f2fb3d3a7086681c61e12d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 14 Aug 2020 19:53:48 +0900 Subject: Bump to 3.2.1 final --- CHANGES | 37 ++----------------------------------- sphinx/__init__.py | 6 +++--- 2 files changed, 5 insertions(+), 38 deletions(-) diff --git a/CHANGES b/CHANGES index 80437e6c6..a6cc75081 100644 --- a/CHANGES +++ b/CHANGES @@ -1,35 +1,5 @@ -Release 3.3.0 (in development) -============================== - -Dependencies ------------- - -Incompatible changes --------------------- - -Deprecated ----------- - -Features added --------------- - -Bugs fixed ----------- - -Testing --------- - -Release 3.2.1 (in development) -============================== - -Dependencies ------------- - -Incompatible changes --------------------- - -Deprecated ----------- +Release 3.2.1 (released Aug 14, 2020) +===================================== Features added -------------- @@ -51,9 +21,6 @@ Bugs fixed * #8099: autodoc: NameError is raised when target code uses ``TYPE_CHECKING`` * C++, fix parsing of template template paramters, broken by the fix of #7944 -Testing --------- - Release 3.2.0 (released Aug 08, 2020) ===================================== diff --git a/sphinx/__init__.py b/sphinx/__init__.py index fde2345a8..1f550419f 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -32,8 +32,8 @@ if 'PYTHONWARNINGS' not in os.environ: warnings.filterwarnings('ignore', "'U' mode is deprecated", DeprecationWarning, module='docutils.io') -__version__ = '3.3.0+' -__released__ = '3.3.0' # used when Sphinx builds its own docs +__version__ = '3.2.1' +__released__ = '3.2.1' # used when Sphinx builds its own docs #: Version info for better programmatic use. #: @@ -43,7 +43,7 @@ __released__ = '3.3.0' # used when Sphinx builds its own docs #: #: .. versionadded:: 1.2 #: Before version 1.2, check the string ``sphinx.__version__``. -version_info = (3, 3, 0, 'beta', 0) +version_info = (3, 2, 1, 'final', 0) package_dir = path.abspath(path.dirname(__file__)) -- cgit v1.2.1 From fabe685638c7f8fa24b3b0a44ac264cff5f1d160 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 14 Aug 2020 19:55:41 +0900 Subject: Bump version --- CHANGES | 21 +++++++++++++++++++++ sphinx/__init__.py | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index a6cc75081..5176d8372 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,24 @@ +Release 3.2.2 (in development) +============================== + +Dependencies +------------ + +Incompatible changes +-------------------- + +Deprecated +---------- + +Features added +-------------- + +Bugs fixed +---------- + +Testing +-------- + Release 3.2.1 (released Aug 14, 2020) ===================================== diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 1f550419f..473896630 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -32,8 +32,8 @@ if 'PYTHONWARNINGS' not in os.environ: warnings.filterwarnings('ignore', "'U' mode is deprecated", DeprecationWarning, module='docutils.io') -__version__ = '3.2.1' -__released__ = '3.2.1' # used when Sphinx builds its own docs +__version__ = '3.2.2+' +__released__ = '3.2.2' # used when Sphinx builds its own docs #: Version info for better programmatic use. #: @@ -43,7 +43,7 @@ __released__ = '3.2.1' # used when Sphinx builds its own docs #: #: .. versionadded:: 1.2 #: Before version 1.2, check the string ``sphinx.__version__``. -version_info = (3, 2, 1, 'final', 0) +version_info = (3, 2, 2, 'beta', 0) package_dir = path.abspath(path.dirname(__file__)) -- cgit v1.2.1 From 19f6c39c48ed043f986e5d17b42aadc55c200a4a Mon Sep 17 00:00:00 2001 From: Yves Chevallier Date: Fri, 7 Aug 2020 14:19:12 +0200 Subject: Invite users to ask their questions on Stackoverflow --- .github/ISSUE_TEMPLATE/config.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index c9009b90e..226532b79 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,5 +2,8 @@ blank_issues_enabled: false # default: true contact_links: - name: Question + url: https://stackoverflow.com/questions/tagged/python-sphinx + about: For Q&A purpose, please use Stackoverflow with the tag python-sphinx +- name: Discussion url: https://groups.google.com/forum/#!forum/sphinx-users - about: For Q&A purpose, please use sphinx-users mailing list. + about: For general discussion, please use sphinx-users mailing list. -- cgit v1.2.1 From e45f55d32fc6d64f868f37b3588e20957f09b705 Mon Sep 17 00:00:00 2001 From: Yves Chevallier Date: Sat, 15 Aug 2020 13:22:22 +0200 Subject: Add rubric in Getting help --- doc/internals/contributing.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/internals/contributing.rst b/doc/internals/contributing.rst index 0e464478a..1f4a31013 100644 --- a/doc/internals/contributing.rst +++ b/doc/internals/contributing.rst @@ -12,6 +12,9 @@ Getting help The Sphinx community maintains a number of mailing lists and IRC channels. +Stack Overflow with tag `python-sphinx`_ + Questions and answers about use and development. + sphinx-users Mailing list for user support. @@ -21,6 +24,7 @@ sphinx-dev #sphinx-doc on irc.freenode.net IRC channel for development questions and user support. +.. _python-sphinx: https://stackoverflow.com/questions/tagged/python-sphinx Bug Reports and Feature Requests -------------------------------- -- cgit v1.2.1