Skip to content

Commit d65ce60

Browse files
authored
Support dataclasses (#48)
1 parent 096f533 commit d65ce60

File tree

4 files changed

+37
-28
lines changed

4 files changed

+37
-28
lines changed

scanpydoc/elegant_typehints/formatting.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def format_annotation(annotation: Type[Any], config: Config) -> Optional[str]:
9393

9494
curframe = inspect.currentframe()
9595
calframe = inspect.getouterframes(curframe, 2)
96-
if calframe[2].function == "process_docstring":
96+
if "process_docstring" in {calframe[2].function, calframe[3].function}:
9797
return format_both(annotation, config)
9898
else: # recursive use
9999
return _format_full(annotation, config)
@@ -102,6 +102,8 @@ def format_annotation(annotation: Type[Any], config: Config) -> Optional[str]:
102102
def format_both(annotation: Type[Any], config: Config) -> str:
103103
terse = _format_terse(annotation, config)
104104
full = _format_full(annotation, config) or _format_orig(annotation, config)
105+
if terse == full:
106+
return terse
105107
return f":annotation-terse:`{_escape(terse)}`\\ :annotation-full:`{_escape(full)}`"
106108

107109

scanpydoc/rtd_github_links.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,21 @@
2424
-----
2525
2626
You can use the filter e.g. in `autosummary templates`_.
27-
To configure the sphinx_rtd_theme_, override the ``autosummary/base.rst`` template like this:
27+
To configure the sphinx_rtd_theme_,
28+
override the ``autosummary/base.rst`` template like this:
2829
2930
.. code:: restructuredtext
3031
3132
:github_url: {{ fullname | github_url }}
3233
3334
{% extends "!autosummary/base.rst" %}
3435
35-
.. _autosummary templates: http://www.sphinx-doc.org/en/master/usage/extensions/autosummary.html#customizing-templates
36+
.. _autosummary templates: \
37+
http://www.sphinx-doc.org/en/master/usage/extensions/autosummary.html#customizing-templates
3638
.. _sphinx_rtd_theme: https://sphinx-rtd-theme.readthedocs.io/en/latest/
3739
"""
40+
from __future__ import annotations
41+
3842
import inspect
3943
import sys
4044
from pathlib import Path, PurePosixPath
@@ -74,7 +78,13 @@ def _get_obj_module(qualname: str) -> Tuple[Any, ModuleType]:
7478
mod = sys.modules[modname]
7579
obj = None
7680
for attr_name in attr_path:
77-
thing = getattr(mod if obj is None else obj, attr_name)
81+
try:
82+
thing = getattr(mod if obj is None else obj, attr_name)
83+
except AttributeError:
84+
if is_dataclass(obj):
85+
thing = next(f for f in fields(obj) if f.name == attr_name)
86+
else:
87+
raise
7888
if isinstance(thing, ModuleType):
7989
mod = thing
8090
else:
@@ -159,3 +169,11 @@ def setup(app: Sphinx) -> Dict[str, Any]:
159169
DEFAULT_FILTERS["github_url"] = github_url
160170

161171
return metadata
172+
173+
174+
if True: # test data
175+
from dataclasses import dataclass, field, fields, is_dataclass
176+
177+
@dataclass
178+
class _TestCls:
179+
test_attr: dict[str, str] = field(default_factory=dict)

tests/test_elegant_typehints.py

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,7 @@ def fn_test(s: str):
8080
:param s: Test
8181
"""
8282

83-
assert process_doc(fn_test) == [
84-
":type s: "
85-
r":annotation-terse:`:py:class:\`str\``\ "
86-
r":annotation-full:`:py:class:\`str\``",
87-
":param s: Test",
88-
]
83+
assert process_doc(fn_test) == [":type s: :py:class:`str`", ":param s: Test"]
8984

9085

9186
def test_defaults_simple(process_doc):
@@ -97,20 +92,11 @@ def fn_test(s: str = "foo", n: None = None, i_: int = 1):
9792
"""
9893

9994
assert process_doc(fn_test) == [
100-
":type s: "
101-
r":annotation-terse:`:py:class:\`str\``\ "
102-
r":annotation-full:`:py:class:\`str\`` "
103-
"(default: ``'foo'``)",
95+
":type s: :py:class:`str` (default: ``'foo'``)",
10496
":param s: Test S",
105-
":type n: "
106-
r":annotation-terse:`:py:obj:\`None\``\ "
107-
r":annotation-full:`:py:obj:\`None\`` "
108-
"(default: ``None``)",
97+
":type n: :py:obj:`None` (default: ``None``)",
10998
":param n: Test N",
110-
r":type i\_: "
111-
r":annotation-terse:`:py:class:\`int\``\ "
112-
r":annotation-full:`:py:class:\`int\`` "
113-
"(default: ``1``)",
99+
r":type i\_: :py:class:`int` (default: ``1``)",
114100
r":param i\_: Test I",
115101
]
116102

@@ -335,13 +321,9 @@ def fn_test():
335321
if not re.match("^:(rtype|param|annotation-(full|terse)):", l)
336322
]
337323
assert lines == [
338-
r":return: foo : "
339-
r":annotation-terse:`:py:class:\`str\``\ "
340-
r":annotation-full:`:py:class:\`str\``",
324+
r":return: foo : :py:class:`str`",
341325
" A foo!",
342-
r" bar : "
343-
r":annotation-terse:`:py:class:\`int\``\ "
344-
r":annotation-full:`:py:class:\`int\``",
326+
r" bar : :py:class:`int`",
345327
" A bar!",
346328
]
347329

tests/test_rtd_github_links.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import dataclasses
2+
from dataclasses import Field, dataclass, field
13
from pathlib import Path
24

35
import pytest
@@ -30,3 +32,8 @@ def test_get_obj_module():
3032
obj, mod = _get_obj_module("scanpydoc.get_version")
3133
assert obj is get_version.get_version
3234
assert mod is get_version
35+
36+
37+
def test_get_obj_module_anntation():
38+
obj, mod = _get_obj_module("scanpydoc.rtd_github_links._TestCls.test_attr")
39+
assert isinstance(obj, Field)

0 commit comments

Comments
 (0)