mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x'
This commit is contained in:
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@@ -37,12 +37,10 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- name: Set up Python ${{ matrix.python }} (deadsnakes)
|
||||
uses: deadsnakes/action@v1.0.0
|
||||
uses: deadsnakes/action@v2.0.1
|
||||
if: endsWith(matrix.python, '-dev')
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
env:
|
||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
|
||||
- name: Check Python version
|
||||
run: python --version
|
||||
- name: Install graphviz
|
||||
|
7
CHANGES
7
CHANGES
@@ -69,6 +69,8 @@ Deprecated
|
||||
|
||||
* The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()``
|
||||
* ``sphinx.ext.autodoc.DataDeclarationDocumenter``
|
||||
* ``sphinx.ext.autodoc.importer._getannotations()``
|
||||
* ``sphinx.pycode.ModuleAnalyzer.parse()``
|
||||
* ``sphinx.util.requests.is_ssl_error()``
|
||||
|
||||
Features added
|
||||
@@ -84,6 +86,7 @@ Features added
|
||||
value equal to None is set.
|
||||
* #8209: autodoc: Add ``:no-value:`` option to :rst:dir:`autoattribute` and
|
||||
:rst:dir:`autodata` directive to suppress the default value of the variable
|
||||
* #8460: autodoc: Support custom types defined by typing.NewType
|
||||
* #6914: Add a new event :event:`warn-missing-reference` to custom warning
|
||||
messages when failed to resolve a cross-reference
|
||||
* #6914: Emit a detailed warning when failed to resolve a ``:ref:`` reference
|
||||
@@ -101,7 +104,11 @@ Bugs fixed
|
||||
type annotated variables
|
||||
* #8443: autodoc: autoattribute directive can't create document for PEP-526
|
||||
based uninitalized variables
|
||||
* #8452: autodoc: autodoc_type_aliases doesn't work when autodoc_typehints is
|
||||
set to "description"
|
||||
* #8419: html search: Do not load ``language_data.js`` in non-search pages
|
||||
* #8454: graphviz: The layout option for graph and digraph directives don't work
|
||||
* #8437: Makefile: ``make clean`` with empty BUILDDIR is dangerous
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
@@ -66,6 +66,16 @@ The following is a list of deprecated interfaces.
|
||||
- 5.0
|
||||
- ``sphinx.ext.autodoc.DataDocumenter``
|
||||
|
||||
* - ``sphinx.ext.autodoc.importer._getannotations()``
|
||||
- 3.4
|
||||
- 4.0
|
||||
- ``sphinx.util.inspect.getannotations()``
|
||||
|
||||
* - ``sphinx.pycode.ModuleAnalyzer.parse()``
|
||||
- 3.4
|
||||
- 5.0
|
||||
- ``sphinx.pycode.ModuleAnalyzer.analyze()``
|
||||
|
||||
* - ``sphinx.util.requests.is_ssl_error()``
|
||||
- 3.4
|
||||
- 5.0
|
||||
|
@@ -573,7 +573,8 @@ class Documenter:
|
||||
else:
|
||||
return 'docstring of %s' % fullname
|
||||
|
||||
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
|
||||
def add_content(self, more_content: Optional[StringList], no_docstring: bool = False
|
||||
) -> None:
|
||||
"""Add content from docstrings, attribute documentation and user."""
|
||||
# set sourcename and add content from attribute documentation
|
||||
sourcename = self.get_sourcename()
|
||||
@@ -844,7 +845,7 @@ class Documenter:
|
||||
|
||||
return documenters
|
||||
|
||||
def generate(self, more_content: Any = None, real_modname: str = None,
|
||||
def generate(self, more_content: Optional[StringList] = None, real_modname: str = None,
|
||||
check_module: bool = False, all_members: bool = False) -> None:
|
||||
"""Generate reST for the object given by *self.name*, and possibly for
|
||||
its members.
|
||||
@@ -983,6 +984,10 @@ class ModuleDocumenter(Documenter):
|
||||
try:
|
||||
if not self.options.ignore_module_all:
|
||||
self.__all__ = inspect.getall(self.object)
|
||||
except AttributeError as exc:
|
||||
# __all__ raises an error.
|
||||
logger.warning(__('%s.__all__ raises an error. Ignored: %r'),
|
||||
(self.fullname, exc), type='autodoc')
|
||||
except ValueError as exc:
|
||||
# invalid __all__ found.
|
||||
logger.warning(__('__all__ should be a list of strings, not %r '
|
||||
@@ -1522,7 +1527,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
||||
for cls in self._signature_class.__mro__:
|
||||
try:
|
||||
analyzer = ModuleAnalyzer.for_module(cls.__module__)
|
||||
analyzer.parse()
|
||||
analyzer.analyze()
|
||||
qualname = '.'.join([cls.__qualname__, self._signature_method_name])
|
||||
if qualname in analyzer.overloads:
|
||||
return analyzer.overloads.get(qualname)
|
||||
@@ -1603,7 +1608,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
||||
tab_width = self.directive.state.document.settings.tab_width
|
||||
return [prepare_docstring(docstring, ignore, tab_width) for docstring in docstrings]
|
||||
|
||||
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
|
||||
def add_content(self, more_content: Optional[StringList], no_docstring: bool = False
|
||||
) -> None:
|
||||
if self.doc_as_attr:
|
||||
classname = safe_getattr(self.object, '__qualname__', None)
|
||||
if not classname:
|
||||
@@ -1623,7 +1629,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
||||
return
|
||||
super().document_members(all_members)
|
||||
|
||||
def generate(self, more_content: Any = None, real_modname: str = None,
|
||||
def generate(self, more_content: Optional[StringList] = None, real_modname: str = None,
|
||||
check_module: bool = False, all_members: bool = False) -> None:
|
||||
# Do not pass real_modname and use the name from the __module__
|
||||
# attribute of the class.
|
||||
@@ -1651,7 +1657,25 @@ class ExceptionDocumenter(ClassDocumenter):
|
||||
return isinstance(member, type) and issubclass(member, BaseException)
|
||||
|
||||
|
||||
class DataDocumenter(ModuleLevelDocumenter):
|
||||
class NewTypeMixin:
|
||||
"""
|
||||
Mixin for DataDocumenter and AttributeDocumenter to provide the feature for
|
||||
supporting NewTypes.
|
||||
"""
|
||||
|
||||
def should_suppress_directive_header(self) -> bool:
|
||||
"""Check directive header should be suppressed."""
|
||||
return inspect.isNewType(self.object) # type: ignore
|
||||
|
||||
def update_content(self, more_content: StringList) -> None:
|
||||
"""Update docstring for the NewType object."""
|
||||
if inspect.isNewType(self.object): # type: ignore
|
||||
supertype = restify(self.object.__supertype__) # type: ignore
|
||||
more_content.append(_('alias of %s') % supertype, '')
|
||||
more_content.append('', '')
|
||||
|
||||
|
||||
class DataDocumenter(ModuleLevelDocumenter, NewTypeMixin):
|
||||
"""
|
||||
Specialized Documenter subclass for data items.
|
||||
"""
|
||||
@@ -1692,7 +1716,12 @@ class DataDocumenter(ModuleLevelDocumenter):
|
||||
def add_directive_header(self, sig: str) -> None:
|
||||
super().add_directive_header(sig)
|
||||
sourcename = self.get_sourcename()
|
||||
if not self.options.annotation:
|
||||
if self.options.annotation is SUPPRESS or self.should_suppress_directive_header():
|
||||
pass
|
||||
elif self.options.annotation:
|
||||
self.add_line(' :annotation: %s' % self.options.annotation,
|
||||
sourcename)
|
||||
else:
|
||||
# obtain annotation for this data
|
||||
annotations = get_type_hints(self.parent, None, self.config.autodoc_type_aliases)
|
||||
if self.objpath[-1] in annotations:
|
||||
@@ -1712,11 +1741,6 @@ class DataDocumenter(ModuleLevelDocumenter):
|
||||
self.add_line(' :value: ' + objrepr, sourcename)
|
||||
except ValueError:
|
||||
pass
|
||||
elif self.options.annotation is SUPPRESS:
|
||||
pass
|
||||
else:
|
||||
self.add_line(' :annotation: %s' % self.options.annotation,
|
||||
sourcename)
|
||||
|
||||
def document_members(self, all_members: bool = False) -> None:
|
||||
pass
|
||||
@@ -1725,11 +1749,16 @@ class DataDocumenter(ModuleLevelDocumenter):
|
||||
return self.get_attr(self.parent or self.object, '__module__', None) \
|
||||
or self.modname
|
||||
|
||||
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
|
||||
def add_content(self, more_content: Optional[StringList], no_docstring: bool = False
|
||||
) -> None:
|
||||
if self.object is UNINITIALIZED_ATTR:
|
||||
# suppress docstring of the value
|
||||
super().add_content(more_content, no_docstring=True)
|
||||
else:
|
||||
if not more_content:
|
||||
more_content = StringList()
|
||||
|
||||
self.update_content(more_content)
|
||||
super().add_content(more_content, no_docstring=no_docstring)
|
||||
|
||||
|
||||
@@ -1770,12 +1799,31 @@ class GenericAliasDocumenter(DataDocumenter):
|
||||
self.options['annotation'] = SUPPRESS
|
||||
super().add_directive_header(sig)
|
||||
|
||||
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
|
||||
def add_content(self, more_content: Optional[StringList], no_docstring: bool = False
|
||||
) -> None:
|
||||
name = stringify_typehint(self.object)
|
||||
content = StringList([_('alias of %s') % name], source='')
|
||||
super().add_content(content)
|
||||
|
||||
|
||||
class NewTypeDataDocumenter(DataDocumenter):
|
||||
"""
|
||||
Specialized Documenter subclass for NewTypes.
|
||||
|
||||
Note: This must be invoked before FunctionDocumenter because NewType is a kind of
|
||||
function object.
|
||||
"""
|
||||
|
||||
objtype = 'newtypedata'
|
||||
directivetype = 'data'
|
||||
priority = FunctionDocumenter.priority + 1
|
||||
|
||||
@classmethod
|
||||
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
|
||||
) -> bool:
|
||||
return inspect.isNewType(member) and isattr
|
||||
|
||||
|
||||
class TypeVarDocumenter(DataDocumenter):
|
||||
"""
|
||||
Specialized Documenter subclass for TypeVars.
|
||||
@@ -1806,7 +1854,8 @@ class TypeVarDocumenter(DataDocumenter):
|
||||
else:
|
||||
return []
|
||||
|
||||
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
|
||||
def add_content(self, more_content: Optional[StringList], no_docstring: bool = False
|
||||
) -> None:
|
||||
attrs = [repr(self.object.__name__)]
|
||||
for constraint in self.object.__constraints__:
|
||||
attrs.append(stringify_typehint(constraint))
|
||||
@@ -1980,7 +2029,7 @@ class SingledispatchMethodDocumenter(MethodDocumenter):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore
|
||||
class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter, NewTypeMixin): # type: ignore # NOQA
|
||||
"""
|
||||
Specialized Documenter subclass for attributes.
|
||||
"""
|
||||
@@ -2076,7 +2125,11 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
|
||||
def add_directive_header(self, sig: str) -> None:
|
||||
super().add_directive_header(sig)
|
||||
sourcename = self.get_sourcename()
|
||||
if not self.options.annotation:
|
||||
if self.options.annotation is SUPPRESS or self.should_suppress_directive_header():
|
||||
pass
|
||||
elif self.options.annotation:
|
||||
self.add_line(' :annotation: %s' % self.options.annotation, sourcename)
|
||||
else:
|
||||
# obtain type annotation for this attribute
|
||||
annotations = get_type_hints(self.parent, None, self.config.autodoc_type_aliases)
|
||||
if self.objpath[-1] in annotations:
|
||||
@@ -2098,10 +2151,6 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
|
||||
self.add_line(' :value: ' + objrepr, sourcename)
|
||||
except ValueError:
|
||||
pass
|
||||
elif self.options.annotation is SUPPRESS:
|
||||
pass
|
||||
else:
|
||||
self.add_line(' :annotation: %s' % self.options.annotation, sourcename)
|
||||
|
||||
def get_doc(self, ignore: int = None) -> List[List[str]]:
|
||||
try:
|
||||
@@ -2114,11 +2163,16 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
|
||||
finally:
|
||||
self.config.autodoc_inherit_docstrings = orig # type: ignore
|
||||
|
||||
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
|
||||
def add_content(self, more_content: Optional[StringList], no_docstring: bool = False
|
||||
) -> None:
|
||||
if not self._datadescriptor:
|
||||
# if it's not a data descriptor, its docstring is very probably the
|
||||
# wrong thing to display
|
||||
no_docstring = True
|
||||
|
||||
if more_content is None:
|
||||
more_content = StringList()
|
||||
self.update_content(more_content)
|
||||
super().add_content(more_content, no_docstring)
|
||||
|
||||
|
||||
@@ -2192,7 +2246,8 @@ class InstanceAttributeDocumenter(AttributeDocumenter):
|
||||
self._datadescriptor = False
|
||||
return True
|
||||
|
||||
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
|
||||
def add_content(self, more_content: Optional[StringList], no_docstring: bool = False
|
||||
) -> None:
|
||||
"""Never try to get a docstring from the object."""
|
||||
super().add_content(more_content, no_docstring=True)
|
||||
|
||||
@@ -2243,14 +2298,38 @@ class SlotsAttributeDocumenter(AttributeDocumenter):
|
||||
% self.__class__.__name__,
|
||||
RemovedInSphinx50Warning, stacklevel=2)
|
||||
name = self.objpath[-1]
|
||||
__slots__ = inspect.getslots(self.parent)
|
||||
if __slots__ and isinstance(__slots__.get(name, None), str):
|
||||
docstring = prepare_docstring(__slots__[name])
|
||||
return [docstring]
|
||||
else:
|
||||
|
||||
try:
|
||||
__slots__ = inspect.getslots(self.parent)
|
||||
if __slots__ and isinstance(__slots__.get(name, None), str):
|
||||
docstring = prepare_docstring(__slots__[name])
|
||||
return [docstring]
|
||||
else:
|
||||
return []
|
||||
except (AttributeError, ValueError) as exc:
|
||||
logger.warning(__('Invalid __slots__ found on %s. Ignored.'),
|
||||
(self.parent.__qualname__, exc), type='autodoc')
|
||||
return []
|
||||
|
||||
|
||||
class NewTypeAttributeDocumenter(AttributeDocumenter):
|
||||
"""
|
||||
Specialized Documenter subclass for NewTypes.
|
||||
|
||||
Note: This must be invoked before MethodDocumenter because NewType is a kind of
|
||||
function object.
|
||||
"""
|
||||
|
||||
objtype = 'newvarattribute'
|
||||
directivetype = 'attribute'
|
||||
priority = MethodDocumenter.priority + 1
|
||||
|
||||
@classmethod
|
||||
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
|
||||
) -> bool:
|
||||
return not isinstance(parent, ModuleDocumenter) and inspect.isNewType(member)
|
||||
|
||||
|
||||
def get_documenters(app: Sphinx) -> Dict[str, Type[Documenter]]:
|
||||
"""Returns registered Documenter classes"""
|
||||
warnings.warn("get_documenters() is deprecated.", RemovedInSphinx50Warning, stacklevel=2)
|
||||
@@ -2280,6 +2359,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_autodocumenter(ExceptionDocumenter)
|
||||
app.add_autodocumenter(DataDocumenter)
|
||||
app.add_autodocumenter(GenericAliasDocumenter)
|
||||
app.add_autodocumenter(NewTypeDataDocumenter)
|
||||
app.add_autodocumenter(TypeVarDocumenter)
|
||||
app.add_autodocumenter(FunctionDocumenter)
|
||||
app.add_autodocumenter(DecoratorDocumenter)
|
||||
@@ -2288,6 +2368,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_autodocumenter(PropertyDocumenter)
|
||||
app.add_autodocumenter(InstanceAttributeDocumenter)
|
||||
app.add_autodocumenter(SlotsAttributeDocumenter)
|
||||
app.add_autodocumenter(NewTypeAttributeDocumenter)
|
||||
|
||||
app.add_config_value('autoclass_content', 'class', True, ENUM('both', 'class', 'init'))
|
||||
app.add_config_value('autodoc_member_order', 'alphabetical', True,
|
||||
|
@@ -15,7 +15,7 @@ from typing import Any, Callable, Dict, List, Mapping, NamedTuple, Optional, Tup
|
||||
|
||||
from sphinx.pycode import ModuleAnalyzer
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.inspect import getslots, isclass, isenumclass, safe_getattr
|
||||
from sphinx.util.inspect import getannotations, getslots, isclass, isenumclass, safe_getattr
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@@ -148,10 +148,12 @@ def get_module_members(module: Any) -> List[Tuple[str, Any]]:
|
||||
continue
|
||||
|
||||
# annotation only member (ex. attr: int)
|
||||
if hasattr(module, '__annotations__'):
|
||||
for name in module.__annotations__:
|
||||
try:
|
||||
for name in getannotations(module):
|
||||
if name not in members:
|
||||
members[name] = (name, INSTANCEATTR)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return sorted(list(members.values()))
|
||||
|
||||
@@ -172,12 +174,9 @@ def _getmro(obj: Any) -> Tuple["Type", ...]:
|
||||
|
||||
|
||||
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 {}
|
||||
warnings.warn('sphinx.ext.autodoc.importer._getannotations() is deprecated.',
|
||||
RemovedInSphinx40Warning)
|
||||
return getannotations(obj)
|
||||
|
||||
|
||||
def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
||||
@@ -210,7 +209,7 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
||||
|
||||
for name in __slots__:
|
||||
members[name] = Attribute(name, True, SLOTSATTR)
|
||||
except (TypeError, ValueError):
|
||||
except (AttributeError, TypeError, ValueError):
|
||||
pass
|
||||
|
||||
# other members
|
||||
@@ -226,10 +225,13 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
||||
|
||||
# annotation only member (ex. attr: int)
|
||||
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, i == 0, INSTANCEATTR)
|
||||
try:
|
||||
for name in getannotations(cls):
|
||||
name = unmangle(cls, name)
|
||||
if name and name not in members:
|
||||
members[name] = Attribute(name, i == 0, INSTANCEATTR)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if analyzer:
|
||||
# append instance attributes (cf. self.attr1) if analyzer knows
|
||||
|
@@ -27,7 +27,7 @@ def record_typehints(app: Sphinx, objtype: str, name: str, obj: Any,
|
||||
if callable(obj):
|
||||
annotations = app.env.temp_data.setdefault('annotations', {})
|
||||
annotation = annotations.setdefault(name, OrderedDict())
|
||||
sig = inspect.signature(obj)
|
||||
sig = inspect.signature(obj, type_aliases=app.config.autodoc_type_aliases)
|
||||
for param in sig.parameters.values():
|
||||
if param.annotation is not param.empty:
|
||||
annotation[param.name] = typing.stringify(param.annotation)
|
||||
|
@@ -85,13 +85,15 @@ def setup_documenters(app: Any) -> None:
|
||||
DecoratorDocumenter, ExceptionDocumenter,
|
||||
FunctionDocumenter, GenericAliasDocumenter,
|
||||
InstanceAttributeDocumenter, MethodDocumenter,
|
||||
ModuleDocumenter, PropertyDocumenter,
|
||||
ModuleDocumenter, NewTypeAttributeDocumenter,
|
||||
NewTypeDataDocumenter, PropertyDocumenter,
|
||||
SingledispatchFunctionDocumenter, SlotsAttributeDocumenter)
|
||||
documenters = [
|
||||
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
|
||||
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
|
||||
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
|
||||
SlotsAttributeDocumenter, GenericAliasDocumenter, SingledispatchFunctionDocumenter,
|
||||
FunctionDocumenter, MethodDocumenter, NewTypeAttributeDocumenter,
|
||||
NewTypeDataDocumenter, AttributeDocumenter, InstanceAttributeDocumenter,
|
||||
DecoratorDocumenter, PropertyDocumenter, SlotsAttributeDocumenter,
|
||||
GenericAliasDocumenter, SingledispatchFunctionDocumenter,
|
||||
] # type: List[Type[Documenter]]
|
||||
for documenter in documenters:
|
||||
app.registry.add_documenter(documenter.objtype, documenter)
|
||||
|
@@ -182,7 +182,8 @@ class GraphvizSimple(SphinxDirective):
|
||||
'alt': directives.unchanged,
|
||||
'align': align_spec,
|
||||
'caption': directives.unchanged,
|
||||
'graphviz_dot': directives.unchanged,
|
||||
'layout': directives.unchanged,
|
||||
'graphviz_dot': directives.unchanged, # an old alias of `layout` option
|
||||
'name': directives.unchanged,
|
||||
'class': directives.class_option,
|
||||
}
|
||||
@@ -194,6 +195,8 @@ class GraphvizSimple(SphinxDirective):
|
||||
node['options'] = {'docname': self.env.docname}
|
||||
if 'graphviz_dot' in self.options:
|
||||
node['options']['graphviz_dot'] = self.options['graphviz_dot']
|
||||
if 'layout' in self.options:
|
||||
node['options']['graphviz_dot'] = self.options['layout']
|
||||
if 'alt' in self.options:
|
||||
node['alt'] = self.options['alt']
|
||||
if 'align' in self.options:
|
||||
|
@@ -18,6 +18,7 @@ from os import path
|
||||
from typing import IO, Any, Dict, List, Optional, Tuple
|
||||
from zipfile import ZipFile
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx50Warning
|
||||
from sphinx.errors import PycodeError
|
||||
from sphinx.pycode.parser import Parser
|
||||
|
||||
@@ -132,18 +133,24 @@ class ModuleAnalyzer:
|
||||
# cache the source code as well
|
||||
self.code = source.read()
|
||||
|
||||
# will be filled by parse()
|
||||
# will be filled by analyze()
|
||||
self.annotations = None # type: Dict[Tuple[str, str], str]
|
||||
self.attr_docs = None # type: Dict[Tuple[str, str], List[str]]
|
||||
self.finals = None # type: List[str]
|
||||
self.overloads = None # type: Dict[str, List[Signature]]
|
||||
self.tagorder = None # type: Dict[str, int]
|
||||
self.tags = None # type: Dict[str, Tuple[str, int, int]]
|
||||
self._parsed = False
|
||||
self._analyzed = False
|
||||
|
||||
def parse(self) -> None:
|
||||
"""Parse the source code."""
|
||||
if self._parsed:
|
||||
warnings.warn('ModuleAnalyzer.parse() is deprecated.',
|
||||
RemovedInSphinx50Warning, stacklevel=2)
|
||||
self.analyze()
|
||||
|
||||
def analyze(self) -> None:
|
||||
"""Analyze the source code."""
|
||||
if self._analyzed:
|
||||
return None
|
||||
|
||||
try:
|
||||
@@ -168,10 +175,10 @@ class ModuleAnalyzer:
|
||||
|
||||
def find_attr_docs(self) -> Dict[Tuple[str, str], List[str]]:
|
||||
"""Find class and module-level attributes and their documentation."""
|
||||
self.parse()
|
||||
self.analyze()
|
||||
return self.attr_docs
|
||||
|
||||
def find_tags(self) -> Dict[str, Tuple[str, int, int]]:
|
||||
"""Find class, function and method definitions and their location."""
|
||||
self.parse()
|
||||
self.analyze()
|
||||
return self.tags
|
||||
|
@@ -50,7 +50,7 @@ help:
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
rm -rf $(BUILDDIR)
|
||||
|
||||
.PHONY: latexpdf
|
||||
latexpdf:
|
||||
|
@@ -141,6 +141,7 @@ def getall(obj: Any) -> Optional[Sequence[str]]:
|
||||
"""Get __all__ attribute of the module as dict.
|
||||
|
||||
Return None if given *obj* does not have __all__.
|
||||
Raises AttributeError if given *obj* raises an error on accessing __all__.
|
||||
Raises ValueError if given *obj* have invalid __all__.
|
||||
"""
|
||||
__all__ = safe_getattr(obj, '__all__', None)
|
||||
@@ -153,10 +154,24 @@ def getall(obj: Any) -> Optional[Sequence[str]]:
|
||||
raise ValueError(__all__)
|
||||
|
||||
|
||||
def getannotations(obj: Any) -> Mapping[str, Any]:
|
||||
"""Get __annotations__ from given *obj* safely.
|
||||
|
||||
Raises AttributeError if given *obj* raises an error on accessing __attribute__.
|
||||
"""
|
||||
__annotations__ = safe_getattr(obj, '__annotations__', None)
|
||||
if isinstance(__annotations__, Mapping):
|
||||
return __annotations__
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
def getslots(obj: Any) -> Optional[Dict]:
|
||||
"""Get __slots__ attribute of the class as dict.
|
||||
|
||||
Return None if gienv *obj* does not have __slots__.
|
||||
Raises AttributeError if given *obj* raises an error on accessing __slots__.
|
||||
Raises ValueError if given *obj* have invalid __slots__.
|
||||
"""
|
||||
if not inspect.isclass(obj):
|
||||
raise TypeError
|
||||
@@ -174,6 +189,16 @@ def getslots(obj: Any) -> Optional[Dict]:
|
||||
raise ValueError
|
||||
|
||||
|
||||
def isNewType(obj: Any) -> bool:
|
||||
"""Check the if object is a kind of NewType."""
|
||||
__module__ = safe_getattr(obj, '__module__', None)
|
||||
__qualname__ = safe_getattr(obj, '__qualname__', None)
|
||||
if __module__ == 'typing' and __qualname__ == 'NewType.<locals>.new_type':
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def isenumclass(x: Any) -> bool:
|
||||
"""Check if the object is subclass of enum."""
|
||||
return inspect.isclass(x) and issubclass(x, enum.Enum)
|
||||
|
@@ -85,10 +85,14 @@ def is_system_TypeVar(typ: Any) -> bool:
|
||||
|
||||
def restify(cls: Optional["Type"]) -> str:
|
||||
"""Convert python class to a reST reference."""
|
||||
from sphinx.util import inspect # lazy loading
|
||||
|
||||
if cls is None or cls is NoneType:
|
||||
return ':obj:`None`'
|
||||
elif cls is Ellipsis:
|
||||
return '...'
|
||||
elif inspect.isNewType(cls):
|
||||
return ':class:`%s`' % cls.__name__
|
||||
elif cls.__module__ in ('__builtin__', 'builtins'):
|
||||
return ':class:`%s`' % cls.__name__
|
||||
else:
|
||||
@@ -231,6 +235,8 @@ def _restify_py36(cls: Optional["Type"]) -> str:
|
||||
|
||||
def stringify(annotation: Any) -> str:
|
||||
"""Stringify type annotation object."""
|
||||
from sphinx.util import inspect # lazy loading
|
||||
|
||||
if isinstance(annotation, str):
|
||||
if annotation.startswith("'") and annotation.endswith("'"):
|
||||
# might be a double Forward-ref'ed type. Go unquoting.
|
||||
@@ -239,6 +245,9 @@ def stringify(annotation: Any) -> str:
|
||||
return annotation
|
||||
elif isinstance(annotation, TypeVar):
|
||||
return annotation.__name__
|
||||
elif inspect.isNewType(annotation):
|
||||
# Could not get the module where it defiend
|
||||
return annotation.__name__
|
||||
elif not annotation:
|
||||
return repr(annotation)
|
||||
elif annotation is NoneType:
|
||||
|
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
@@ -2,10 +2,6 @@ class Foo:
|
||||
__slots__ = ['attr']
|
||||
|
||||
|
||||
class FooSingleString:
|
||||
__slots__ = 'attr'
|
||||
|
||||
|
||||
class Bar:
|
||||
__slots__ = {'attr1': 'docstring of attr1',
|
||||
'attr2': 'docstring of attr2',
|
||||
@@ -13,3 +9,7 @@ class Bar:
|
||||
|
||||
def __init__(self):
|
||||
self.attr2 = None #: docstring of instance attr2
|
||||
|
||||
|
||||
class Baz:
|
||||
__slots__ = 'attr'
|
||||
|
@@ -1,4 +1,4 @@
|
||||
from typing import TypeVar
|
||||
from typing import NewType, TypeVar
|
||||
|
||||
#: T1
|
||||
T1 = TypeVar("T1")
|
||||
@@ -13,3 +13,13 @@ T4 = TypeVar("T4", covariant=True)
|
||||
|
||||
#: T5
|
||||
T5 = TypeVar("T5", contravariant=True)
|
||||
|
||||
#: T6
|
||||
T6 = NewType("T6", int)
|
||||
|
||||
|
||||
class Class:
|
||||
# TODO: TypeVar
|
||||
|
||||
#: T6
|
||||
T6 = NewType("T6", int)
|
||||
|
@@ -6,7 +6,7 @@ source_suffix = '.txt'
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
doctest_global_setup = '''
|
||||
from test_ext_doctest import record
|
||||
from tests.test_ext_doctest import record
|
||||
|
||||
record('doctest_global_setup', 'body', True)
|
||||
'''
|
||||
|
@@ -139,7 +139,7 @@ Special directives
|
||||
|
||||
.. testcleanup:: *
|
||||
|
||||
import test_ext_doctest
|
||||
from tests import test_ext_doctest
|
||||
test_ext_doctest.cleanup_call()
|
||||
|
||||
non-ASCII result
|
||||
|
@@ -16,7 +16,6 @@ from shutil import copyfile
|
||||
from subprocess import PIPE, CalledProcessError
|
||||
|
||||
import pytest
|
||||
from test_build_html import ENV_WARNINGS
|
||||
|
||||
from sphinx.builders.latex import default_latex_documents
|
||||
from sphinx.config import Config
|
||||
@@ -25,6 +24,8 @@ from sphinx.testing.util import strip_escseq
|
||||
from sphinx.util.osutil import cd, ensuredir
|
||||
from sphinx.writers.latex import LaTeXTranslator
|
||||
|
||||
from .test_build_html import ENV_WARNINGS
|
||||
|
||||
LATEX_ENGINES = ['pdflatex', 'lualatex', 'xelatex']
|
||||
DOCCLASSES = ['howto', 'manual']
|
||||
STYLEFILES = ['article.cls', 'fancyhdr.sty', 'titlesec.sty', 'amsmath.sty',
|
||||
|
@@ -15,7 +15,7 @@ import textwrap
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
from utils import CERT_FILE, http_server, https_server, modify_env
|
||||
from .utils import CERT_FILE, http_server, https_server, modify_env
|
||||
|
||||
|
||||
@pytest.mark.sphinx('linkcheck', testroot='linkcheck', freshenv=True)
|
||||
|
@@ -15,7 +15,6 @@ from subprocess import PIPE, CalledProcessError
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from test_build_html import ENV_WARNINGS
|
||||
|
||||
from sphinx.builders.texinfo import default_texinfo_documents
|
||||
from sphinx.config import Config
|
||||
@@ -23,6 +22,8 @@ from sphinx.testing.util import strip_escseq
|
||||
from sphinx.util.docutils import new_document
|
||||
from sphinx.writers.texinfo import TexinfoTranslator
|
||||
|
||||
from .test_build_html import ENV_WARNINGS
|
||||
|
||||
TEXINFO_WARNINGS = ENV_WARNINGS + """\
|
||||
%(root)s/index.rst:\\d+: WARNING: unknown option: &option
|
||||
%(root)s/index.rst:\\d+: WARNING: citation not found: missing
|
||||
|
@@ -1193,6 +1193,14 @@ def test_slots(app):
|
||||
' :module: target.slots',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: Baz()',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Baz.attr',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: Foo()',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
@@ -1200,14 +1208,6 @@ def test_slots(app):
|
||||
' .. py:attribute:: Foo.attr',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: FooSingleString()',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: FooSingleString.attr',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@@ -1728,6 +1728,18 @@ def test_autodoc_TypeVar(app):
|
||||
'.. py:module:: target.typevar',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: Class()',
|
||||
' :module: target.typevar',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Class.T6',
|
||||
' :module: target.typevar',
|
||||
'',
|
||||
' T6',
|
||||
'',
|
||||
' alias of :class:`int`',
|
||||
'',
|
||||
'',
|
||||
'.. py:data:: T1',
|
||||
' :module: target.typevar',
|
||||
'',
|
||||
@@ -1755,6 +1767,14 @@ def test_autodoc_TypeVar(app):
|
||||
' T5',
|
||||
'',
|
||||
" alias of TypeVar('T5', contravariant=True)",
|
||||
'',
|
||||
'.. py:data:: T6',
|
||||
' :module: target.typevar',
|
||||
'',
|
||||
' T6',
|
||||
'',
|
||||
' alias of :class:`int`',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
|
@@ -12,7 +12,8 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from test_ext_autodoc import do_autodoc
|
||||
|
||||
from .test_ext_autodoc import do_autodoc
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
@@ -69,3 +70,18 @@ def test_autoattribute_instance_variable(app):
|
||||
' attr4',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_autoattribute_NewType(app):
|
||||
actual = do_autodoc(app, 'attribute', 'target.typevar.Class.T6')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:attribute:: Class.T6',
|
||||
' :module: target.typevar',
|
||||
'',
|
||||
' T6',
|
||||
'',
|
||||
' alias of :class:`int`',
|
||||
'',
|
||||
]
|
||||
|
@@ -12,7 +12,8 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from test_ext_autodoc import do_autodoc
|
||||
|
||||
from .test_ext_autodoc import do_autodoc
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
|
@@ -12,7 +12,8 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from test_ext_autodoc import do_autodoc
|
||||
|
||||
from .test_ext_autodoc import do_autodoc
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
@@ -72,3 +73,18 @@ def test_autodata_type_comment(app):
|
||||
' attr3',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_autodata_NewType(app):
|
||||
actual = do_autodoc(app, 'data', 'target.typevar.T6')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:data:: T6',
|
||||
' :module: target.typevar',
|
||||
'',
|
||||
' T6',
|
||||
'',
|
||||
' alias of :class:`int`',
|
||||
'',
|
||||
]
|
||||
|
@@ -10,7 +10,8 @@
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from test_ext_autodoc import do_autodoc
|
||||
|
||||
from .test_ext_autodoc import do_autodoc
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
|
@@ -12,10 +12,11 @@ import platform
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from test_ext_autodoc import do_autodoc
|
||||
|
||||
from sphinx.testing import restructuredtext
|
||||
|
||||
from .test_ext_autodoc import do_autodoc
|
||||
|
||||
IS_PYPY = platform.python_implementation() == 'PyPy'
|
||||
|
||||
|
||||
@@ -777,6 +778,28 @@ def test_autodoc_type_aliases(app):
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
|
||||
@pytest.mark.sphinx('text', testroot='ext-autodoc',
|
||||
srcdir='autodoc_typehints_description_and_type_aliases',
|
||||
confoverrides={'autodoc_typehints': "description",
|
||||
'autodoc_type_aliases': {'myint': 'myint'}})
|
||||
def test_autodoc_typehints_description_and_type_aliases(app):
|
||||
(app.srcdir / 'annotations.rst').write_text('.. autofunction:: target.annotations.sum')
|
||||
app.build()
|
||||
context = (app.outdir / 'annotations.txt').read_text()
|
||||
assert ('target.annotations.sum(x, y)\n'
|
||||
'\n'
|
||||
' docstring\n'
|
||||
'\n'
|
||||
' Parameters:\n'
|
||||
' * **x** (*myint*) --\n'
|
||||
'\n'
|
||||
' * **y** (*myint*) --\n'
|
||||
'\n'
|
||||
' Return type:\n'
|
||||
' myint\n' == context)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_autodoc_default_options(app):
|
||||
# no settings
|
||||
|
@@ -9,10 +9,11 @@
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from test_ext_autodoc import do_autodoc
|
||||
|
||||
from sphinx.ext.autodoc import between, cut_lines
|
||||
|
||||
from .test_ext_autodoc import do_autodoc
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_process_docstring(app):
|
||||
|
@@ -9,7 +9,8 @@
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from test_ext_autodoc import do_autodoc
|
||||
|
||||
from .test_ext_autodoc import do_autodoc
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
|
@@ -15,14 +15,15 @@ from unittest import mock
|
||||
|
||||
import pytest
|
||||
from docutils import nodes
|
||||
from test_util_inventory import inventory_v2, inventory_v2_not_having_version
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.ext.intersphinx import (INVENTORY_FILENAME, _get_safe_url, _strip_basic_auth,
|
||||
fetch_inventory, inspect_main, load_mappings,
|
||||
missing_reference, normalize_intersphinx_mapping)
|
||||
from sphinx.ext.intersphinx import setup as intersphinx_setup
|
||||
from utils import http_server
|
||||
|
||||
from .test_util_inventory import inventory_v2, inventory_v2_not_having_version
|
||||
from .utils import http_server
|
||||
|
||||
|
||||
def fake_node(domain, type, target, content, **attrs):
|
||||
|
@@ -129,8 +129,8 @@ def test_signature_partialmethod():
|
||||
|
||||
|
||||
def test_signature_annotations():
|
||||
from typing_test_data import (Node, f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12,
|
||||
f13, f14, f15, f16, f17, f18, f19, f20, f21)
|
||||
from .typing_test_data import (Node, f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12,
|
||||
f13, f14, f15, f16, f17, f18, f19, f20, f21)
|
||||
|
||||
# Class annotations
|
||||
sig = inspect.signature(f0)
|
||||
@@ -223,10 +223,10 @@ def test_signature_annotations():
|
||||
|
||||
# type hints by string
|
||||
sig = inspect.signature(Node.children)
|
||||
assert stringify_signature(sig) == '(self) -> List[typing_test_data.Node]'
|
||||
assert stringify_signature(sig) == '(self) -> List[tests.typing_test_data.Node]'
|
||||
|
||||
sig = inspect.signature(Node.__init__)
|
||||
assert stringify_signature(sig) == '(self, parent: Optional[typing_test_data.Node]) -> None'
|
||||
assert stringify_signature(sig) == '(self, parent: Optional[tests.typing_test_data.Node]) -> None'
|
||||
|
||||
# show_annotation is False
|
||||
sig = inspect.signature(f7)
|
||||
|
@@ -10,7 +10,8 @@
|
||||
|
||||
import sys
|
||||
from numbers import Integral
|
||||
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, TypeVar, Union
|
||||
from typing import (Any, Callable, Dict, Generator, List, NewType, Optional, Tuple, TypeVar,
|
||||
Union)
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -26,6 +27,7 @@ class MyClass2(MyClass1):
|
||||
|
||||
|
||||
T = TypeVar('T')
|
||||
MyInt = NewType('MyInt', int)
|
||||
|
||||
|
||||
class MyList(List[T]):
|
||||
@@ -53,7 +55,7 @@ def test_restify_type_hints_containers():
|
||||
assert restify(Tuple[str, str, str]) == ":class:`Tuple`\\ [:class:`str`, :class:`str`, :class:`str`]"
|
||||
assert restify(Tuple[str, ...]) == ":class:`Tuple`\\ [:class:`str`, ...]"
|
||||
assert restify(List[Dict[str, Tuple]]) == ":class:`List`\\ [:class:`Dict`\\ [:class:`str`, :class:`Tuple`]]"
|
||||
assert restify(MyList[Tuple[int, int]]) == ":class:`test_util_typing.MyList`\\ [:class:`Tuple`\\ [:class:`int`, :class:`int`]]"
|
||||
assert restify(MyList[Tuple[int, int]]) == ":class:`tests.test_util_typing.MyList`\\ [:class:`Tuple`\\ [:class:`int`, :class:`int`]]"
|
||||
assert restify(Generator[None, None, None]) == ":class:`Generator`\\ [:obj:`None`, :obj:`None`, :obj:`None`]"
|
||||
|
||||
|
||||
@@ -76,10 +78,10 @@ def test_restify_type_hints_Union():
|
||||
if sys.version_info >= (3, 7):
|
||||
assert restify(Union[int, Integral]) == ":obj:`Union`\\ [:class:`int`, :class:`numbers.Integral`]"
|
||||
assert (restify(Union[MyClass1, MyClass2]) ==
|
||||
":obj:`Union`\\ [:class:`test_util_typing.MyClass1`, :class:`test_util_typing.<MyClass2>`]")
|
||||
":obj:`Union`\\ [:class:`tests.test_util_typing.MyClass1`, :class:`tests.test_util_typing.<MyClass2>`]")
|
||||
else:
|
||||
assert restify(Union[int, Integral]) == ":class:`numbers.Integral`"
|
||||
assert restify(Union[MyClass1, MyClass2]) == ":class:`test_util_typing.MyClass1`"
|
||||
assert restify(Union[MyClass1, MyClass2]) == ":class:`tests.test_util_typing.MyClass1`"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
|
||||
@@ -88,15 +90,16 @@ def test_restify_type_hints_typevars():
|
||||
T_co = TypeVar('T_co', covariant=True)
|
||||
T_contra = TypeVar('T_contra', contravariant=True)
|
||||
|
||||
assert restify(T) == ":obj:`test_util_typing.T`"
|
||||
assert restify(T_co) == ":obj:`test_util_typing.T_co`"
|
||||
assert restify(T_contra) == ":obj:`test_util_typing.T_contra`"
|
||||
assert restify(List[T]) == ":class:`List`\\ [:obj:`test_util_typing.T`]"
|
||||
assert restify(T) == ":obj:`tests.test_util_typing.T`"
|
||||
assert restify(T_co) == ":obj:`tests.test_util_typing.T_co`"
|
||||
assert restify(T_contra) == ":obj:`tests.test_util_typing.T_contra`"
|
||||
assert restify(List[T]) == ":class:`List`\\ [:obj:`tests.test_util_typing.T`]"
|
||||
assert restify(MyInt) == ":class:`MyInt`"
|
||||
|
||||
|
||||
def test_restify_type_hints_custom_class():
|
||||
assert restify(MyClass1) == ":class:`test_util_typing.MyClass1`"
|
||||
assert restify(MyClass2) == ":class:`test_util_typing.<MyClass2>`"
|
||||
assert restify(MyClass1) == ":class:`tests.test_util_typing.MyClass1`"
|
||||
assert restify(MyClass2) == ":class:`tests.test_util_typing.<MyClass2>`"
|
||||
|
||||
|
||||
def test_restify_type_hints_alias():
|
||||
@@ -107,7 +110,7 @@ def test_restify_type_hints_alias():
|
||||
|
||||
|
||||
def test_restify_broken_type_hints():
|
||||
assert restify(BrokenType) == ':class:`test_util_typing.BrokenType`'
|
||||
assert restify(BrokenType) == ':class:`tests.test_util_typing.BrokenType`'
|
||||
|
||||
|
||||
def test_stringify():
|
||||
@@ -127,7 +130,7 @@ def test_stringify_type_hints_containers():
|
||||
assert stringify(Tuple[str, str, str]) == "Tuple[str, str, str]"
|
||||
assert stringify(Tuple[str, ...]) == "Tuple[str, ...]"
|
||||
assert stringify(List[Dict[str, Tuple]]) == "List[Dict[str, Tuple]]"
|
||||
assert stringify(MyList[Tuple[int, int]]) == "test_util_typing.MyList[Tuple[int, int]]"
|
||||
assert stringify(MyList[Tuple[int, int]]) == "tests.test_util_typing.MyList[Tuple[int, int]]"
|
||||
assert stringify(Generator[None, None, None]) == "Generator[None, None, None]"
|
||||
|
||||
|
||||
@@ -164,10 +167,10 @@ def test_stringify_type_hints_Union():
|
||||
if sys.version_info >= (3, 7):
|
||||
assert stringify(Union[int, Integral]) == "Union[int, numbers.Integral]"
|
||||
assert (stringify(Union[MyClass1, MyClass2]) ==
|
||||
"Union[test_util_typing.MyClass1, test_util_typing.<MyClass2>]")
|
||||
"Union[tests.test_util_typing.MyClass1, tests.test_util_typing.<MyClass2>]")
|
||||
else:
|
||||
assert stringify(Union[int, Integral]) == "numbers.Integral"
|
||||
assert stringify(Union[MyClass1, MyClass2]) == "test_util_typing.MyClass1"
|
||||
assert stringify(Union[MyClass1, MyClass2]) == "tests.test_util_typing.MyClass1"
|
||||
|
||||
|
||||
def test_stringify_type_hints_typevars():
|
||||
@@ -179,11 +182,12 @@ def test_stringify_type_hints_typevars():
|
||||
assert stringify(T_co) == "T_co"
|
||||
assert stringify(T_contra) == "T_contra"
|
||||
assert stringify(List[T]) == "List[T]"
|
||||
assert stringify(MyInt) == "MyInt"
|
||||
|
||||
|
||||
def test_stringify_type_hints_custom_class():
|
||||
assert stringify(MyClass1) == "test_util_typing.MyClass1"
|
||||
assert stringify(MyClass2) == "test_util_typing.<MyClass2>"
|
||||
assert stringify(MyClass1) == "tests.test_util_typing.MyClass1"
|
||||
assert stringify(MyClass2) == "tests.test_util_typing.<MyClass2>"
|
||||
|
||||
|
||||
def test_stringify_type_hints_alias():
|
||||
@@ -194,4 +198,4 @@ def test_stringify_type_hints_alias():
|
||||
|
||||
|
||||
def test_stringify_broken_type_hints():
|
||||
assert stringify(BrokenType) == 'test_util_typing.BrokenType'
|
||||
assert stringify(BrokenType) == 'tests.test_util_typing.BrokenType'
|
||||
|
Reference in New Issue
Block a user