From 14ef3791b9c147c8fbf35f7e068d56eac84fef55 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 23 May 2020 20:17:55 +0900 Subject: refactor: autodoc: Sort members alphabetically when unknown order --- sphinx/ext/autodoc/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'sphinx') diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index f9c775f7d..c0905e523 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -733,10 +733,8 @@ class Documenter: # This is a reasonable assumption in Python 3.6 and up, where # module.__dict__ is insertion-ordered. pass - elif member_order == 'alphabetical': + else: # alphabetical memberdocumenters.sort(key=lambda e: e[0].name) - else: - raise ValueError("Illegal member order {}".format(member_order)) for documenter, isattr in memberdocumenters: documenter.generate( -- cgit v1.2.3 From b8e2cfde7b3f0fda0a77ebfc8254e59fc36ff0af Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 23 May 2020 20:25:38 +0900 Subject: refactor: autodoc: Add Documenter.sort_members() --- sphinx/ext/autodoc/__init__.py | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'sphinx') diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index c0905e523..075e74e22 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -714,12 +714,26 @@ class Documenter: '.'.join(self.objpath + [mname]) documenter = classes[-1](self.directive, full_mname, self.indent) memberdocumenters.append((documenter, isattr)) - member_order = self.options.member_order or \ - self.env.config.autodoc_member_order - if member_order == 'groupwise': + + member_order = self.options.member_order or self.env.config.autodoc_member_order + memberdocumenters = self.sort_members(memberdocumenters, member_order) + + for documenter, isattr in memberdocumenters: + documenter.generate( + all_members=True, real_modname=self.real_modname, + check_module=members_check_module and not isattr) + + # reset current objects + self.env.temp_data['autodoc:module'] = None + self.env.temp_data['autodoc:class'] = None + + def sort_members(self, documenters: List[Tuple["Documenter", bool]], + order: str) -> List[Tuple["Documenter", bool]]: + """Sort the given member list.""" + if order == 'groupwise': # sort by group; alphabetically within groups - memberdocumenters.sort(key=lambda e: (e[0].member_order, e[0].name)) - elif member_order == 'bysource': + documenters.sort(key=lambda e: (e[0].member_order, e[0].name)) + elif order == 'bysource': if self.analyzer: # sort by source order, by virtue of the module analyzer tagorder = self.analyzer.tagorder @@ -727,23 +741,16 @@ class Documenter: def keyfunc(entry: Tuple[Documenter, bool]) -> int: fullname = entry[0].name.split('::')[1] return tagorder.get(fullname, len(tagorder)) - memberdocumenters.sort(key=keyfunc) + documenters.sort(key=keyfunc) else: # Assume that member discovery order matches source order. # This is a reasonable assumption in Python 3.6 and up, where # module.__dict__ is insertion-ordered. pass else: # alphabetical - memberdocumenters.sort(key=lambda e: e[0].name) + documenters.sort(key=lambda e: e[0].name) - for documenter, isattr in memberdocumenters: - documenter.generate( - all_members=True, real_modname=self.real_modname, - check_module=members_check_module and not isattr) - - # reset current objects - self.env.temp_data['autodoc:module'] = None - self.env.temp_data['autodoc:class'] = None + return documenters def generate(self, more_content: Any = None, real_modname: str = None, check_module: bool = False, all_members: bool = False) -> None: -- cgit v1.2.3 From d674d917ae006f056b0f1f39d85f3579900dd53b Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 23 May 2020 20:41:54 +0900 Subject: refactor: autodoc: Copy module.__all__ to ModuleDocumenter.__all__ --- sphinx/ext/autodoc/__init__.py | 43 +++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) (limited to 'sphinx') diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 075e74e22..998ab7fe2 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -857,6 +857,7 @@ class ModuleDocumenter(Documenter): def __init__(self, *args: Any) -> None: super().__init__(*args) merge_special_members_option(self.options) + self.__all__ = None @classmethod def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any @@ -879,6 +880,30 @@ class ModuleDocumenter(Documenter): type='autodoc') return ret + def import_object(self) -> Any: + def is_valid_module_all(__all__: Any) -> bool: + """Check the given *__all__* is valid for a module.""" + if (isinstance(__all__, (list, tuple)) and + all(isinstance(e, str) for e in __all__)): + return True + else: + return False + + ret = super().import_object() + + if not self.options.ignore_module_all: + __all__ = getattr(self.object, '__all__', None) + if is_valid_module_all(__all__): + # valid __all__ found. copy it to self.__all__ + self.__all__ = __all__ + elif __all__: + # invalid __all__ found. + logger.warning(__('__all__ should be a list of strings, not %r ' + '(in module %s) -- ignoring __all__') % + (__all__, self.fullname), type='autodoc') + + return ret + def add_directive_header(self, sig: str) -> None: Documenter.add_directive_header(self, sig) @@ -894,24 +919,12 @@ class ModuleDocumenter(Documenter): def get_object_members(self, want_all: bool) -> Tuple[bool, List[Tuple[str, Any]]]: if want_all: - if (self.options.ignore_module_all or not - hasattr(self.object, '__all__')): + if self.__all__: + memberlist = self.__all__ + else: # for implicit module members, check __module__ to avoid # documenting imported objects return True, get_module_members(self.object) - else: - memberlist = self.object.__all__ - # Sometimes __all__ is broken... - if not isinstance(memberlist, (list, tuple)) or not \ - all(isinstance(entry, str) for entry in memberlist): - logger.warning( - __('__all__ should be a list of strings, not %r ' - '(in module %s) -- ignoring __all__') % - (memberlist, self.fullname), - type='autodoc' - ) - # fall back to all members - return True, get_module_members(self.object) else: memberlist = self.options.members or [] ret = [] -- cgit v1.2.3 From a5e3b4a43db4c9baf190960f88bb07838e4dc3e2 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 23 May 2020 21:42:29 +0900 Subject: Fix #3673: autodoc: bysource order does not work for a module having __all__ --- sphinx/ext/autodoc/__init__.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'sphinx') diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 998ab7fe2..027fb0869 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -940,6 +940,25 @@ class ModuleDocumenter(Documenter): ) return False, ret + def sort_members(self, documenters: List[Tuple["Documenter", bool]], + order: str) -> List[Tuple["Documenter", bool]]: + if order == 'bysource' and self.__all__: + # Sort alphabetically first (for members not listed on the __all__) + documenters.sort(key=lambda e: e[0].name) + + # Sort by __all__ + def keyfunc(entry: Tuple[Documenter, bool]) -> int: + name = entry[0].name.split('::')[1] + if name in self.__all__: + return self.__all__.index(name) + else: + return len(self.__all__) + documenters.sort(key=keyfunc) + + return documenters + else: + return super().sort_members(documenters, order) + class ModuleLevelDocumenter(Documenter): """ -- cgit v1.2.3 From d501b94311d5e139e27ecb709ce645a18206063a Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 14 Mar 2020 17:33:29 +0900 Subject: latex: Allow to add LaTeX package after hyperref loaded via API --- sphinx/application.py | 11 ++++++++--- sphinx/builders/latex/__init__.py | 2 ++ sphinx/registry.py | 9 +++++++-- sphinx/templates/latex/latex.tex_t | 8 ++++++++ 4 files changed, 25 insertions(+), 5 deletions(-) (limited to 'sphinx') diff --git a/sphinx/application.py b/sphinx/application.py index b02fb4d60..bd23c86e7 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -968,12 +968,14 @@ class Sphinx: self.add_css_file(filename, **attributes) - def add_latex_package(self, packagename: str, options: str = None) -> None: + def add_latex_package(self, packagename: str, options: str = None, + after_hyperref: bool = False) -> None: r"""Register a package to include in the LaTeX source code. Add *packagename* to the list of packages that LaTeX source code will include. If you provide *options*, it will be taken to `\usepackage` - declaration. + declaration. If you set *after_hyperref* truthy, the package will be + loaded after ``hyperref`` package. .. code-block:: python @@ -983,8 +985,11 @@ class Sphinx: # => \usepackage[foo,bar]{mypackage} .. versionadded:: 1.3 + .. versionadded:: 3.1 + + *after_hyperref* option. """ - self.registry.add_latex_package(packagename, options) + self.registry.add_latex_package(packagename, options, after_hyperref) def add_lexer(self, alias: str, lexer: Union[Lexer, "Type[Lexer]"]) -> None: """Register a new lexer for source code. diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 225975c20..11b0899f6 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -129,6 +129,7 @@ class LaTeXBuilder(Builder): self.document_data = [] # type: List[Tuple[str, str, str, str, str, bool]] self.themes = ThemeFactory(self.app) self.usepackages = self.app.registry.latex_packages + self.usepackages_after_hyperref = self.app.registry.latex_packages_after_hyperref texescape.init() self.init_context() @@ -180,6 +181,7 @@ class LaTeXBuilder(Builder): # Apply extension settings to context self.context['packages'] = self.usepackages + self.context['packages_after_hyperref'] = self.usepackages_after_hyperref # Apply user settings to context self.context.update(self.config.latex_elements) diff --git a/sphinx/registry.py b/sphinx/registry.py index 200f59d42..cad74559c 100644 --- a/sphinx/registry.py +++ b/sphinx/registry.py @@ -98,6 +98,8 @@ class SphinxComponentRegistry: #: LaTeX packages; list of package names and its options self.latex_packages = [] # type: List[Tuple[str, str]] + self.latex_packages_after_hyperref = [] # type: List[Tuple[str, str]] + #: post transforms; list of transforms self.post_transforms = [] # type: List[Type[Transform]] @@ -363,9 +365,12 @@ class SphinxComponentRegistry: logger.debug('[app] adding js_file: %r, %r', filename, attributes) self.js_files.append((filename, attributes)) - def add_latex_package(self, name: str, options: str) -> None: + def add_latex_package(self, name: str, options: str, after_hyperref: bool = False) -> None: logger.debug('[app] adding latex package: %r', name) - self.latex_packages.append((name, options)) + if after_hyperref: + self.latex_packages_after_hyperref.append((name, options)) + else: + self.latex_packages.append((name, options)) def add_enumerable_node(self, node: "Type[Node]", figtype: str, title_getter: TitleGetter = None, override: bool = False) -> None: diff --git a/sphinx/templates/latex/latex.tex_t b/sphinx/templates/latex/latex.tex_t index a0a5a26b1..5082254e7 100644 --- a/sphinx/templates/latex/latex.tex_t +++ b/sphinx/templates/latex/latex.tex_t @@ -46,6 +46,14 @@ <%- endfor %> <%= hyperref %> +<%- for name, option in packages_after_hyperref %> +<%- if option %> +\usepackage[<%= option %>]{<%= name %>} +<%- else %> +\usepackage{<%= name %>} +<%- endif %> +<%- endfor %> + <%= contentsname %> \usepackage{sphinxmessages} <%= tocdepth %> -- cgit v1.2.3 From 6c324be96b936c619eb51ce5973ce58fb663df12 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 14 Mar 2020 17:34:31 +0900 Subject: Fix #4187: latex: EN DASH disappears from PDF bookmarks in Japanese documents --- sphinx/builders/latex/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sphinx') diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 11b0899f6..88c471675 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -503,6 +503,12 @@ def validate_latex_theme_options(app: Sphinx, config: Config) -> None: config.latex_theme_options.pop(key) +def install_pakcages_for_ja(app: Sphinx) -> None: + """Install packages for Japanese.""" + if app.config.language == 'ja': + app.add_latex_package('pxjahyper', after_hyperref=True) + + def default_latex_engine(config: Config) -> str: """ Better default latex_engine settings for specific languages. """ if config.language == 'ja': @@ -550,6 +556,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.add_builder(LaTeXBuilder) app.connect('config-inited', validate_config_values, priority=800) app.connect('config-inited', validate_latex_theme_options, priority=800) + app.connect('builder-inited', install_pakcages_for_ja) app.add_config_value('latex_engine', default_latex_engine, None, ENUM('pdflatex', 'xelatex', 'lualatex', 'platex', 'uplatex')) -- cgit v1.2.3 From a28c9ad84225b09ae4968a21e9d24dee38d34c95 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 28 May 2020 01:46:14 +0900 Subject: Fix #7734: napoleon: overescaped trailing underscore on attribute --- sphinx/ext/napoleon/docstring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sphinx') diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 11409e6f6..32edd7f8f 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -318,7 +318,7 @@ class GoogleDocstring: return [line[min_indent:] for line in lines] def _escape_args_and_kwargs(self, name: str) -> str: - if name.endswith('_'): + if name.endswith('_') and getattr(self._config, 'strip_signature_backslash', False): name = name[:-1] + r'\_' if name[:2] == '**': -- cgit v1.2.3 From d229b120adb57f02e7b56c8936da081a09a28703 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 26 Mar 2020 15:30:33 +0000 Subject: Fix autoclass signature parsing This fixes: * Signatures defined by __new__ * Signatures defined by metaclasses * Signatures defined by builtin base classes All of these changes bring the sphinx docs inline with the behavior of `inspect.signature`. Note that this changes autodoc to output `.. py:class: MyClass()` with parentheses even if no user-defined __init__ is present. This is quite deliberate, as if no user-defined `__init__` is present the default is `object.__init__`, which indeed does not take arguments. --- sphinx/ext/autodoc/__init__.py | 88 +++++++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 15 deletions(-) (limited to 'sphinx') diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 027fb0869..89de4372a 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1205,6 +1205,14 @@ class DecoratorDocumenter(FunctionDocumenter): return None +# Types which have confusing metaclass signatures it would be best not to show. +# These are listed by name, rather than storing the objects themselves, to avoid +# needing to import the modules. +_METACLASS_CALL_BLACKLIST = [ + 'enum.EnumMeta.__call__', +] + + class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: ignore """ Specialized Documenter subclass for classes. @@ -1243,22 +1251,72 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: if self.env.config.autodoc_typehints in ('none', 'description'): kwargs.setdefault('show_annotation', False) - # for classes, the relevant signature is the __init__ method's - initmeth = self.get_attr(self.object, '__init__', None) - # classes without __init__ method, default __init__ or - # __init__ written in C? - if initmeth is None or \ - inspect.is_builtin_class_method(self.object, '__init__') or \ - not(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)): - return None - try: - self.env.app.emit('autodoc-before-process-signature', initmeth, True) - sig = inspect.signature(initmeth, bound_method=True) + def get_user_defined_function_or_method(obj: Any, attr: str) -> Any: + """ Get the `attr` function or method from `obj`, if it is user-defined. """ + if inspect.is_builtin_class_method(obj, attr): + return None + attr = self.get_attr(obj, attr, None) + if not (inspect.ismethod(attr) or inspect.isfunction(attr)): + return None + return attr + + sig = None + + # this sequence is copied from inspect._signature_from_callable + + # First, let's see if it has an overloaded __call__ defined + # in its metaclass + if sig is None: + call = get_user_defined_function_or_method(type(self.object), '__call__') + + if call is not None: + if "{0.__module__}.{0.__qualname__}".format(call) in _METACLASS_CALL_BLACKLIST: + call = None + + if call is not None: + self.env.app.emit('autodoc-before-process-signature', call, True) + try: + sig = inspect.signature(call, bound_method=True) + except ValueError: + pass + + # Now we check if the 'obj' class has a '__new__' method + if sig is None: + new = get_user_defined_function_or_method(self.object, '__new__') + if new is not None: + self.env.app.emit('autodoc-before-process-signature', new, True) + try: + sig = inspect.signature(new, bound_method=True) + except ValueError: + pass + + # Finally, we should have at least __init__ implemented + if sig is None: + init = get_user_defined_function_or_method(self.object, '__init__') + if init is not None: + self.env.app.emit('autodoc-before-process-signature', init, True) + try: + sig = inspect.signature(init, bound_method=True) + except ValueError: + pass + + # None of the attributes are user-defined, so fall back to let inspect + # handle it. + if sig is None: + # We don't know the exact method that inspect.signature will read + # the signature from, so just pass the object itself to our hook. + self.env.app.emit('autodoc-before-process-signature', self.object, False) + try: + sig = inspect.signature(self.object, bound_method=False) + except ValueError: + pass + + if sig is not None: return stringify_signature(sig, show_return_annotation=False, **kwargs) - except TypeError: - # still not possible: happens e.g. for old-style classes - # with __init__ in C - return None + + # Still no signature: happens e.g. for old-style classes + # with __init__ in C and no `__text_signature__`. + return None def format_signature(self, **kwargs: Any) -> str: if self.doc_as_attr: -- cgit v1.2.3 From 79d8bfb9e03216c99ba2e734d425361b87ec2f84 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 22 May 2020 11:55:14 -0400 Subject: BUG: Fix check for meth --- sphinx/util/inspect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sphinx') diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 6ba698eed..15f0d66e2 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -726,7 +726,7 @@ def getdoc(obj: Any, attrgetter: Callable = safe_getattr, # This tries to obtain the docstring from super classes. for basecls in getattr(cls, '__mro__', []): meth = safe_getattr(basecls, name, None) - if meth: + if meth is not None: doc = inspect.getdoc(meth) if doc: break -- cgit v1.2.3 From d5584172ab6aa05a05bdd2835df699ed2a94f501 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 7 May 2020 17:29:37 +0100 Subject: Refactor to simplify format_args, catch TypeError --- sphinx/ext/autodoc/__init__.py | 114 +++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 51 deletions(-) (limited to 'sphinx') diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 89de4372a..f8e4be999 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -13,7 +13,7 @@ import importlib import re import warnings -from inspect import Parameter +from inspect import Parameter, Signature from types import ModuleType from typing import ( Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union @@ -392,6 +392,17 @@ class Documenter: # directives of course) return '.'.join(self.objpath) or self.modname + def _call_format_args(self, **kwargs: Any) -> str: + if kwargs: + try: + return self.format_args(**kwargs) + except TypeError: + # avoid chaining exceptions, by putting nothing here + pass + + # retry without arguments for old documenters + return self.format_args() + def format_signature(self, **kwargs: Any) -> str: """Format the signature (arguments and return annotation) of the object. @@ -405,12 +416,7 @@ class Documenter: # try to introspect the signature try: retann = None - try: - args = self.format_args(**kwargs) - except TypeError: - # retry without arguments for old documenters - args = self.format_args() - + args = self._call_format_args(**kwargs) if args: matched = re.match(r'^(\(.*\))\s+->\s+(.*)$', args) if matched: @@ -1247,10 +1253,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: self.doc_as_attr = True return ret - def format_args(self, **kwargs: Any) -> str: - if self.env.config.autodoc_typehints in ('none', 'description'): - kwargs.setdefault('show_annotation', False) - + def _get_signature(self) -> Optional[Signature]: def get_user_defined_function_or_method(obj: Any, attr: str) -> Any: """ Get the `attr` function or method from `obj`, if it is user-defined. """ if inspect.is_builtin_class_method(obj, attr): @@ -1260,64 +1263,73 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: return None return attr - sig = None - - # this sequence is copied from inspect._signature_from_callable + # This sequence is copied from inspect._signature_from_callable. + # ValueError means that no signature could be found, so we keep going. # First, let's see if it has an overloaded __call__ defined # in its metaclass - if sig is None: - call = get_user_defined_function_or_method(type(self.object), '__call__') + call = get_user_defined_function_or_method(type(self.object), '__call__') - if call is not None: - if "{0.__module__}.{0.__qualname__}".format(call) in _METACLASS_CALL_BLACKLIST: - call = None + if call is not None: + if "{0.__module__}.{0.__qualname__}".format(call) in _METACLASS_CALL_BLACKLIST: + call = None - if call is not None: - self.env.app.emit('autodoc-before-process-signature', call, True) - try: - sig = inspect.signature(call, bound_method=True) - except ValueError: - pass + if call is not None: + self.env.app.emit('autodoc-before-process-signature', call, True) + try: + return inspect.signature(call, bound_method=True) + except ValueError: + pass # Now we check if the 'obj' class has a '__new__' method - if sig is None: - new = get_user_defined_function_or_method(self.object, '__new__') - if new is not None: - self.env.app.emit('autodoc-before-process-signature', new, True) - try: - sig = inspect.signature(new, bound_method=True) - except ValueError: - pass + new = get_user_defined_function_or_method(self.object, '__new__') + if new is not None: + self.env.app.emit('autodoc-before-process-signature', new, True) + try: + return inspect.signature(new, bound_method=True) + except ValueError: + pass # Finally, we should have at least __init__ implemented - if sig is None: - init = get_user_defined_function_or_method(self.object, '__init__') - if init is not None: - self.env.app.emit('autodoc-before-process-signature', init, True) - try: - sig = inspect.signature(init, bound_method=True) - except ValueError: - pass - - # None of the attributes are user-defined, so fall back to let inspect - # handle it. - if sig is None: - # We don't know the exact method that inspect.signature will read - # the signature from, so just pass the object itself to our hook. - self.env.app.emit('autodoc-before-process-signature', self.object, False) + init = get_user_defined_function_or_method(self.object, '__init__') + if init is not None: + self.env.app.emit('autodoc-before-process-signature', init, True) try: - sig = inspect.signature(self.object, bound_method=False) + return inspect.signature(init, bound_method=True) except ValueError: pass - if sig is not None: - return stringify_signature(sig, show_return_annotation=False, **kwargs) + # None of the attributes are user-defined, so fall back to let inspect + # handle it. + # We don't know the exact method that inspect.signature will read + # the signature from, so just pass the object itself to our hook. + self.env.app.emit('autodoc-before-process-signature', self.object, False) + try: + return inspect.signature(self.object, bound_method=False) + except ValueError: + pass # Still no signature: happens e.g. for old-style classes # with __init__ in C and no `__text_signature__`. return None + def format_args(self, **kwargs: Any) -> str: + if self.env.config.autodoc_typehints in ('none', 'description'): + kwargs.setdefault('show_annotation', False) + + try: + sig = self._get_signature() + except TypeError as exc: + # __signature__ attribute contained junk + logger.warning(__("Failed to get a constructor signature for %s: %s"), + self.fullname, exc) + return None + + if sig is None: + return None + + return stringify_signature(sig, show_return_annotation=False, **kwargs) + def format_signature(self, **kwargs: Any) -> str: if self.doc_as_attr: return '' -- cgit v1.2.3