diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index dd31eb7..8a8fb35 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -3,6 +3,15 @@ Changelog
Versions follow `Semantic Versioning `_ (``..``).
+v1.8.2 (TBC)
+-------------------
+
+Bug Fixes
+^^^^^^^^^
+
+* Fixed error when parsing a class with no constructor.
+
+
v1.8.1 (2021-04-24)
-------------------
diff --git a/autoapi/mappers/python/mapper.py b/autoapi/mappers/python/mapper.py
index 655e5c8..686e9cd 100644
--- a/autoapi/mappers/python/mapper.py
+++ b/autoapi/mappers/python/mapper.py
@@ -380,6 +380,15 @@ class PythonSphinxMapper(SphinxMapperBase):
)
obj.url_root = self.url_root
+ for child_data in data.get("children", []):
+ for child_obj in self.create_class(
+ child_data, options=options, **kwargs
+ ):
+ obj.children.append(child_obj)
+
+ # Some objects require children to establish their docstring
+ # or type annotations (eg classes with inheritance),
+ # so do this after all children have been created.
lines = sphinx.util.docstrings.prepare_docstring(obj.docstring)
if lines and "autodoc-process-docstring" in self.app.events.events:
self.app.emit(
@@ -388,12 +397,6 @@ class PythonSphinxMapper(SphinxMapperBase):
obj.docstring = "\n".join(lines)
self._record_typehints(obj)
- for child_data in data.get("children", []):
- for child_obj in self.create_class(
- child_data, options=options, **kwargs
- ):
- obj.children.append(child_obj)
-
# Parser gives children in source order already
if self.app.config.autoapi_member_order == "alphabetical":
obj.children.sort(key=operator.attrgetter("name"))
@@ -405,14 +408,25 @@ class PythonSphinxMapper(SphinxMapperBase):
def _record_typehints(self, obj):
if isinstance(
obj, (PythonClass, PythonFunction, PythonMethod)
- ) and not obj.obj.get("overloads"):
+ ) and not obj.overloads:
obj_annotations = {}
- for _, name, annotation, _ in obj.obj["args"]:
+
+ include_return_annotation = True
+ obj_data = obj.obj
+ if isinstance(obj, PythonClass):
+ constructor = obj.constructor
+ if constructor:
+ include_return_annotation = False
+ obj_data = constructor.obj
+ else:
+ return
+
+ for _, name, annotation, _ in obj_data["args"]:
if name and annotation:
obj_annotations[name] = annotation
- return_annotation = obj.obj.get("return_annotation")
- if return_annotation:
+ return_annotation = obj_data["return_annotation"]
+ if include_return_annotation and return_annotation:
obj_annotations["return"] = return_annotation
self.app.env.autoapi_annotations[obj.id] = obj_annotations
diff --git a/autoapi/mappers/python/objects.py b/autoapi/mappers/python/objects.py
index 17a6301..310f8e1 100644
--- a/autoapi/mappers/python/objects.py
+++ b/autoapi/mappers/python/objects.py
@@ -1,3 +1,4 @@
+import functools
from typing import Optional
import sphinx.util.logging
@@ -174,6 +175,10 @@ class PythonFunction(PythonPythonMapper):
autodoc_typehints == "description" and not obj["overloads"]
)
self.args = _format_args(obj["args"], show_annotations)
+ """The arguments to this object, formatted as a string.
+
+ :type: str
+ """
self.return_annotation = obj["return_annotation"] if show_annotations else None
"""The type annotation for the return type of this function.
@@ -199,18 +204,6 @@ class PythonFunction(PythonPythonMapper):
:type: list(tuple(str, str))
"""
- @property
- def args(self):
- """The arguments to this object, formatted as a string.
-
- :type: str
- """
- return self._args
-
- @args.setter
- def args(self, value):
- self._args = value
-
class PythonMethod(PythonFunction):
"""The representation of a method."""
@@ -343,8 +336,6 @@ class PythonClass(PythonPythonMapper):
def __init__(self, obj, **kwargs):
super(PythonClass, self).__init__(obj, **kwargs)
- self.args = obj["args"]
-
self.bases = obj["bases"]
"""The fully qualified names of all base classes.
@@ -357,20 +348,34 @@ class PythonClass(PythonPythonMapper):
:type: str
"""
- args = self._args
+ args = ""
+
+ if self.constructor:
+ autodoc_typehints = getattr(self.app.config, "autodoc_typehints", "signature")
+ show_annotations = autodoc_typehints != "none" and not (
+ autodoc_typehints == "description" and not self.constructor.overloads
+ )
+ args_data = self.constructor.obj["args"]
+ if args_data and args_data[0][1] == "self":
+ args_data = args_data[1:]
+ args = _format_args(args_data, show_annotations)
- constructor = self.constructor
- if constructor:
- args = constructor.args
+ return args
- if args.startswith("self"):
- args = args[4:].lstrip(",").lstrip()
+ @property
+ def overloads(self):
+ overloads = []
- return args
+ if self.constructor:
+ overload_data = self.constructor.obj["overloads"]
+ if overload_data and overload_data[0][1] == "self":
+ overload_data = overload_data[1:]
+ overloads = [
+ (_format_args(args), return_annotation)
+ for args, return_annotation in overload_data
+ ]
- @args.setter
- def args(self, value):
- self._args = value
+ return overloads
@property
def docstring(self):
@@ -404,6 +409,7 @@ class PythonClass(PythonPythonMapper):
return self._children_of_type("class")
@property
+ @functools.lru_cache()
def constructor(self):
for child in self.children:
if child.short_name == "__init__":
diff --git a/autoapi/mappers/python/parser.py b/autoapi/mappers/python/parser.py
index abdb4ba..c79d239 100644
--- a/autoapi/mappers/python/parser.py
+++ b/autoapi/mappers/python/parser.py
@@ -86,22 +86,12 @@ class Parser:
if astroid_utils.is_exception(node):
type_ = "exception"
- args = []
- try:
- constructor = node.lookup("__init__")[1]
- except IndexError:
- pass
- else:
- if isinstance(constructor, astroid.nodes.FunctionDef):
- args = astroid_utils.get_args_info(constructor.args)
-
basenames = list(astroid_utils.get_full_basenames(node))
data = {
"type": type_,
"name": node.name,
"full_name": self._get_full_name(node.name),
- "args": args,
"bases": basenames,
"doc": astroid_utils.get_class_docstring(node),
"from_line_no": node.fromlineno,
diff --git a/autoapi/templates/python/class.rst b/autoapi/templates/python/class.rst
index a080ba8..053d8e6 100644
--- a/autoapi/templates/python/class.rst
+++ b/autoapi/templates/python/class.rst
@@ -1,12 +1,8 @@
{% if obj.display %}
.. {{ obj.type }}:: {{ obj.short_name }}{% if obj.args %}({{ obj.args }}){% endif %}
-{% if obj.constructor %}
-
-{% for (args, return_annotation) in obj.constructor.overloads %}
- {% if args and args.startswith("self, ") %}{% set args = args[6:] %}{% endif %}
+{% for (args, return_annotation) in obj.overloads %}
{{ " " * (obj.type | length) }} {{ obj.short_name }}{% if args %}({{ args }}){% endif %}
{% endfor %}
-{% endif %}
{% if obj.bases %}
diff --git a/tests/python/py38positionalparams/conf.py b/tests/python/py38positionalparams/conf.py
index a3292c7..ca3b9cc 100644
--- a/tests/python/py38positionalparams/conf.py
+++ b/tests/python/py38positionalparams/conf.py
@@ -13,7 +13,6 @@ exclude_patterns = ["_build"]
pygments_style = "sphinx"
todo_include_todos = False
html_theme = "alabaster"
-html_static_path = ["_static"]
htmlhelp_basename = "pyexampledoc"
extensions = ["sphinx.ext.autodoc", "autoapi.extension"]
autoapi_type = "python"
diff --git a/tests/python/py3example/conf.py b/tests/python/py3example/conf.py
index b4c6a01..1415b0b 100644
--- a/tests/python/py3example/conf.py
+++ b/tests/python/py3example/conf.py
@@ -13,7 +13,6 @@ exclude_patterns = ["_build"]
pygments_style = "sphinx"
todo_include_todos = False
html_theme = "alabaster"
-html_static_path = ["_static"]
htmlhelp_basename = "pyexampledoc"
extensions = ["sphinx.ext.autodoc", "autoapi.extension"]
autoapi_type = "python"
diff --git a/tests/python/py3implicitnamespace/conf.py b/tests/python/py3implicitnamespace/conf.py
index 53e4789..3cf98b6 100644
--- a/tests/python/py3implicitnamespace/conf.py
+++ b/tests/python/py3implicitnamespace/conf.py
@@ -13,7 +13,6 @@ exclude_patterns = ["_build"]
pygments_style = "sphinx"
todo_include_todos = False
html_theme = "alabaster"
-html_static_path = ["_static"]
htmlhelp_basename = "py3implicitnamespacedoc"
extensions = ["sphinx.ext.autodoc", "autoapi.extension"]
autoapi_type = "python"
diff --git a/tests/python/pyannotationcommentsexample/conf.py b/tests/python/pyannotationcommentsexample/conf.py
index a3292c7..ca3b9cc 100644
--- a/tests/python/pyannotationcommentsexample/conf.py
+++ b/tests/python/pyannotationcommentsexample/conf.py
@@ -13,7 +13,6 @@ exclude_patterns = ["_build"]
pygments_style = "sphinx"
todo_include_todos = False
html_theme = "alabaster"
-html_static_path = ["_static"]
htmlhelp_basename = "pyexampledoc"
extensions = ["sphinx.ext.autodoc", "autoapi.extension"]
autoapi_type = "python"
diff --git a/tests/python/pyautodoc_typehints/conf.py b/tests/python/pyautodoc_typehints/conf.py
index 4d34f5a..882bd22 100644
--- a/tests/python/pyautodoc_typehints/conf.py
+++ b/tests/python/pyautodoc_typehints/conf.py
@@ -13,7 +13,6 @@ exclude_patterns = ["_build"]
pygments_style = "sphinx"
todo_include_todos = False
html_theme = "alabaster"
-html_static_path = ["_static"]
htmlhelp_basename = "pyexampledoc"
extensions = ["sphinx.ext.autodoc", "autoapi.extension", "sphinx.ext.napoleon"]
autoapi_type = "python"
diff --git a/tests/python/pyexample/conf.py b/tests/python/pyexample/conf.py
index a3292c7..ca3b9cc 100644
--- a/tests/python/pyexample/conf.py
+++ b/tests/python/pyexample/conf.py
@@ -13,7 +13,6 @@ exclude_patterns = ["_build"]
pygments_style = "sphinx"
todo_include_todos = False
html_theme = "alabaster"
-html_static_path = ["_static"]
htmlhelp_basename = "pyexampledoc"
extensions = ["sphinx.ext.autodoc", "autoapi.extension"]
autoapi_type = "python"
diff --git a/tests/python/pyexample/example/example.py b/tests/python/pyexample/example/example.py
index 81a2809..b7c032a 100644
--- a/tests/python/pyexample/example/example.py
+++ b/tests/python/pyexample/example/example.py
@@ -107,3 +107,7 @@ def decorator_okay(func):
class Bar(Foo):
def method_okay(self, foo=None, bar=None):
pass
+
+
+class ClassWithNoInit:
+ pass
diff --git a/tests/python/pyexample/manualapi.rst b/tests/python/pyexample/manualapi.rst
index 78a5e6f..82826b7 100644
--- a/tests/python/pyexample/manualapi.rst
+++ b/tests/python/pyexample/manualapi.rst
@@ -7,3 +7,4 @@ Autodoc Directives
.. autoapidecorator:: example.decorator_okay
+ :noindex:
diff --git a/tests/python/pyiexample/conf.py b/tests/python/pyiexample/conf.py
index 2382363..31b9218 100644
--- a/tests/python/pyiexample/conf.py
+++ b/tests/python/pyiexample/conf.py
@@ -13,7 +13,6 @@ exclude_patterns = ["_build"]
pygments_style = "sphinx"
todo_include_todos = False
html_theme = "alabaster"
-html_static_path = ["_static"]
htmlhelp_basename = "pyexampledoc"
extensions = ["sphinx.ext.autodoc", "autoapi.extension"]
autoapi_type = "python"
diff --git a/tests/python/pyiexample2/conf.py b/tests/python/pyiexample2/conf.py
index 46a98a9..464950c 100644
--- a/tests/python/pyiexample2/conf.py
+++ b/tests/python/pyiexample2/conf.py
@@ -13,7 +13,6 @@ exclude_patterns = ["_build"]
pygments_style = "sphinx"
todo_include_todos = False
html_theme = "alabaster"
-html_static_path = ["_static"]
htmlhelp_basename = "pyexampledoc"
extensions = ["sphinx.ext.autodoc", "autoapi.extension"]
autoapi_type = "python"
diff --git a/tests/python/pypackagecomplex/conf.py b/tests/python/pypackagecomplex/conf.py
index e96a5a6..2b77167 100644
--- a/tests/python/pypackagecomplex/conf.py
+++ b/tests/python/pypackagecomplex/conf.py
@@ -13,7 +13,6 @@ exclude_patterns = ["_build"]
pygments_style = "sphinx"
todo_include_todos = False
html_theme = "alabaster"
-html_static_path = ["_static"]
htmlhelp_basename = "pypackagecomplexdoc"
extensions = ["autoapi.extension"]
autoapi_type = "python"
diff --git a/tests/python/pypackageexample/conf.py b/tests/python/pypackageexample/conf.py
index dbb6c3a..ead788f 100644
--- a/tests/python/pypackageexample/conf.py
+++ b/tests/python/pypackageexample/conf.py
@@ -13,7 +13,6 @@ exclude_patterns = ["_build"]
pygments_style = "sphinx"
todo_include_todos = False
html_theme = "alabaster"
-html_static_path = ["_static"]
htmlhelp_basename = "pypackageexampledoc"
extensions = ["autoapi.extension"]
autoapi_type = "python"
diff --git a/tests/python/pyskipexample/conf.py b/tests/python/pyskipexample/conf.py
index 064f75a..44cbd31 100644
--- a/tests/python/pyskipexample/conf.py
+++ b/tests/python/pyskipexample/conf.py
@@ -13,7 +13,6 @@ exclude_patterns = ["_build"]
pygments_style = "sphinx"
todo_include_todos = False
html_theme = "alabaster"
-html_static_path = ["_static"]
htmlhelp_basename = "pyexampledoc"
extensions = ["sphinx.ext.autodoc", "autoapi.extension"]
autoapi_type = "python"
diff --git a/tests/python/test_pyintegration.py b/tests/python/test_pyintegration.py
index cc8f0fd..e0d4f00 100644
--- a/tests/python/test_pyintegration.py
+++ b/tests/python/test_pyintegration.py
@@ -51,7 +51,7 @@ def builder():
class TestSimpleModule:
@pytest.fixture(autouse=True, scope="class")
def built(self, builder):
- builder("pyexample")
+ builder("pyexample", warningiserror=True, confoverrides={"suppress_warnings": ["app"]})
def test_integration(self):
self.check_integration("_build/text/autoapi/example/index.txt")