Skip to content

Commit 5faec73

Browse files
committed
Fixed error when parsing a class with no constructor
1 parent 83b1260 commit 5faec73

File tree

19 files changed

+70
-61
lines changed

19 files changed

+70
-61
lines changed

CHANGELOG.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ Changelog
33

44
Versions follow `Semantic Versioning <https://semver.org/>`_ (``<major>.<minor>.<patch>``).
55

6+
v1.8.2 (TBC)
7+
-------------------
8+
9+
Bug Fixes
10+
^^^^^^^^^
11+
12+
* Fixed error when parsing a class with no constructor.
13+
14+
615
v1.8.1 (2021-04-24)
716
-------------------
817

autoapi/mappers/python/mapper.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,15 @@ def create_class(self, data, options=None, **kwargs):
380380
)
381381
obj.url_root = self.url_root
382382

383+
for child_data in data.get("children", []):
384+
for child_obj in self.create_class(
385+
child_data, options=options, **kwargs
386+
):
387+
obj.children.append(child_obj)
388+
389+
# Some objects require children to establish their docstring
390+
# or type annotations (eg classes with inheritance),
391+
# so do this after all children have been created.
383392
lines = sphinx.util.docstrings.prepare_docstring(obj.docstring)
384393
if lines and "autodoc-process-docstring" in self.app.events.events:
385394
self.app.emit(
@@ -388,12 +397,6 @@ def create_class(self, data, options=None, **kwargs):
388397
obj.docstring = "\n".join(lines)
389398
self._record_typehints(obj)
390399

391-
for child_data in data.get("children", []):
392-
for child_obj in self.create_class(
393-
child_data, options=options, **kwargs
394-
):
395-
obj.children.append(child_obj)
396-
397400
# Parser gives children in source order already
398401
if self.app.config.autoapi_member_order == "alphabetical":
399402
obj.children.sort(key=operator.attrgetter("name"))
@@ -405,14 +408,25 @@ def create_class(self, data, options=None, **kwargs):
405408
def _record_typehints(self, obj):
406409
if isinstance(
407410
obj, (PythonClass, PythonFunction, PythonMethod)
408-
) and not obj.obj.get("overloads"):
411+
) and not obj.overloads:
409412
obj_annotations = {}
410-
for _, name, annotation, _ in obj.obj["args"]:
413+
414+
include_return_annotation = True
415+
obj_data = obj.obj
416+
if isinstance(obj, PythonClass):
417+
constructor = obj.constructor
418+
if constructor:
419+
include_return_annotation = False
420+
obj_data = constructor.obj
421+
else:
422+
return
423+
424+
for _, name, annotation, _ in obj_data["args"]:
411425
if name and annotation:
412426
obj_annotations[name] = annotation
413427

414-
return_annotation = obj.obj.get("return_annotation")
415-
if return_annotation:
428+
return_annotation = obj_data["return_annotation"]
429+
if include_return_annotation and return_annotation:
416430
obj_annotations["return"] = return_annotation
417431

418432
self.app.env.autoapi_annotations[obj.id] = obj_annotations

autoapi/mappers/python/objects.py

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import functools
12
from typing import Optional
23

34
import sphinx.util.logging
@@ -174,6 +175,10 @@ def __init__(self, obj, **kwargs):
174175
autodoc_typehints == "description" and not obj["overloads"]
175176
)
176177
self.args = _format_args(obj["args"], show_annotations)
178+
"""The arguments to this object, formatted as a string.
179+
180+
:type: str
181+
"""
177182

178183
self.return_annotation = obj["return_annotation"] if show_annotations else None
179184
"""The type annotation for the return type of this function.
@@ -199,18 +204,6 @@ def __init__(self, obj, **kwargs):
199204
:type: list(tuple(str, str))
200205
"""
201206

202-
@property
203-
def args(self):
204-
"""The arguments to this object, formatted as a string.
205-
206-
:type: str
207-
"""
208-
return self._args
209-
210-
@args.setter
211-
def args(self, value):
212-
self._args = value
213-
214207

215208
class PythonMethod(PythonFunction):
216209
"""The representation of a method."""
@@ -343,8 +336,6 @@ class PythonClass(PythonPythonMapper):
343336
def __init__(self, obj, **kwargs):
344337
super(PythonClass, self).__init__(obj, **kwargs)
345338

346-
self.args = obj["args"]
347-
348339
self.bases = obj["bases"]
349340
"""The fully qualified names of all base classes.
350341
@@ -357,20 +348,34 @@ def args(self):
357348
358349
:type: str
359350
"""
360-
args = self._args
351+
args = ""
352+
353+
if self.constructor:
354+
autodoc_typehints = getattr(self.app.config, "autodoc_typehints", "signature")
355+
show_annotations = autodoc_typehints != "none" and not (
356+
autodoc_typehints == "description" and not self.constructor.overloads
357+
)
358+
args_data = self.constructor.obj["args"]
359+
if args_data and args_data[0][1] == "self":
360+
args_data = args_data[1:]
361+
args = _format_args(args_data, show_annotations)
361362

362-
constructor = self.constructor
363-
if constructor:
364-
args = constructor.args
363+
return args
365364

366-
if args.startswith("self"):
367-
args = args[4:].lstrip(",").lstrip()
365+
@property
366+
def overloads(self):
367+
overloads = []
368368

369-
return args
369+
if self.constructor:
370+
overload_data = self.constructor.obj["overloads"]
371+
if overload_data and overload_data[0][1] == "self":
372+
overload_data = overload_data[1:]
373+
overloads = [
374+
(_format_args(args), return_annotation)
375+
for args, return_annotation in overload_data
376+
]
370377

371-
@args.setter
372-
def args(self, value):
373-
self._args = value
378+
return overloads
374379

375380
@property
376381
def docstring(self):
@@ -404,6 +409,7 @@ def classes(self):
404409
return self._children_of_type("class")
405410

406411
@property
412+
@functools.lru_cache()
407413
def constructor(self):
408414
for child in self.children:
409415
if child.short_name == "__init__":

autoapi/mappers/python/parser.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,22 +86,12 @@ def parse_classdef(self, node, data=None):
8686
if astroid_utils.is_exception(node):
8787
type_ = "exception"
8888

89-
args = []
90-
try:
91-
constructor = node.lookup("__init__")[1]
92-
except IndexError:
93-
pass
94-
else:
95-
if isinstance(constructor, astroid.nodes.FunctionDef):
96-
args = astroid_utils.get_args_info(constructor.args)
97-
9889
basenames = list(astroid_utils.get_full_basenames(node))
9990

10091
data = {
10192
"type": type_,
10293
"name": node.name,
10394
"full_name": self._get_full_name(node.name),
104-
"args": args,
10595
"bases": basenames,
10696
"doc": astroid_utils.get_class_docstring(node),
10797
"from_line_no": node.fromlineno,

autoapi/templates/python/class.rst

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
{% if obj.display %}
22
.. {{ obj.type }}:: {{ obj.short_name }}{% if obj.args %}({{ obj.args }}){% endif %}
3-
{% if obj.constructor %}
4-
5-
{% for (args, return_annotation) in obj.constructor.overloads %}
6-
{% if args and args.startswith("self, ") %}{% set args = args[6:] %}{% endif %}
3+
{% for (args, return_annotation) in obj.overloads %}
74
{{ " " * (obj.type | length) }} {{ obj.short_name }}{% if args %}({{ args }}){% endif %}
85
{% endfor %}
9-
{% endif %}
106

117

128
{% if obj.bases %}

tests/python/py38positionalparams/conf.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
pygments_style = "sphinx"
1414
todo_include_todos = False
1515
html_theme = "alabaster"
16-
html_static_path = ["_static"]
1716
htmlhelp_basename = "pyexampledoc"
1817
extensions = ["sphinx.ext.autodoc", "autoapi.extension"]
1918
autoapi_type = "python"

tests/python/py3example/conf.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
pygments_style = "sphinx"
1414
todo_include_todos = False
1515
html_theme = "alabaster"
16-
html_static_path = ["_static"]
1716
htmlhelp_basename = "pyexampledoc"
1817
extensions = ["sphinx.ext.autodoc", "autoapi.extension"]
1918
autoapi_type = "python"

tests/python/py3implicitnamespace/conf.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
pygments_style = "sphinx"
1414
todo_include_todos = False
1515
html_theme = "alabaster"
16-
html_static_path = ["_static"]
1716
htmlhelp_basename = "py3implicitnamespacedoc"
1817
extensions = ["sphinx.ext.autodoc", "autoapi.extension"]
1918
autoapi_type = "python"

tests/python/pyannotationcommentsexample/conf.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
pygments_style = "sphinx"
1414
todo_include_todos = False
1515
html_theme = "alabaster"
16-
html_static_path = ["_static"]
1716
htmlhelp_basename = "pyexampledoc"
1817
extensions = ["sphinx.ext.autodoc", "autoapi.extension"]
1918
autoapi_type = "python"

tests/python/pyautodoc_typehints/conf.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
pygments_style = "sphinx"
1414
todo_include_todos = False
1515
html_theme = "alabaster"
16-
html_static_path = ["_static"]
1716
htmlhelp_basename = "pyexampledoc"
1817
extensions = ["sphinx.ext.autodoc", "autoapi.extension", "sphinx.ext.napoleon"]
1918
autoapi_type = "python"

0 commit comments

Comments
 (0)