From ccdfd2bd881fb261e8e337f68c0f4288998f6906 Mon Sep 17 00:00:00 2001 From: "csaba.nemes" Date: Wed, 23 Sep 2020 16:28:45 +0200 Subject: [PATCH 1/4] 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 268a0a7..a98bd20 100644 --- a/sphinx_automodapi/automodsumm.py +++ b/sphinx_automodapi/automodsumm.py @@ -103,6 +103,7 @@ class members that are inherited from a base class. This value can be import inspect import os import re +import dataclasses import sphinx from docutils.parsers.rst.directives import flag @@ -595,11 +596,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 2365af06a15267719ecc0051f646132711006511 Mon Sep 17 00:00:00 2001 From: "csaba.nemes" Date: Wed, 23 Sep 2020 16:41:08 +0200 Subject: [PATCH 2/4] 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' From e4bea83eadaa0180eb242cdefcaaf07f77e8d37b Mon Sep 17 00:00:00 2001 From: Leo Singer Date: Mon, 28 Apr 2025 07:17:40 -0400 Subject: [PATCH 3/4] Clean up exceptions and conditionals --- sphinx_automodapi/autodoc_enhancements.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/sphinx_automodapi/autodoc_enhancements.py b/sphinx_automodapi/autodoc_enhancements.py index 84ec880..e5f304b 100644 --- a/sphinx_automodapi/autodoc_enhancements.py +++ b/sphinx_automodapi/autodoc_enhancements.py @@ -62,17 +62,12 @@ def type_object_attrgetter(obj, attr, *defargs): try: return getattr(obj, attr, *defargs) - except AttributeError as e: + except AttributeError: # for dataclasses, get the attribute from the __dataclass_fields__ - if dataclasses.is_dataclass(obj): - if attr in obj.__dataclass_fields__: - return obj.__dataclass_fields__[attr].name - else: - # raise original AttributeError - raise e + if dataclasses.is_dataclass(obj) and attr in obj.__dataclass_fields__: + return obj.__dataclass_fields__[attr].name else: - # raise original AttributeError - raise e + raise def setup(app): From ec44299749ef054d012285dc1886a196dea13c55 Mon Sep 17 00:00:00 2001 From: Leo Singer Date: Mon, 28 Apr 2025 07:31:17 -0400 Subject: [PATCH 4/4] Don't treat name of field as default value --- sphinx_automodapi/autodoc_enhancements.py | 2 +- sphinx_automodapi/tests/test_autodoc_enhancements.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx_automodapi/autodoc_enhancements.py b/sphinx_automodapi/autodoc_enhancements.py index e5f304b..8350638 100644 --- a/sphinx_automodapi/autodoc_enhancements.py +++ b/sphinx_automodapi/autodoc_enhancements.py @@ -65,7 +65,7 @@ def type_object_attrgetter(obj, attr, *defargs): except AttributeError: # for dataclasses, get the attribute from the __dataclass_fields__ if dataclasses.is_dataclass(obj) and attr in obj.__dataclass_fields__: - return obj.__dataclass_fields__[attr].name + return obj.__dataclass_fields__[attr].default else: raise diff --git a/sphinx_automodapi/tests/test_autodoc_enhancements.py b/sphinx_automodapi/tests/test_autodoc_enhancements.py index e60380a..bd1fc36 100644 --- a/sphinx_automodapi/tests/test_autodoc_enhancements.py +++ b/sphinx_automodapi/tests/test_autodoc_enhancements.py @@ -56,5 +56,5 @@ class MyDataclass: with pytest.raises(AttributeError): getattr(MyDataclass, 'foo') - assert type_object_attrgetter(MyDataclass, 'foo') == 'foo' + assert type_object_attrgetter(MyDataclass, 'foo') == dataclasses.MISSING assert getattr(MyDataclass, 'bar') == 'bar value'