Skip to content

Commit 6d6669f

Browse files
committed
Fix 'NoneType' object is not iterable on Generics
Fix the following exception during doc builds ``` Exception occurred: File "/path/to/.venv/lib/python3.7/site-packages/sphinx_autodoc_typehints.py", line 119, in format_annotation extra = '\\[{}]'.format(', '.join(format_annotation(param) for param in params)) TypeError: 'NoneType' object is not iterable ``` ``` \# Sphinx version: 1.8.2 \# Python version: 3.7.1 (CPython) \# Docutils version: 0.14 \# Jinja2 version: 2.10 \# Last messages: \# 19 added, 0 changed, 0 removed \# \# reading sources... \# \# Loaded extensions: \# sphinx.ext.mathjax (1.8.2) from /path/to/.venv/lib/python3.7/site-packages/sphinx/ext/mathjax.py \# alabaster (0.7.12) from /path/to/.venv/lib/python3.7/site-packages/alabaster/__init__.py \# sphinx.ext.autodoc (1.8.2) from /path/to/.venv/lib/python3.7/site-packages/sphinx/ext/autodoc/__init__.py \# sphinx.ext.intersphinx (1.8.2) from /path/to/.venv/lib/python3.7/site-packages/sphinx/ext/intersphinx.py \# sphinx.ext.napoleon (1.8.2) from /path/to/.venv/lib/python3.7/site-packages/sphinx/ext/napoleon/__init__.py \# sphinx_autodoc_typehints (unknown version) from /path/to/.venv/lib/python3.7/site-packages/sphinx_autodoc_typehints.py Traceback (most recent call last): File "/path/to/.venv/lib/python3.7/site-packages/sphinx/cmd/build.py", line 304, in build_main app.build(args.force_all, filenames) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/application.py", line 341, in build self.builder.build_update() File "/path/to/.venv/lib/python3.7/site-packages/sphinx/builders/__init__.py", line 347, in build_update len(to_build)) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/builders/__init__.py", line 360, in build updated_docnames = set(self.read()) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/builders/__init__.py", line 468, in read self._read_serial(docnames) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/builders/__init__.py", line 490, in _read_serial self.read_doc(docname) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/builders/__init__.py", line 534, in read_doc doctree = read_doc(self.app, self.env, self.env.doc2path(docname)) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/io.py", line 318, in read_doc pub.publish() File "/path/to/.venv/lib/python3.7/site-packages/docutils/core.py", line 217, in publish self.settings) File "/path/to/.venv/lib/python3.7/site-packages/docutils/readers/__init__.py", line 72, in read self.parse() File "/path/to/.venv/lib/python3.7/site-packages/docutils/readers/__init__.py", line 78, in parse self.parser.parse(self.input, document) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/parsers.py", line 88, in parse self.statemachine.run(inputstring, document, inliner=self.inliner) File "/path/to/.venv/lib/python3.7/site-packages/docutils/parsers/rst/states.py", line 171, in run input_source=document['source']) File "/path/to/.venv/lib/python3.7/site-packages/docutils/statemachine.py", line 239, in run context, state, transitions) File "/path/to/.venv/lib/python3.7/site-packages/docutils/statemachine.py", line 460, in check_line return method(match, context, next_state) File "/path/to/.venv/lib/python3.7/site-packages/docutils/parsers/rst/states.py", line 2753, in underline self.section(title, source, style, lineno - 1, messages) File "/path/to/.venv/lib/python3.7/site-packages/docutils/parsers/rst/states.py", line 327, in section self.new_subsection(title, lineno, messages) File "/path/to/.venv/lib/python3.7/site-packages/docutils/parsers/rst/states.py", line 395, in new_subsection node=section_node, match_titles=True) File "/path/to/.venv/lib/python3.7/site-packages/docutils/parsers/rst/states.py", line 282, in nested_parse node=node, match_titles=match_titles) File "/path/to/.venv/lib/python3.7/site-packages/docutils/parsers/rst/states.py", line 196, in run results = StateMachineWS.run(self, input_lines, input_offset) File "/path/to/.venv/lib/python3.7/site-packages/docutils/statemachine.py", line 239, in run context, state, transitions) File "/path/to/.venv/lib/python3.7/site-packages/docutils/statemachine.py", line 460, in check_line return method(match, context, next_state) File "/path/to/.venv/lib/python3.7/site-packages/docutils/parsers/rst/states.py", line 2326, in explicit_markup nodelist, blank_finish = self.explicit_construct(match) File "/path/to/.venv/lib/python3.7/site-packages/docutils/parsers/rst/states.py", line 2338, in explicit_construct return method(self, expmatch) File "/path/to/.venv/lib/python3.7/site-packages/docutils/parsers/rst/states.py", line 2081, in directive directive_class, match, type_name, option_presets) File "/path/to/.venv/lib/python3.7/site-packages/docutils/parsers/rst/states.py", line 2130, in run_directive result = directive_instance.run() File "/path/to/.venv/lib/python3.7/site-packages/sphinx/ext/autodoc/directive.py", line 131, in run documenter.generate(more_content=self.content) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/ext/autodoc/__init__.py", line 819, in generate self.document_members(all_members) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/ext/autodoc/__init__.py", line 740, in document_members check_module=members_check_module and not isattr) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/ext/autodoc/__init__.py", line 1247, in generate all_members=all_members) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/ext/autodoc/__init__.py", line 819, in generate self.document_members(all_members) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/ext/autodoc/__init__.py", line 1235, in document_members ModuleLevelDocumenter.document_members(self, all_members) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/ext/autodoc/__init__.py", line 740, in document_members check_module=members_check_module and not isattr) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/ext/autodoc/__init__.py", line 816, in generate self.add_content(more_content) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/ext/autodoc/__init__.py", line 554, in add_content for i, line in enumerate(self.process_doc(docstrings)): File "/path/to/.venv/lib/python3.7/site-packages/sphinx/ext/autodoc/__init__.py", line 514, in process_doc self.options, docstringlines) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/application.py", line 510, in emit return self.events.emit(event, self, *args) File "/path/to/.venv/lib/python3.7/site-packages/sphinx/events.py", line 80, in emit results.append(callback(*args)) File "/path/to/.venv/lib/python3.7/site-packages/sphinx_autodoc_typehints.py", line 190, in process_docstring formatted_annotation = format_annotation(annotation) File "/path/to/.venv/lib/python3.7/site-packages/sphinx_autodoc_typehints.py", line 103, in format_annotation extra = '\\[{}]'.format(', '.join(format_annotation(param) for param in params)) File "/path/to/.venv/lib/python3.7/site-packages/sphinx_autodoc_typehints.py", line 103, in <genexpr> extra = '\\[{}]'.format(', '.join(format_annotation(param) for param in params)) File "/path/to/.venv/lib/python3.7/site-packages/sphinx_autodoc_typehints.py", line 119, in format_annotation extra = '\\[{}]'.format(', '.join(format_annotation(param) for param in params)) TypeError: 'NoneType' object is not iterable ```
1 parent ab043b9 commit 6d6669f

File tree

3 files changed

+28
-5
lines changed

3 files changed

+28
-5
lines changed

setup.cfg

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ install_requires =
3030
typing >= 3.5; python_version == "3.4"
3131

3232
[options.extras_require]
33-
test = pytest >= 3.1.0
33+
test =
34+
pytest >= 3.1.0
35+
typing_extensions >= 3.5
3436

3537
[flake8]
3638
max-line-length = 99

sphinx_autodoc_typehints.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
from sphinx.util import logging
66
from sphinx.util.inspect import Signature
77

8+
try:
9+
from typing_extensions import Protocol
10+
except ImportError:
11+
Protocol = None
12+
813
try:
914
from inspect import unwrap
1015
except ImportError:
@@ -47,7 +52,8 @@ def format_annotation(annotation):
4752
if inspect.isclass(getattr(annotation, '__origin__', None)):
4853
annotation_cls = annotation.__origin__
4954
try:
50-
if Generic in annotation_cls.mro():
55+
mro = annotation_cls.mro()
56+
if Generic in mro or (Protocol and Protocol in mro):
5157
module = annotation_cls.__module__
5258
except TypeError:
5359
pass # annotation_cls was either the "type" object or typing.Type
@@ -116,10 +122,12 @@ def format_annotation(annotation):
116122
annotation_cls = annotation.__origin__
117123

118124
extra = ''
119-
if Generic in annotation_cls.mro():
125+
mro = annotation_cls.mro()
126+
if Generic in mro or (Protocol and Protocol in mro):
120127
params = (getattr(annotation, '__parameters__', None) or
121128
getattr(annotation, '__args__', None))
122-
extra = '\\[{}]'.format(', '.join(format_annotation(param) for param in params))
129+
if params:
130+
extra = '\\[{}]'.format(', '.join(format_annotation(param) for param in params))
123131

124132
return ':py:class:`~{}.{}`{}'.format(annotation.__module__, annotation_cls.__qualname__,
125133
extra)

tests/test_sphinx_autodoc_typehints.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from typing import (
66
Any, AnyStr, Callable, Dict, Generic, Mapping, Optional, Pattern, Tuple, TypeVar, Union, Type)
77

8+
from typing_extensions import Protocol
9+
810
from sphinx_autodoc_typehints import format_annotation, process_docstring
911

1012
T = TypeVar('T')
@@ -21,6 +23,14 @@ class B(Generic[T]):
2123
pass
2224

2325

26+
class C(Protocol):
27+
pass
28+
29+
30+
class D(Protocol[T]):
31+
pass
32+
33+
2434
class Slotted:
2535
__slots__ = ()
2636

@@ -71,7 +81,10 @@ class Slotted:
7181
(Pattern[str], ':py:class:`~typing.Pattern`\\[:py:class:`str`]'),
7282
(A, ':py:class:`~%s.A`' % __name__),
7383
(B, ':py:class:`~%s.B`\\[\\~T]' % __name__),
74-
(B[int], ':py:class:`~%s.B`\\[:py:class:`int`]' % __name__)
84+
(B[int], ':py:class:`~%s.B`\\[:py:class:`int`]' % __name__),
85+
(C, ':py:class:`~%s.C`' % __name__),
86+
(D, ':py:class:`~%s.D`\\[\\~T]' % __name__),
87+
(D[int], ':py:class:`~%s.D`\\[:py:class:`int`]' % __name__)
7588
])
7689
def test_format_annotation(annotation, expected_result):
7790
result = format_annotation(annotation)

0 commit comments

Comments
 (0)