Fixed error when parsing a class with no constructor

pull/300/head
Ashley Whetter 3 years ago
parent 83b1260e67
commit 5faec73073

@ -3,6 +3,15 @@ Changelog
Versions follow `Semantic Versioning <https://semver.org/>`_ (``<major>.<minor>.<patch>``).
v1.8.2 (TBC)
-------------------
Bug Fixes
^^^^^^^^^
* Fixed error when parsing a class with no constructor.
v1.8.1 (2021-04-24)
-------------------

@ -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

@ -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__":

@ -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,

@ -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 %}

@ -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"

@ -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"

@ -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"

@ -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"

@ -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"

@ -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"

@ -107,3 +107,7 @@ def decorator_okay(func):
class Bar(Foo):
def method_okay(self, foo=None, bar=None):
pass
class ClassWithNoInit:
pass

@ -7,3 +7,4 @@ Autodoc Directives
.. autoapidecorator:: example.decorator_okay
:noindex:

@ -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"

@ -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"

@ -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"

@ -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"

@ -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"

@ -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")

Loading…
Cancel
Save