From 746acde8f9d14e3b4a5857188c7cc5b68eaff519 Mon Sep 17 00:00:00 2001 From: "csaba.nemes" Date: Wed, 23 Sep 2020 16:28:45 +0200 Subject: [PATCH 1/2] add support for datalcass fields with no default value --- sphinx_automodapi/autodoc_enhancements.py | 9 ++++++++- sphinx_automodapi/automodsumm.py | 14 +++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/sphinx_automodapi/autodoc_enhancements.py b/sphinx_automodapi/autodoc_enhancements.py index 7721043..4b75f9e 100644 --- a/sphinx_automodapi/autodoc_enhancements.py +++ b/sphinx_automodapi/autodoc_enhancements.py @@ -1,6 +1,8 @@ """ Miscellaneous enhancements to help autodoc along. """ +import dataclasses + from sphinx.ext.autodoc import AttributeDocumenter __all__ = [] @@ -58,7 +60,12 @@ def type_object_attrgetter(obj, attr, *defargs): return base.__dict__[attr] break - return getattr(obj, attr, *defargs) + try: + return getattr(obj, attr, *defargs) + except AttributeError: + # for dataclasses, get the attribute from the __dataclass_fields__ + if dataclasses.is_dataclass(obj): + return obj.__dataclass_fields__[attr].name def setup(app): diff --git a/sphinx_automodapi/automodsumm.py b/sphinx_automodapi/automodsumm.py index 10a71f6..2d8b280 100644 --- a/sphinx_automodapi/automodsumm.py +++ b/sphinx_automodapi/automodsumm.py @@ -87,6 +87,7 @@ class members that are inherited from a base class. This value can be import inspect import os import re +import dataclasses from sphinx.util import logging from sphinx.ext.autosummary import Autosummary @@ -548,11 +549,22 @@ def get_members_class(obj, typ, include_public=[], else: names = getattr(obj, '__dict__').keys() + # add dataclass_field names for dataclass classes + if dataclasses.is_dataclass(obj): + dataclass_fieldnames = getattr(obj, '__dataclass_fields__').keys() + names = list(set(list(names) + list(dataclass_fieldnames))) + for name in names: try: documenter = get_documenter(app, safe_getattr(obj, name), obj) except AttributeError: - continue + # for dataclasses try to get the attribute from the __dataclass_fields__ + if dataclasses.is_dataclass(obj): + try: + attr = obj.__dataclass_fields__[name] + documenter = get_documenter(app, attr, obj) + except KeyError: + continue if typ is None or documenter.objtype == typ: items.append(name) elif typ == 'attribute' and documenter.objtype == 'property': From 91f6fc5b7b1cf57eb6f7771f8726f9796dbd366d Mon Sep 17 00:00:00 2001 From: "csaba.nemes" Date: Wed, 23 Sep 2020 16:41:08 +0200 Subject: [PATCH 2/2] add test, raise original AttributeError if dataclass field not found --- sphinx_automodapi/autodoc_enhancements.py | 11 +++++++++-- .../tests/test_autodoc_enhancements.py | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/sphinx_automodapi/autodoc_enhancements.py b/sphinx_automodapi/autodoc_enhancements.py index 4b75f9e..84ec880 100644 --- a/sphinx_automodapi/autodoc_enhancements.py +++ b/sphinx_automodapi/autodoc_enhancements.py @@ -62,10 +62,17 @@ def type_object_attrgetter(obj, attr, *defargs): try: return getattr(obj, attr, *defargs) - except AttributeError: + except AttributeError as e: # for dataclasses, get the attribute from the __dataclass_fields__ if dataclasses.is_dataclass(obj): - return obj.__dataclass_fields__[attr].name + if attr in obj.__dataclass_fields__: + return obj.__dataclass_fields__[attr].name + else: + # raise original AttributeError + raise e + else: + # raise original AttributeError + raise e def setup(app): diff --git a/sphinx_automodapi/tests/test_autodoc_enhancements.py b/sphinx_automodapi/tests/test_autodoc_enhancements.py index 5e3f6de..e60380a 100644 --- a/sphinx_automodapi/tests/test_autodoc_enhancements.py +++ b/sphinx_automodapi/tests/test_autodoc_enhancements.py @@ -41,3 +41,20 @@ def test_type_attrgetter(): assert type_object_attrgetter(MyClass, 'susy', 'default') == 'default' assert type_object_attrgetter(MyClass, '__dict__') == MyClass.__dict__ + + +def test_type_attrgetter_for_dataclass(): + """ + This tests the attribute getter for non-default dataclass fields + """ + import dataclasses + + @dataclasses.dataclass + class MyDataclass: + foo: int + bar: str = "bar value" + + with pytest.raises(AttributeError): + getattr(MyDataclass, 'foo') + assert type_object_attrgetter(MyDataclass, 'foo') == 'foo' + assert getattr(MyDataclass, 'bar') == 'bar value'