Hello community,
here is the log from the commit of package python-sphinx-autodoc-typehints for
openSUSE:Factory checked in at 2020-03-10 14:05:52
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-sphinx-autodoc-typehints (Old)
and /work/SRC/openSUSE:Factory/.python-sphinx-autodoc-typehints.new.26092
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-sphinx-autodoc-typehints"
Tue Mar 10 14:05:52 2020 rev:5 rq:782837 version:1.10.3
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-sphinx-autodoc-typehints/python-sphinx-autodoc-typehints.changes
2019-09-17 13:39:14.165825381 +0200
+++
/work/SRC/openSUSE:Factory/.python-sphinx-autodoc-typehints.new.26092/python-sphinx-autodoc-typehints.changes
2020-03-10 14:05:52.903400813 +0100
@@ -1,0 +2,22 @@
+Fri Mar 6 14:50:12 UTC 2020 - [email protected]
+
+- version update to 1.10.3
+ * Fixed ``TypeError`` (or wrong rendered class name) when an annotation is a
generic class that has
+ a ``name`` property
+ * Fixed inner classes missing their parent class name(s) when rendered
+ * Fixed ``KeyError`` when encountering mocked annotations
(``autodoc_mock_imports``)
+ * Rewrote the annotation formatting logic (fixes Python 3.5.2 compatibility
regressions and an
+ ``AttributeError`` regression introduced in v1.9.0)
+ * Fixed decorator classes not being processed as classes
+ * Added support for typing_extensions_
+ * Added the ``typehints_document_rtype`` option (PR by Simon-Martin Schröder)
+ * Fixed metaclasses as annotations causing ``TypeError``
+ * Fixed rendering of ``typing.Literal``
+ * Fixed OSError when generating docs for SQLAlchemy mapped classes
+ * Fixed unparametrized generic classes being rendered with their type
parameters
+ (e.g. ``Dict[~KT, ~VT]``)
+- added patches
+ fix use object.inv which comes with python-doc
+ + python-sphinx-autodoc-typehints-system-object.inv.patch
+
+-------------------------------------------------------------------
Old:
----
sphinx-autodoc-typehints-1.8.0.tar.gz
New:
----
python-sphinx-autodoc-typehints-system-object.inv.patch
sphinx-autodoc-typehints-1.10.3.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-sphinx-autodoc-typehints.spec ++++++
--- /var/tmp/diff_new_pack.pM4fJF/_old 2020-03-10 14:05:53.627401165 +0100
+++ /var/tmp/diff_new_pack.pM4fJF/_new 2020-03-10 14:05:53.627401165 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-sphinx-autodoc-typehints
#
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -19,13 +19,15 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-sphinx-autodoc-typehints
-Version: 1.8.0
+Version: 1.10.3
Release: 0
Summary: Type hints (PEP 484) support for the Sphinx autodoc extension
License: MIT
Group: Development/Languages/Python
URL: https://github.com/agronholm/sphinx-autodoc-typehints
Source:
https://files.pythonhosted.org/packages/source/s/sphinx-autodoc-typehints/sphinx-autodoc-typehints-%{version}.tar.gz
+# use object.inv which comes with python-doc; TODO more elegant solution
+Patch0: python-sphinx-autodoc-typehints-system-object.inv.patch
BuildRequires: %{python_module setuptools >= 36.2.7}
BuildRequires: %{python_module setuptools_scm >= 1.7.0}
BuildRequires: fdupes
@@ -35,8 +37,10 @@
BuildArch: noarch
# SECTION tests
BuildRequires: %{python_module Sphinx >= 1.7}
+BuildRequires: %{python_module doc}
BuildRequires: %{python_module pathlib}
BuildRequires: %{python_module pytest}
+BuildRequires: %{python_module sphobjinv}
BuildRequires: %{python_module typing_extensions}
# /SECTION
%python_subpackages
@@ -47,6 +51,7 @@
%prep
%setup -q -n sphinx-autodoc-typehints-%{version}
+%patch0 -p1
%build
%python_build
++++++ python-sphinx-autodoc-typehints-system-object.inv.patch ++++++
Index: sphinx-autodoc-typehints-1.10.3/tests/conftest.py
===================================================================
--- sphinx-autodoc-typehints-1.10.3.orig/tests/conftest.py 2019-11-11
20:10:08.000000000 +0100
+++ sphinx-autodoc-typehints-1.10.3/tests/conftest.py 2020-03-06
15:49:28.411760937 +0100
@@ -13,10 +13,8 @@ collect_ignore = ['roots']
@pytest.fixture(scope='session')
def inv(pytestconfig):
- cache_path =
'python{v.major}.{v.minor}/objects.inv'.format(v=sys.version_info)
- inv_dict = pytestconfig.cache.get(cache_path, None)
- if inv_dict is not None:
- return Inventory(inv_dict)
+ inv_dict = '/usr/share/doc/packages/python3/html/objects.inv'
+ return Inventory(inv_dict)
print("Downloading objects.inv")
url =
'https://docs.python.org/{v.major}.{v.minor}/objects.inv'.format(v=sys.version_info)
++++++ sphinx-autodoc-typehints-1.8.0.tar.gz ->
sphinx-autodoc-typehints-1.10.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sphinx-autodoc-typehints-1.8.0/.gitignore
new/sphinx-autodoc-typehints-1.10.3/.gitignore
--- old/sphinx-autodoc-typehints-1.8.0/.gitignore 2019-09-12
08:34:03.000000000 +0200
+++ new/sphinx-autodoc-typehints-1.10.3/.gitignore 2019-11-11
20:10:08.000000000 +0100
@@ -10,3 +10,4 @@
__pycache__/
dist/
build/
+.vscode/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sphinx-autodoc-typehints-1.8.0/.travis.yml
new/sphinx-autodoc-typehints-1.10.3/.travis.yml
--- old/sphinx-autodoc-typehints-1.8.0/.travis.yml 2019-09-12
08:34:03.000000000 +0200
+++ new/sphinx-autodoc-typehints-1.10.3/.travis.yml 2019-11-11
20:10:08.000000000 +0100
@@ -15,12 +15,17 @@
- stage: test
env: TOXENV=py35
- python: "3.5"
+ python: "3.5.2"
after_success: &after_success
- pip install coveralls
- coveralls
- stage: test
+ env: TOXENV=py35
+ python: "3.5"
+ after_success: *after_success
+
+ - stage: test
env: TOXENV=py36
python: "3.6"
after_success: *after_success
@@ -32,7 +37,7 @@
- stage: test
env: TOXENV=py38
- python: "3.8-dev"
+ python: "3.8"
after_success: *after_success
- stage: deploy to pypi
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sphinx-autodoc-typehints-1.8.0/CHANGELOG.rst
new/sphinx-autodoc-typehints-1.10.3/CHANGELOG.rst
--- old/sphinx-autodoc-typehints-1.8.0/CHANGELOG.rst 2019-09-12
08:34:03.000000000 +0200
+++ new/sphinx-autodoc-typehints-1.10.3/CHANGELOG.rst 2019-11-11
20:10:08.000000000 +0100
@@ -1,3 +1,44 @@
+1.10.3
+======
+
+* Fixed ``TypeError`` (or wrong rendered class name) when an annotation is a
generic class that has
+ a ``name`` property
+
+
+1.10.2
+======
+
+* Fixed inner classes missing their parent class name(s) when rendered
+
+
+1.10.1
+======
+
+* Fixed ``KeyError`` when encountering mocked annotations
(``autodoc_mock_imports``)
+
+
+1.10.0
+======
+
+* Rewrote the annotation formatting logic (fixes Python 3.5.2 compatibility
regressions and an
+ ``AttributeError`` regression introduced in v1.9.0)
+* Fixed decorator classes not being processed as classes
+
+
+1.9.0
+=====
+
+* Added support for typing_extensions_
+* Added the ``typehints_document_rtype`` option (PR by Simon-Martin Schröder)
+* Fixed metaclasses as annotations causing ``TypeError``
+* Fixed rendering of ``typing.Literal``
+* Fixed OSError when generating docs for SQLAlchemy mapped classes
+* Fixed unparametrized generic classes being rendered with their type
parameters
+ (e.g. ``Dict[~KT, ~VT]``)
+
+.. _typing_extensions: https://pypi.org/project/typing-extensions/
+
+
1.8.0
=====
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sphinx-autodoc-typehints-1.8.0/PKG-INFO
new/sphinx-autodoc-typehints-1.10.3/PKG-INFO
--- old/sphinx-autodoc-typehints-1.8.0/PKG-INFO 2019-09-12 08:34:19.000000000
+0200
+++ new/sphinx-autodoc-typehints-1.10.3/PKG-INFO 2019-11-11
20:10:25.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: sphinx-autodoc-typehints
-Version: 1.8.0
+Version: 1.10.3
Summary: Type hints (PEP 484) support for the Sphinx autodoc extension
Home-page: UNKNOWN
Author: Alex Grönholm
@@ -74,6 +74,8 @@
* ``always_document_param_types`` (default: ``False``): If ``False``,
do not add type info for
undocumented parameters. If ``True``, add stub documentation for
undocumented parameters to
be able to add type info.
+ * ``typehints_document_rtype`` (default: ``True``): If ``False``,
never add an ``:rtype:`` directive.
+ If ``True``, add the ``:rtype:`` directive if no existing
``:rtype:`` is found.
How it works
@@ -153,5 +155,5 @@
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Requires-Python: >=3.5.2
-Provides-Extra: type_comments
Provides-Extra: test
+Provides-Extra: type_comments
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sphinx-autodoc-typehints-1.8.0/README.rst
new/sphinx-autodoc-typehints-1.10.3/README.rst
--- old/sphinx-autodoc-typehints-1.8.0/README.rst 2019-09-12
08:34:03.000000000 +0200
+++ new/sphinx-autodoc-typehints-1.10.3/README.rst 2019-11-11
20:10:08.000000000 +0100
@@ -63,6 +63,8 @@
* ``always_document_param_types`` (default: ``False``): If ``False``, do not
add type info for
undocumented parameters. If ``True``, add stub documentation for
undocumented parameters to
be able to add type info.
+* ``typehints_document_rtype`` (default: ``True``): If ``False``, never add an
``:rtype:`` directive.
+ If ``True``, add the ``:rtype:`` directive if no existing ``:rtype:`` is
found.
How it works
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sphinx-autodoc-typehints-1.8.0/setup.cfg
new/sphinx-autodoc-typehints-1.10.3/setup.cfg
--- old/sphinx-autodoc-typehints-1.8.0/setup.cfg 2019-09-12
08:34:19.000000000 +0200
+++ new/sphinx-autodoc-typehints-1.10.3/setup.cfg 2019-11-11
20:10:25.000000000 +0100
@@ -33,6 +33,7 @@
pytest >= 3.1.0
typing_extensions >= 3.5
dataclasses; python_version == "3.6"
+ sphobjinv >= 2.0
type_comments =
typed_ast >= 1.4.0; python_version < "3.8"
@@ -40,6 +41,7 @@
max-line-length = 99
[tool:pytest]
+addopts = -rsx --tb=short
testpaths = tests
[egg_info]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/sphinx-autodoc-typehints-1.8.0/sphinx_autodoc_typehints.egg-info/PKG-INFO
new/sphinx-autodoc-typehints-1.10.3/sphinx_autodoc_typehints.egg-info/PKG-INFO
---
old/sphinx-autodoc-typehints-1.8.0/sphinx_autodoc_typehints.egg-info/PKG-INFO
2019-09-12 08:34:19.000000000 +0200
+++
new/sphinx-autodoc-typehints-1.10.3/sphinx_autodoc_typehints.egg-info/PKG-INFO
2019-11-11 20:10:25.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: sphinx-autodoc-typehints
-Version: 1.8.0
+Version: 1.10.3
Summary: Type hints (PEP 484) support for the Sphinx autodoc extension
Home-page: UNKNOWN
Author: Alex Grönholm
@@ -74,6 +74,8 @@
* ``always_document_param_types`` (default: ``False``): If ``False``,
do not add type info for
undocumented parameters. If ``True``, add stub documentation for
undocumented parameters to
be able to add type info.
+ * ``typehints_document_rtype`` (default: ``True``): If ``False``,
never add an ``:rtype:`` directive.
+ If ``True``, add the ``:rtype:`` directive if no existing
``:rtype:`` is found.
How it works
@@ -153,5 +155,5 @@
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Requires-Python: >=3.5.2
-Provides-Extra: type_comments
Provides-Extra: test
+Provides-Extra: type_comments
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/sphinx-autodoc-typehints-1.8.0/sphinx_autodoc_typehints.egg-info/requires.txt
new/sphinx-autodoc-typehints-1.10.3/sphinx_autodoc_typehints.egg-info/requires.txt
---
old/sphinx-autodoc-typehints-1.8.0/sphinx_autodoc_typehints.egg-info/requires.txt
2019-09-12 08:34:19.000000000 +0200
+++
new/sphinx-autodoc-typehints-1.10.3/sphinx_autodoc_typehints.egg-info/requires.txt
2019-11-11 20:10:25.000000000 +0100
@@ -3,6 +3,7 @@
[test]
pytest>=3.1.0
typing_extensions>=3.5
+sphobjinv>=2.0
[test:python_version == "3.6"]
dataclasses
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/sphinx-autodoc-typehints-1.8.0/sphinx_autodoc_typehints.py
new/sphinx-autodoc-typehints-1.10.3/sphinx_autodoc_typehints.py
--- old/sphinx-autodoc-typehints-1.8.0/sphinx_autodoc_typehints.py
2019-09-12 08:34:03.000000000 +0200
+++ new/sphinx-autodoc-typehints-1.10.3/sphinx_autodoc_typehints.py
2019-11-11 20:10:08.000000000 +0100
@@ -2,145 +2,156 @@
import sys
import textwrap
import typing
-from typing import get_type_hints, TypeVar, Any, AnyStr, Generic, Union
+from typing import get_type_hints, TypeVar, Any, AnyStr, Tuple
from sphinx.util import logging
from sphinx.util.inspect import Signature
-try:
- from typing_extensions import Protocol
-except ImportError:
- Protocol = None
-
logger = logging.getLogger(__name__)
-pydata_annotations = {'Any', 'AnyStr', 'Callable', 'ClassVar', 'NoReturn',
'Optional', 'Tuple',
- 'Union'}
+pydata_annotations = {'Any', 'AnyStr', 'Callable', 'ClassVar', 'Literal',
'NoReturn', 'Optional',
+ 'Tuple', 'Union'}
-def format_annotation(annotation, fully_qualified=False):
- if inspect.isclass(annotation) and annotation.__module__ == 'builtins':
- if annotation.__qualname__ == 'NoneType':
- return '``None``'
+def get_annotation_module(annotation) -> str:
+ # Special cases
+ if annotation is None:
+ return 'builtins'
+
+ if hasattr(annotation, '__module__'):
+ return annotation.__module__
+
+ if hasattr(annotation, '__origin__'):
+ return annotation.__origin__.__module__
+
+ raise ValueError('Cannot determine the module of {}'.format(annotation))
+
+
+def get_annotation_class_name(annotation, module: str) -> str:
+ # Special cases
+ if annotation is None:
+ return 'None'
+ elif annotation is Any:
+ return 'Any'
+ elif annotation is AnyStr:
+ return 'AnyStr'
+ elif inspect.isfunction(annotation) and hasattr(annotation,
'__supertype__'):
+ return 'NewType'
+
+ if getattr(annotation, '__qualname__', None):
+ return annotation.__qualname__
+ elif getattr(annotation, '_name', None): # Required for generic aliases
on Python 3.7+
+ return annotation._name
+ elif getattr(annotation, 'name', None) and module in ('typing',
'typing_extensions'):
+ # Required for at least Pattern and Match
+ return annotation.name
+
+ origin = getattr(annotation, '__origin__', None)
+ if origin:
+ if getattr(origin, '__qualname__', None): # Required for Protocol
subclasses
+ return origin.__qualname__
+ elif getattr(origin, '_name', None): # Required for Union on Python
3.7+
+ return origin._name
else:
- return ':py:class:`{}`'.format(annotation.__qualname__)
+ return origin.__class__.__qualname__.lstrip('_') # Required for
Union on Python < 3.7
- annotation_cls = annotation if inspect.isclass(annotation) else
type(annotation)
- if annotation_cls.__module__ == 'typing':
- class_name = str(annotation).split('[')[0].split('.')[-1]
- params = None
- module = 'typing'
- extra = ''
+ annotation_cls = annotation if inspect.isclass(annotation) else
annotation.__class__
+ return annotation_cls.__qualname__.lstrip('_')
+
+
+def get_annotation_args(annotation, module: str, class_name: str) -> Tuple:
+ try:
+ original = getattr(sys.modules[module], class_name)
+ except (KeyError, AttributeError):
+ pass
+ else:
+ if annotation is original:
+ return () # This is the original, unparametrized type
- origin = getattr(annotation, '__origin__', None)
- if inspect.isclass(origin):
- annotation_cls = annotation.__origin__
- try:
- mro = annotation_cls.mro()
- if Generic in mro or (Protocol and Protocol in mro):
- module = annotation_cls.__module__
- except TypeError:
- pass # annotation_cls was either the "type" object or
typing.Type
-
- if annotation is Any:
- return ':py:data:`{}typing.Any`'.format("" if fully_qualified else
"~")
- elif annotation is AnyStr:
- return ':py:data:`{}typing.AnyStr`'.format("" if fully_qualified
else "~")
- elif isinstance(annotation, TypeVar):
- return '\\%r' % annotation
- elif (annotation is Union or getattr(annotation, '__origin__', None)
is Union or
- hasattr(annotation, '__union_params__')):
- if hasattr(annotation, '__union_params__'):
- params = annotation.__union_params__
- elif hasattr(annotation, '__args__'):
- params = annotation.__args__
-
- if params and len(params) == 2 and (hasattr(params[1],
'__qualname__') and
- params[1].__qualname__ ==
'NoneType'):
- class_name = 'Optional'
- params = (params[0],)
- elif annotation_cls.__qualname__ == 'Tuple' and hasattr(annotation,
'__tuple_params__'):
- params = annotation.__tuple_params__
- if annotation.__tuple_use_ellipsis__:
- params += (Ellipsis,)
- elif annotation_cls.__qualname__ == 'Callable':
- arg_annotations = result_annotation = None
- if hasattr(annotation, '__result__'):
- arg_annotations = annotation.__args__
- result_annotation = annotation.__result__
- elif getattr(annotation, '__args__', None):
- arg_annotations = annotation.__args__[:-1]
- result_annotation = annotation.__args__[-1]
-
- if arg_annotations in (Ellipsis, (Ellipsis,)):
- params = [Ellipsis, result_annotation]
- elif arg_annotations is not None:
- params = [
- '\\[{}]'.format(
- ', '.join(
- format_annotation(param, fully_qualified)
- for param in arg_annotations)),
- result_annotation
- ]
- elif str(annotation).startswith('typing.ClassVar[') and
hasattr(annotation, '__type__'):
- # < py3.7
- params = (annotation.__type__,)
- elif hasattr(annotation, 'type_var'):
- # Type alias
- class_name = annotation.name
- params = (annotation.type_var,)
- elif getattr(annotation, '__args__', None) is not None:
- params = annotation.__args__
- elif hasattr(annotation, '__parameters__'):
- params = annotation.__parameters__
-
- if params:
- extra = '\\[{}]'.format(', '.join(
- format_annotation(param, fully_qualified) for param in params))
-
- return '{prefix}`{qualify}{module}.{name}`{extra}'.format(
- prefix=':py:data:' if class_name in pydata_annotations else
':py:class:',
- qualify="" if fully_qualified else "~",
- module=module,
- name=class_name,
- extra=extra
- )
+ # Special cases
+ if class_name in ('Pattern', 'Match') and hasattr(annotation, 'type_var'):
# Python < 3.7
+ return annotation.type_var,
+ elif class_name == 'Callable' and hasattr(annotation, '__result__'): #
Python < 3.5.3
+ argtypes = (Ellipsis,) if annotation.__args__ is Ellipsis else
annotation.__args__
+ return argtypes + (annotation.__result__,)
+ elif class_name == 'Union' and hasattr(annotation, '__union_params__'): #
Union on Python 3.5
+ return annotation.__union_params__
+ elif class_name == 'Tuple' and hasattr(annotation, '__tuple_params__'): #
Tuple on Python 3.5
+ params = annotation.__tuple_params__
+ if getattr(annotation, '__tuple_use_ellipsis__', False):
+ params += (Ellipsis,)
+
+ return params
+ elif class_name == 'ClassVar' and hasattr(annotation, '__type__'): #
ClassVar on Python < 3.7
+ return annotation.__type__,
+ elif class_name == 'NewType' and hasattr(annotation, '__supertype__'):
+ return annotation.__supertype__,
+ elif class_name == 'Literal' and hasattr(annotation, '__values__'):
+ return annotation.__values__
+ elif class_name == 'Generic':
+ return annotation.__parameters__
+
+ return getattr(annotation, '__args__', ())
+
+
+def format_annotation(annotation, fully_qualified: bool = False) -> str:
+ # Special cases
+ if annotation is None or annotation is type(None): # noqa: E721
+ return '``None``'
elif annotation is Ellipsis:
return '...'
- elif (inspect.isfunction(annotation) and annotation.__module__ == 'typing'
and
- hasattr(annotation, '__name__') and hasattr(annotation,
'__supertype__')):
- return ':py:func:`{qualify}typing.NewType`\\(:py:data:`~{name}`,
{extra})'.format(
- qualify="" if fully_qualified else "~",
- name=annotation.__name__,
- extra=format_annotation(annotation.__supertype__, fully_qualified),
- )
- elif inspect.isclass(annotation) or inspect.isclass(getattr(annotation,
'__origin__', None)):
- if not inspect.isclass(annotation):
- annotation_cls = annotation.__origin__
-
- extra = ''
- mro = annotation_cls.mro()
- if Generic in mro or (Protocol and Protocol in mro):
- params = (getattr(annotation, '__parameters__', None) or
- getattr(annotation, '__args__', None))
- if params:
- extra = '\\[{}]'.format(', '.join(
- format_annotation(param, fully_qualified) for param in
params))
-
- return ':py:class:`{qualify}{module}.{name}`{extra}'.format(
- qualify="" if fully_qualified else "~",
- module=annotation.__module__,
- name=annotation_cls.__qualname__,
- extra=extra
- )
- return str(annotation)
+ # Type variables are also handled specially
+ try:
+ if isinstance(annotation, TypeVar) and annotation is not AnyStr:
+ return '\\' + repr(annotation)
+ except TypeError:
+ pass
+
+ try:
+ module = get_annotation_module(annotation)
+ class_name = get_annotation_class_name(annotation, module)
+ args = get_annotation_args(annotation, module, class_name)
+ except ValueError:
+ return str(annotation)
+
+ # Redirect all typing_extensions types to the stdlib typing module
+ if module == 'typing_extensions':
+ module = 'typing'
+
+ full_name = (module + '.' + class_name) if module != 'builtins' else
class_name
+ prefix = '' if fully_qualified or full_name == class_name else '~'
+ role = 'data' if class_name in pydata_annotations else 'class'
+ args_format = '\\[{}]'
+ formatted_args = ''
+
+ # Some types require special handling
+ if full_name == 'typing.NewType':
+ args_format = '\\(:py:data:`~{name}`, {{}})'.format(prefix=prefix,
+
name=annotation.__name__)
+ role = 'func'
+ elif full_name == 'typing.Union' and len(args) == 2 and type(None) in args:
+ full_name = 'typing.Optional'
+ args = tuple(x for x in args if x is not type(None)) # noqa: E721
+ elif full_name == 'typing.Callable' and args and args[0] is not ...:
+ formatted_args = '\\[\\[' + ', '.join(format_annotation(arg) for arg
in args[:-1]) + ']'
+ formatted_args += ', ' + format_annotation(args[-1]) + ']'
+ elif full_name == 'typing.Literal':
+ formatted_args = '\\[' + ', '.join(repr(arg) for arg in args) + ']'
+
+ if args and not formatted_args:
+ formatted_args = args_format.format(', '.join(format_annotation(arg,
fully_qualified)
+ for arg in args))
+
+ return ':py:{role}:`{prefix}{full_name}`{formatted_args}'.format(
+ role=role, prefix=prefix, full_name=full_name,
formatted_args=formatted_args)
def process_signature(app, what: str, name: str, obj, options, signature,
return_annotation):
if not callable(obj):
return
- if what in ('class', 'exception'):
+ original_obj = obj
+ if inspect.isclass(obj):
obj = getattr(obj, '__init__', getattr(obj, '__new__', None))
if not getattr(obj, '__annotations__', None):
@@ -160,7 +171,7 @@
return
if parameters:
- if what in ('class', 'exception'):
+ if inspect.isclass(original_obj):
del parameters[0]
elif what == 'method':
outer = inspect.getmodule(obj)
@@ -245,7 +256,7 @@
try:
obj_ast = ast.parse(textwrap.dedent(inspect.getsource(obj)),
**parse_kwargs)
- except TypeError:
+ except (OSError, TypeError):
return {}
obj_ast = _one_child(obj_ast)
@@ -339,11 +350,12 @@
def process_docstring(app, what, name, obj, options, lines):
+ original_obj = obj
if isinstance(obj, property):
obj = obj.fget
if callable(obj):
- if what in ('class', 'exception'):
+ if inspect.isclass(obj):
obj = getattr(obj, '__init__')
obj = inspect.unwrap(obj)
@@ -376,7 +388,7 @@
':type {}: {}'.format(argname, formatted_annotation)
)
- if 'return' in type_hints and what not in ('class', 'exception'):
+ if 'return' in type_hints and not inspect.isclass(original_obj):
formatted_annotation = format_annotation(
type_hints['return'],
fully_qualified=app.config.typehints_fully_qualified)
@@ -388,7 +400,7 @@
elif line.startswith(':return:') or
line.startswith(':returns:'):
insert_index = i
- if insert_index is not None:
+ if insert_index is not None and
app.config.typehints_document_rtype:
if insert_index == len(lines):
# Ensure that :rtype: doesn't get joined with a paragraph
of text, which
# prevents it being interpreted.
@@ -407,6 +419,7 @@
app.add_config_value('set_type_checking_flag', False, 'html')
app.add_config_value('always_document_param_types', False, 'html')
app.add_config_value('typehints_fully_qualified', False, 'env')
+ app.add_config_value('typehints_document_rtype', True, 'env')
app.connect('builder-inited', builder_ready)
app.connect('autodoc-process-signature', process_signature)
app.connect('autodoc-process-docstring', process_docstring)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sphinx-autodoc-typehints-1.8.0/tests/conftest.py
new/sphinx-autodoc-typehints-1.10.3/tests/conftest.py
--- old/sphinx-autodoc-typehints-1.8.0/tests/conftest.py 2019-09-12
08:34:03.000000000 +0200
+++ new/sphinx-autodoc-typehints-1.10.3/tests/conftest.py 2019-11-11
20:10:08.000000000 +0100
@@ -1,14 +1,30 @@
import os
+import sys
import pathlib
import shutil
import pytest
from sphinx.testing.path import path
+from sphobjinv import Inventory
pytest_plugins = 'sphinx.testing.fixtures'
collect_ignore = ['roots']
[email protected](scope='session')
+def inv(pytestconfig):
+ cache_path =
'python{v.major}.{v.minor}/objects.inv'.format(v=sys.version_info)
+ inv_dict = pytestconfig.cache.get(cache_path, None)
+ if inv_dict is not None:
+ return Inventory(inv_dict)
+
+ print("Downloading objects.inv")
+ url =
'https://docs.python.org/{v.major}.{v.minor}/objects.inv'.format(v=sys.version_info)
+ inv = Inventory(url=url)
+ pytestconfig.cache.set(cache_path, inv.json_dict())
+ return inv
+
+
@pytest.fixture(autouse=True)
def remove_sphinx_projects(sphinx_test_tempdir):
# Remove any directory which appears to be a Sphinx project from
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/sphinx-autodoc-typehints-1.8.0/tests/roots/test-dummy/dummy_module.py
new/sphinx-autodoc-typehints-1.10.3/tests/roots/test-dummy/dummy_module.py
--- old/sphinx-autodoc-typehints-1.8.0/tests/roots/test-dummy/dummy_module.py
2019-09-12 08:34:03.000000000 +0200
+++ new/sphinx-autodoc-typehints-1.10.3/tests/roots/test-dummy/dummy_module.py
2019-11-11 20:10:08.000000000 +0100
@@ -1,4 +1,5 @@
import typing
+from mailbox import Mailbox
from typing import Callable, Union
try:
@@ -237,3 +238,22 @@
@dataclass
class DataClass:
"""Class docstring."""
+
+
+class Decorator:
+ """
+ Initializer docstring.
+
+ :param func: function
+ """
+
+ def __init__(self, func: Callable[[int, str], str]):
+ pass
+
+
+def mocked_import(x: Mailbox):
+ """
+ A docstring.
+
+ :param x: function
+ """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/sphinx-autodoc-typehints-1.8.0/tests/roots/test-dummy/index.rst
new/sphinx-autodoc-typehints-1.10.3/tests/roots/test-dummy/index.rst
--- old/sphinx-autodoc-typehints-1.8.0/tests/roots/test-dummy/index.rst
2019-09-12 08:34:03.000000000 +0200
+++ new/sphinx-autodoc-typehints-1.10.3/tests/roots/test-dummy/index.rst
2019-11-11 20:10:08.000000000 +0100
@@ -32,3 +32,7 @@
.. autoclass:: dummy_module.DataClass
:undoc-members:
:special-members: __init__
+
+.. autodecorator:: dummy_module.Decorator
+
+.. autofunction:: dummy_module.mocked_import
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/sphinx-autodoc-typehints-1.8.0/tests/test_sphinx_autodoc_typehints.py
new/sphinx-autodoc-typehints-1.10.3/tests/test_sphinx_autodoc_typehints.py
--- old/sphinx-autodoc-typehints-1.8.0/tests/test_sphinx_autodoc_typehints.py
2019-09-12 08:34:03.000000000 +0200
+++ new/sphinx-autodoc-typehints-1.10.3/tests/test_sphinx_autodoc_typehints.py
2019-11-11 20:10:08.000000000 +0100
@@ -1,24 +1,23 @@
import pathlib
-import pytest
+import re
import sys
import textwrap
+import typing
from typing import (
- Any, AnyStr, Callable, Dict, Generic, Mapping, NewType, Optional, Pattern,
- Tuple, TypeVar, Union, Type)
-
-from typing_extensions import Protocol
+ Any, AnyStr, Callable, Dict, Generic, Mapping, NewType, Optional, Pattern,
Match, Tuple,
+ TypeVar, Union, Type)
-from sphinx_autodoc_typehints import format_annotation, process_docstring
+import pytest
+import typing_extensions
-try:
- from typing import ClassVar # not available prior to Python 3.5.3
-except ImportError:
- ClassVar = None
+from sphinx_autodoc_typehints import (
+ format_annotation, process_docstring, get_annotation_module,
get_annotation_class_name,
+ get_annotation_args)
try:
- from typing import NoReturn # not available prior to Python 3.6.5
+ from typing import IO
except ImportError:
- NoReturn = None
+ from typing.io import IO
T = TypeVar('T')
U = TypeVar('U', covariant=True)
@@ -30,20 +29,24 @@
def get_type(self):
return type(self)
+ class Inner:
+ pass
+
class B(Generic[T]):
- pass
+ # This is set to make sure the correct class name ("B") is picked up
+ name = 'Foo'
class C(B[str]):
pass
-class D(Protocol):
+class D(typing_extensions.Protocol):
pass
-class E(Protocol[T]):
+class E(typing_extensions.Protocol[T]):
pass
@@ -51,26 +54,64 @@
__slots__ = ()
+class Metaclass(type):
+ pass
+
+
[email protected]('annotation, module, class_name, args', [
+ pytest.param(str, 'builtins', 'str', (), id='str'),
+ pytest.param(None, 'builtins', 'None', (), id='None'),
+ pytest.param(Any, 'typing', 'Any', (), id='Any'),
+ pytest.param(AnyStr, 'typing', 'AnyStr', (), id='AnyStr'),
+ pytest.param(Dict, 'typing', 'Dict', (), id='Dict'),
+ pytest.param(Dict[str, int], 'typing', 'Dict', (str, int),
id='Dict_parametrized'),
+ pytest.param(Dict[T, int], 'typing', 'Dict', (T, int), id='Dict_typevar'),
+ pytest.param(Tuple, 'typing', 'Tuple', (), id='Tuple'),
+ pytest.param(Tuple[str, int], 'typing', 'Tuple', (str, int),
id='Tuple_parametrized'),
+ pytest.param(Union[str, int], 'typing', 'Union', (str, int), id='Union'),
+ pytest.param(Callable, 'typing', 'Callable', (), id='Callable'),
+ pytest.param(Callable[..., str], 'typing', 'Callable', (..., str),
id='Callable_returntype'),
+ pytest.param(Callable[[int, str], str], 'typing', 'Callable', (int, str,
str),
+ id='Callable_all_types'),
+ pytest.param(Pattern, 'typing', 'Pattern', (), id='Pattern'),
+ pytest.param(Pattern[str], 'typing', 'Pattern', (str,),
id='Pattern_parametrized'),
+ pytest.param(Match, 'typing', 'Match', (), id='Match'),
+ pytest.param(Match[str], 'typing', 'Match', (str,),
id='Match_parametrized'),
+ pytest.param(IO, 'typing', 'IO', (), id='IO'),
+ pytest.param(W, 'typing', 'NewType', (str,), id='W'),
+ pytest.param(Metaclass, __name__, 'Metaclass', (), id='Metaclass'),
+ pytest.param(Slotted, __name__, 'Slotted', (), id='Slotted'),
+ pytest.param(A, __name__, 'A', (), id='A'),
+ pytest.param(B, __name__, 'B', (), id='B'),
+ pytest.param(C, __name__, 'C', (), id='C'),
+ pytest.param(D, __name__, 'D', (), id='D'),
+ pytest.param(E, __name__, 'E', (), id='E'),
+ pytest.param(E[int], __name__, 'E', (int,), id='E_parametrized'),
+ pytest.param(A.Inner, __name__, 'A.Inner', (), id='Inner')
+])
+def test_parse_annotation(annotation, module, class_name, args):
+ assert get_annotation_module(annotation) == module
+ assert get_annotation_class_name(annotation, module) == class_name
+ assert get_annotation_args(annotation, module, class_name) == args
+
+
@pytest.mark.parametrize('annotation, expected_result', [
(str, ':py:class:`str`'),
(int, ':py:class:`int`'),
(type(None), '``None``'),
- pytest.param(NoReturn, ':py:data:`~typing.NoReturn`',
- marks=[pytest.mark.skipif(NoReturn is None,
- reason='typing.NoReturn is not
available')]),
- pytest.param(ClassVar[str],
':py:data:`~typing.ClassVar`\\[:py:class:`str`]',
- marks=[pytest.mark.skipif(ClassVar is None,
- reason='typing.ClassVar is not
available')]),
+ (type, ':py:class:`type`'),
+ (Type, ':py:class:`~typing.Type`'),
+ (Type[A],
':py:class:`~typing.Type`\\[:py:class:`~%s.A`]' % __name__),
(Any, ':py:data:`~typing.Any`'),
(AnyStr, ':py:data:`~typing.AnyStr`'),
(Generic[T], ':py:class:`~typing.Generic`\\[\\~T]'),
- (Mapping, ':py:class:`~typing.Mapping`\\[\\~KT,
\\+VT_co]'),
+ (Mapping, ':py:class:`~typing.Mapping`'),
(Mapping[T, int], ':py:class:`~typing.Mapping`\\[\\~T,
:py:class:`int`]'),
(Mapping[str, V],
':py:class:`~typing.Mapping`\\[:py:class:`str`, \\-V]'),
(Mapping[T, U], ':py:class:`~typing.Mapping`\\[\\~T,
\\+U]'),
(Mapping[str, bool],
':py:class:`~typing.Mapping`\\[:py:class:`str`, '
':py:class:`bool`]'),
- (Dict, ':py:class:`~typing.Dict`\\[\\~KT,
\\~VT]'),
+ (Dict, ':py:class:`~typing.Dict`'),
(Dict[T, int], ':py:class:`~typing.Dict`\\[\\~T,
:py:class:`int`]'),
(Dict[str, V],
':py:class:`~typing.Dict`\\[:py:class:`str`, \\-V]'),
(Dict[T, U], ':py:class:`~typing.Dict`\\[\\~T, \\+U]'),
@@ -99,90 +140,60 @@
(Callable[[int, str], None],
':py:data:`~typing.Callable`\\[\\[:py:class:`int`, '
':py:class:`str`], ``None``]'),
(Callable[[T], T], ':py:data:`~typing.Callable`\\[\\[\\~T],
\\~T]'),
- (Pattern,
':py:class:`~typing.Pattern`\\[:py:data:`~typing.AnyStr`]'),
+ (Pattern, ':py:class:`~typing.Pattern`'),
(Pattern[str],
':py:class:`~typing.Pattern`\\[:py:class:`str`]'),
+ (IO, ':py:class:`~typing.IO`'),
+ (Metaclass, ':py:class:`~%s.Metaclass`' % __name__),
(A, ':py:class:`~%s.A`' % __name__),
- (B, ':py:class:`~%s.B`\\[\\~T]' % __name__),
+ (B, ':py:class:`~%s.B`' % __name__),
(B[int], ':py:class:`~%s.B`\\[:py:class:`int`]' %
__name__),
(C, ':py:class:`~%s.C`' % __name__),
(D, ':py:class:`~%s.D`' % __name__),
- (E, ':py:class:`~%s.E`\\[\\~T]' % __name__),
+ (E, ':py:class:`~%s.E`' % __name__),
(E[int], ':py:class:`~%s.E`\\[:py:class:`int`]' %
__name__),
(W,
':py:func:`~typing.NewType`\\(:py:data:`~W`, :py:class:`str`)')
])
-def test_format_annotation(annotation, expected_result):
+def test_format_annotation(inv, annotation, expected_result):
result = format_annotation(annotation)
assert result == expected_result
-
[email protected]('annotation, expected_result', [
- (str, ':py:class:`str`'),
- (int, ':py:class:`int`'),
- (type(None), '``None``'),
- pytest.param(NoReturn, ':py:data:`typing.NoReturn`',
- marks=[pytest.mark.skipif(NoReturn is None,
- reason='typing.NoReturn is not
available')]),
- pytest.param(ClassVar[str],
':py:data:`typing.ClassVar`\\[:py:class:`str`]',
- marks=[pytest.mark.skipif(ClassVar is None,
- reason='typing.ClassVar is not
available')]),
- (Any, ':py:data:`typing.Any`'),
- (AnyStr, ':py:data:`typing.AnyStr`'),
- (Generic[T], ':py:class:`typing.Generic`\\[\\~T]'),
- (Mapping, ':py:class:`typing.Mapping`\\[\\~KT,
\\+VT_co]'),
- (Mapping[T, int], ':py:class:`typing.Mapping`\\[\\~T,
:py:class:`int`]'),
- (Mapping[str, V],
':py:class:`typing.Mapping`\\[:py:class:`str`, \\-V]'),
- (Mapping[T, U], ':py:class:`typing.Mapping`\\[\\~T,
\\+U]'),
- (Mapping[str, bool],
':py:class:`typing.Mapping`\\[:py:class:`str`, '
- ':py:class:`bool`]'),
- (Dict, ':py:class:`typing.Dict`\\[\\~KT, \\~VT]'),
- (Dict[T, int], ':py:class:`typing.Dict`\\[\\~T,
:py:class:`int`]'),
- (Dict[str, V],
':py:class:`typing.Dict`\\[:py:class:`str`, \\-V]'),
- (Dict[T, U], ':py:class:`typing.Dict`\\[\\~T, \\+U]'),
- (Dict[str, bool],
':py:class:`typing.Dict`\\[:py:class:`str`, '
- ':py:class:`bool`]'),
- (Tuple, ':py:data:`typing.Tuple`'),
- (Tuple[str, bool],
':py:data:`typing.Tuple`\\[:py:class:`str`, '
- ':py:class:`bool`]'),
- (Tuple[int, int, int],
':py:data:`typing.Tuple`\\[:py:class:`int`, '
- ':py:class:`int`, :py:class:`int`]'),
- (Tuple[str, ...],
':py:data:`typing.Tuple`\\[:py:class:`str`, ...]'),
- (Union, ':py:data:`typing.Union`'),
- (Union[str, bool],
':py:data:`typing.Union`\\[:py:class:`str`, '
- ':py:class:`bool`]'),
- (Optional[str],
':py:data:`typing.Optional`\\[:py:class:`str`]'),
- (Callable, ':py:data:`typing.Callable`'),
- (Callable[..., int], ':py:data:`typing.Callable`\\[...,
:py:class:`int`]'),
- (Callable[[int], int],
':py:data:`typing.Callable`\\[\\[:py:class:`int`], '
- ':py:class:`int`]'),
- (Callable[[int, str], bool],
':py:data:`typing.Callable`\\[\\[:py:class:`int`, '
- ':py:class:`str`], :py:class:`bool`]'),
- (Callable[[int, str], None],
':py:data:`typing.Callable`\\[\\[:py:class:`int`, '
- ':py:class:`str`], ``None``]'),
- (Callable[[T], T], ':py:data:`typing.Callable`\\[\\[\\~T],
\\~T]'),
- (Pattern,
':py:class:`typing.Pattern`\\[:py:data:`typing.AnyStr`]'),
- (Pattern[str],
':py:class:`typing.Pattern`\\[:py:class:`str`]'),
- (A, ':py:class:`%s.A`' % __name__),
- (B, ':py:class:`%s.B`\\[\\~T]' % __name__),
- (B[int], ':py:class:`%s.B`\\[:py:class:`int`]' %
__name__),
- (C, ':py:class:`%s.C`' % __name__),
- (D, ':py:class:`%s.D`' % __name__),
- (E, ':py:class:`%s.E`\\[\\~T]' % __name__),
- (E[int], ':py:class:`%s.E`\\[:py:class:`int`]' %
__name__),
- (W,
':py:func:`typing.NewType`\\(:py:data:`~W`, :py:class:`str`)')
+ # Test with the "fully_qualified" flag turned on
+ if 'typing' in expected_result or __name__ in expected_result:
+ expected_result = expected_result.replace('~typing', 'typing')
+ expected_result = expected_result.replace('~' + __name__, __name__)
+ assert format_annotation(annotation, fully_qualified=True) ==
expected_result
+
+ # Test for the correct role (class vs data) using the official Sphinx
inventory
+ if 'typing' in expected_result:
+ m = re.match('^:py:(?P<role>class|data|func):`~(?P<name>[^`]+)`',
result)
+ assert m, 'No match'
+ name = m.group('name')
+ expected_role = next((o.role for o in inv.objects if o.name == name),
None)
+ if expected_role:
+ if expected_role == 'function':
+ expected_role = 'func'
+
+ assert m.group('role') == expected_role
+
+
[email protected]('library', [typing, typing_extensions],
+ ids=['typing', 'typing_extensions'])
[email protected]('annotation, params, expected_result', [
+ ('ClassVar', int, ":py:data:`~typing.ClassVar`\\[:py:class:`int`]"),
+ ('NoReturn', None, ":py:data:`~typing.NoReturn`"),
+ ('Literal', ('a', 1), ":py:data:`~typing.Literal`\\['a', 1]"),
+ ('Type', None, ':py:class:`~typing.Type`'),
+ ('Type', (A,), ':py:class:`~typing.Type`\\[:py:class:`~%s.A`]' % __name__)
])
-def test_format_annotation_fully_qualified(annotation, expected_result):
- result = format_annotation(annotation, fully_qualified=True)
- assert result == expected_result
+def test_format_annotation_both_libs(inv, library, annotation, params,
expected_result):
+ try:
+ annotation_cls = getattr(library, annotation)
+ except AttributeError:
+ pytest.skip('{} not available in the {} module'.format(annotation,
library.__name__))
-
[email protected]('type_param, expected_result', [
- (None, ':py:class:`~typing.Type`\\[\\+CT'),
- (A, ':py:class:`~typing.Type`\\[:py:class:`~%s.A`]' % __name__)
-])
-def test_format_annotation_type(type_param, expected_result):
- annotation = Type[type_param] if type_param else Type
- result = format_annotation(annotation)
- assert result.startswith(expected_result)
+ ann = annotation_cls if params is None else annotation_cls[params]
+ result = format_annotation(ann)
+ assert result == expected_result
def test_process_docstring_slot_wrapper():
@@ -201,6 +212,7 @@
sys.path.insert(0, str(test_path))
app.config.always_document_param_types = always_document_param_types
+ app.config.autodoc_mock_imports = ['mailbox']
app.build()
assert 'build succeeded' in status.getvalue() # Build succeeded
@@ -209,14 +221,17 @@
warnings = warning.getvalue().strip()
assert 'Cannot resolve forward reference in type annotations of ' in
warnings, warnings
+ format_args = {}
if always_document_param_types:
- undoc_params = '''
-
- Parameters:
- **x** ("int") --'''
+ format_args['undoc_params'] = '\n\n Parameters:\n **x** ("int")
--'
+ else:
+ format_args['undoc_params'] = ""
+ if sys.version_info < (3, 6):
+ format_args['dataclass_docstring'] = ('Initialize self. See
help(type(self)) for '
+ 'accurate signature.')
else:
- undoc_params = ""
+ format_args['dataclass_docstring'] = 'Return type:\n "None"'
text_path = pathlib.Path(app.srcdir) / '_build' / 'text' / 'index.txt'
with text_path.open('r') as f:
@@ -469,16 +484,22 @@
Class docstring.
__init__()
- '''.format(undoc_params=undoc_params)).replace('–', '--')
- if sys.version_info < (3, 6):
- expected_contents += '''
- Initialize self. See help(type(self)) for accurate signature.
-'''
- else:
- expected_contents += '''
- Return type:
- "None"
-'''
+ {dataclass_docstring}
+
+ @dummy_module.Decorator(func)
+
+ Initializer docstring.
+
+ Parameters:
+ **func** ("Callable"[["int", "str"], "str"]) -- function
+
+ dummy_module.mocked_import(x)
+ A docstring.
+
+ Parameters:
+ **x** ("Mailbox") -- function
+ ''')
+ expected_contents =
expected_contents.format(**format_args).replace('–', '--')
assert text_contents == expected_contents