From 6dc56fcae9d3438cee7b51eb868ab43c3e599be9 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Fri, 11 Mar 2022 12:59:12 -0500 Subject: [PATCH 1/3] Support dataclasses --- scanpydoc/rtd_github_links.py | 16 +++++++++++++++- tests/test_rtd_github_links.py | 7 +++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/scanpydoc/rtd_github_links.py b/scanpydoc/rtd_github_links.py index f256353..231e9f0 100644 --- a/scanpydoc/rtd_github_links.py +++ b/scanpydoc/rtd_github_links.py @@ -74,7 +74,13 @@ def _get_obj_module(qualname: str) -> Tuple[Any, ModuleType]: mod = sys.modules[modname] obj = None for attr_name in attr_path: - thing = getattr(mod if obj is None else obj, attr_name) + try: + thing = getattr(mod if obj is None else obj, attr_name) + except AttributeError: + if is_dataclass(obj): + thing = next(f for f in fields(obj) if f.name == attr_name) + else: + raise if isinstance(thing, ModuleType): mod = thing else: @@ -159,3 +165,11 @@ def setup(app: Sphinx) -> Dict[str, Any]: DEFAULT_FILTERS["github_url"] = github_url return metadata + + +if True: # test data + from dataclasses import dataclass, field, is_dataclass, fields + + @dataclass + class _TestCls: + test_attr: dict[str, str] = field(default_factory=dict) diff --git a/tests/test_rtd_github_links.py b/tests/test_rtd_github_links.py index f1e81f5..7bd7bc0 100644 --- a/tests/test_rtd_github_links.py +++ b/tests/test_rtd_github_links.py @@ -1,3 +1,5 @@ +import dataclasses +from dataclasses import dataclass, field, Field from pathlib import Path import pytest @@ -30,3 +32,8 @@ def test_get_obj_module(): obj, mod = _get_obj_module("scanpydoc.get_version") assert obj is get_version.get_version assert mod is get_version + + +def test_get_obj_module_anntation(): + obj, mod = _get_obj_module("scanpydoc.rtd_github_links._TestCls.test_attr") + assert isinstance(obj, Field) From 0f141d7a66534721181f5a32ed82018f2f7d8800 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 11 Mar 2022 17:59:39 +0000 Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- scanpydoc/rtd_github_links.py | 2 +- tests/test_rtd_github_links.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scanpydoc/rtd_github_links.py b/scanpydoc/rtd_github_links.py index 231e9f0..2951b0a 100644 --- a/scanpydoc/rtd_github_links.py +++ b/scanpydoc/rtd_github_links.py @@ -168,7 +168,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: if True: # test data - from dataclasses import dataclass, field, is_dataclass, fields + from dataclasses import dataclass, field, fields, is_dataclass @dataclass class _TestCls: diff --git a/tests/test_rtd_github_links.py b/tests/test_rtd_github_links.py index 7bd7bc0..7c83281 100644 --- a/tests/test_rtd_github_links.py +++ b/tests/test_rtd_github_links.py @@ -1,5 +1,5 @@ import dataclasses -from dataclasses import dataclass, field, Field +from dataclasses import Field, dataclass, field from pathlib import Path import pytest From 19204402d233dad394caf4d9e1dbb366b247f0d3 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Fri, 11 Mar 2022 13:25:53 -0500 Subject: [PATCH 3/3] Some formatting --- scanpydoc/elegant_typehints/formatting.py | 4 ++- scanpydoc/rtd_github_links.py | 8 ++++-- tests/test_elegant_typehints.py | 30 +++++------------------ 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/scanpydoc/elegant_typehints/formatting.py b/scanpydoc/elegant_typehints/formatting.py index 71b3d4c..8fcde93 100644 --- a/scanpydoc/elegant_typehints/formatting.py +++ b/scanpydoc/elegant_typehints/formatting.py @@ -93,7 +93,7 @@ def format_annotation(annotation: Type[Any], config: Config) -> Optional[str]: curframe = inspect.currentframe() calframe = inspect.getouterframes(curframe, 2) - if calframe[2].function == "process_docstring": + if "process_docstring" in {calframe[2].function, calframe[3].function}: return format_both(annotation, config) else: # recursive use return _format_full(annotation, config) @@ -102,6 +102,8 @@ def format_annotation(annotation: Type[Any], config: Config) -> Optional[str]: def format_both(annotation: Type[Any], config: Config) -> str: terse = _format_terse(annotation, config) full = _format_full(annotation, config) or _format_orig(annotation, config) + if terse == full: + return terse return f":annotation-terse:`{_escape(terse)}`\\ :annotation-full:`{_escape(full)}`" diff --git a/scanpydoc/rtd_github_links.py b/scanpydoc/rtd_github_links.py index 2951b0a..c44e42f 100644 --- a/scanpydoc/rtd_github_links.py +++ b/scanpydoc/rtd_github_links.py @@ -24,7 +24,8 @@ ----- You can use the filter e.g. in `autosummary templates`_. -To configure the sphinx_rtd_theme_, override the ``autosummary/base.rst`` template like this: +To configure the sphinx_rtd_theme_, +override the ``autosummary/base.rst`` template like this: .. code:: restructuredtext @@ -32,9 +33,12 @@ {% extends "!autosummary/base.rst" %} -.. _autosummary templates: http://www.sphinx-doc.org/en/master/usage/extensions/autosummary.html#customizing-templates +.. _autosummary templates: \ + http://www.sphinx-doc.org/en/master/usage/extensions/autosummary.html#customizing-templates .. _sphinx_rtd_theme: https://sphinx-rtd-theme.readthedocs.io/en/latest/ """ +from __future__ import annotations + import inspect import sys from pathlib import Path, PurePosixPath diff --git a/tests/test_elegant_typehints.py b/tests/test_elegant_typehints.py index b4d52b1..7b4a994 100644 --- a/tests/test_elegant_typehints.py +++ b/tests/test_elegant_typehints.py @@ -80,12 +80,7 @@ def fn_test(s: str): :param s: Test """ - assert process_doc(fn_test) == [ - ":type s: " - r":annotation-terse:`:py:class:\`str\``\ " - r":annotation-full:`:py:class:\`str\``", - ":param s: Test", - ] + assert process_doc(fn_test) == [":type s: :py:class:`str`", ":param s: Test"] def test_defaults_simple(process_doc): @@ -97,20 +92,11 @@ def fn_test(s: str = "foo", n: None = None, i_: int = 1): """ assert process_doc(fn_test) == [ - ":type s: " - r":annotation-terse:`:py:class:\`str\``\ " - r":annotation-full:`:py:class:\`str\`` " - "(default: ``'foo'``)", + ":type s: :py:class:`str` (default: ``'foo'``)", ":param s: Test S", - ":type n: " - r":annotation-terse:`:py:obj:\`None\``\ " - r":annotation-full:`:py:obj:\`None\`` " - "(default: ``None``)", + ":type n: :py:obj:`None` (default: ``None``)", ":param n: Test N", - r":type i\_: " - r":annotation-terse:`:py:class:\`int\``\ " - r":annotation-full:`:py:class:\`int\`` " - "(default: ``1``)", + r":type i\_: :py:class:`int` (default: ``1``)", r":param i\_: Test I", ] @@ -335,13 +321,9 @@ def fn_test(): if not re.match("^:(rtype|param|annotation-(full|terse)):", l) ] assert lines == [ - r":return: foo : " - r":annotation-terse:`:py:class:\`str\``\ " - r":annotation-full:`:py:class:\`str\``", + r":return: foo : :py:class:`str`", " A foo!", - r" bar : " - r":annotation-terse:`:py:class:\`int\``\ " - r":annotation-full:`:py:class:\`int\``", + r" bar : :py:class:`int`", " A bar!", ]