From bf6eef2ff7b4fb17a3bedb080ae328c2d74dd803 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Tue, 9 Jan 2024 12:34:24 +0100 Subject: [PATCH] Add test for wrapper (#119) --- .pre-commit-config.yaml | 1 + .prettierrc.cjs | 2 +- .vscode/launch.json | 17 +++ src/scanpydoc/rtd_github_links/__init__.py | 2 + src/scanpydoc/rtd_github_links/_testdata.py | 25 ++++ tests/test_rtd_github_links.py | 120 +++++++------------- 6 files changed, 86 insertions(+), 81 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 src/scanpydoc/rtd_github_links/_testdata.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index baf2d03..4b94da4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,3 +24,4 @@ repos: - sphinx - pytest - types-docutils + - legacy-api-wrap diff --git a/.prettierrc.cjs b/.prettierrc.cjs index eef022b..c867b60 100644 --- a/.prettierrc.cjs +++ b/.prettierrc.cjs @@ -6,7 +6,7 @@ module.exports = { plugins: [require.resolve("prettier-plugin-jinja-template")], overrides: [ { - files: ["settings.json"], + files: [".vscode/*.json"], options: { parser: "json5", quoteProps: "preserve", diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..23dc8ec --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Verwendet IntelliSense zum Ermitteln möglicher Attribute. + // Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen. + // Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "pythonArgs": ["-Xfrozen_modules=off"], + "console": "internalConsole", + "justMyCode": false, + }, + ], +} diff --git a/src/scanpydoc/rtd_github_links/__init__.py b/src/scanpydoc/rtd_github_links/__init__.py index 90de2d0..5ba641f 100644 --- a/src/scanpydoc/rtd_github_links/__init__.py +++ b/src/scanpydoc/rtd_github_links/__init__.py @@ -180,6 +180,8 @@ def _get_linenos(obj: _SourceObjectType) -> tuple[int, int] | tuple[None, None]: def _module_path(obj: _SourceObjectType, module: ModuleType) -> PurePosixPath: """Relative module path to parent directory of toplevel module.""" + while hasattr(obj, "__wrapped__"): + obj = obj.__wrapped__ try: file = Path(inspect.getabsfile(obj)) except TypeError: diff --git a/src/scanpydoc/rtd_github_links/_testdata.py b/src/scanpydoc/rtd_github_links/_testdata.py new file mode 100644 index 0000000..e20d6a8 --- /dev/null +++ b/src/scanpydoc/rtd_github_links/_testdata.py @@ -0,0 +1,25 @@ +"""This module exists just for rtd_github_links tests.""" # noqa: D404 + +from __future__ import annotations + +from dataclasses import field, dataclass + +from legacy_api_wrap import legacy_api + + +@dataclass +class TestDataCls: + test_attr: dict[str, str] = field(default_factory=dict) + + +class TestCls: + test_anno: int + + +def test_func() -> None: # pragma: no cover + pass + + +@legacy_api() +def test_func_wrap() -> None: # pragma: no cover + pass diff --git a/tests/test_rtd_github_links.py b/tests/test_rtd_github_links.py index b26e7cd..655684c 100644 --- a/tests/test_rtd_github_links.py +++ b/tests/test_rtd_github_links.py @@ -4,30 +4,28 @@ import re import sys import textwrap -from types import ModuleType, FunctionType from typing import TYPE_CHECKING from pathlib import Path, PurePosixPath from importlib import import_module -from dataclasses import field, dataclass import pytest from sphinx.config import Config -from legacy_api_wrap import legacy_api -import scanpydoc -from scanpydoc import rtd_github_links from scanpydoc.rtd_github_links import ( + _testdata, github_url, _infer_vars, _get_linenos, + _module_path, _get_obj_module, ) from scanpydoc.rtd_github_links._linkcode import CInfo, PyInfo, linkcode_resolve if TYPE_CHECKING: + from types import ModuleType from typing import Literal - from collections.abc import Callable, Generator + from collections.abc import Callable from sphinx.application import Sphinx from _pytest.monkeypatch import MonkeyPatch @@ -123,7 +121,7 @@ def test_app(monkeypatch: MonkeyPatch, make_app_setup: Callable[..., Sphinx]) -> ), ) assert app.config["linkcode_resolve"] is linkcode_resolve - assert filters == dict(github_url=rtd_github_links.github_url) + assert filters == dict(github_url=github_url) @pytest.mark.parametrize( @@ -150,55 +148,11 @@ def test_as_function( assert github_url(f"scanpydoc.{module}.{name}") == f"{prefix}/{obj_path}#L{s}-L{e}" -class _TestMod: - modname = "testing.scanpydoc" - - @dataclass - class TestDataCls: - test_attr: dict[str, str] = field(default_factory=dict) - - class TestCls: - test_anno: int - - def test_func(self) -> None: # pragma: no cover - pass - - test_func_wrap: Callable[[], None] # is set below - - -@pytest.fixture() -def test_mod() -> Generator[ModuleType, None, None]: - mod = sys.modules[_TestMod.modname] = ModuleType(_TestMod.modname) - mod.__file__ = str( - Path(scanpydoc.__file__).parent.parent - / Path(*_TestMod.modname.split(".")) - / "__init__.py" - ) - for name, obj in vars(_TestMod).items(): - if not isinstance(obj, (type, FunctionType)): - continue - # pretend things are in the same module - if "test_rtd_github_links" in obj.__module__: - obj.__module__ = _TestMod.modname - setattr(mod, name, obj) - - mod.test_func_wrap = legacy_api()(mod.test_func) # type: ignore[attr-defined] - - try: - yield mod - finally: - sys.modules.pop(_TestMod.modname, None) - - -def test_get_github_url_only_annotation( - prefix: PurePosixPath, - test_mod: ModuleType, # noqa: ARG001 -) -> None: +def test_get_github_url_only_annotation(prefix: PurePosixPath) -> None: """Doesn’t really work but shouldn’t crash either.""" - url = github_url(f"{_TestMod.modname}.TestCls.test_anno") - assert url == str( - prefix.parent / Path(*_TestMod.modname.split(".")) / "__init__.py" - ) + url = github_url(f"{_testdata.__name__}.TestCls.test_anno") + path = prefix.parent / Path(*_testdata.__name__.split(".")) + assert url == str(path.with_suffix(".py")) def test_get_github_url_error() -> None: @@ -209,50 +163,56 @@ def test_get_github_url_error() -> None: @pytest.mark.parametrize( - ("obj_path", "get_obj", "get_mod"), + ("obj_path", "obj", "mod", "path_expected"), [ pytest.param( - "scanpydoc.indent", - lambda _: textwrap.indent, - lambda _: textwrap, - id="reexport", + "scanpydoc.indent", textwrap.indent, textwrap, "textwrap.py", id="reexport" ), pytest.param( - "testing.scanpydoc.test_func", - lambda m: m.test_func, - lambda m: m, + "scanpydoc.rtd_github_links._testdata.test_func", + _testdata.test_func, + _testdata, + "scanpydoc/rtd_github_links/_testdata.py", id="func", ), pytest.param( - "testing.scanpydoc.test_func_wrap", - lambda m: m.test_func_wrap, - lambda m: m, + "scanpydoc.rtd_github_links._testdata.test_func_wrap", + _testdata.test_func_wrap, + _testdata, + "scanpydoc/rtd_github_links/_testdata.py", id="wrapper", ), - pytest.param("testing.scanpydoc", lambda m: m, lambda m: m, id="mod"), pytest.param( - "testing.scanpydoc.TestDataCls.test_attr", - lambda m: m.TestDataCls.__dataclass_fields__["test_attr"], - lambda m: m, + "scanpydoc.rtd_github_links._testdata", + _testdata, + _testdata, + "scanpydoc/rtd_github_links/_testdata.py", + id="mod", + ), + pytest.param( + "scanpydoc.rtd_github_links._testdata.TestDataCls.test_attr", + _testdata.TestDataCls.__dataclass_fields__["test_attr"], + _testdata, + "scanpydoc/rtd_github_links/_testdata.py", id="dataclass_field", ), pytest.param( - "testing.scanpydoc.TestCls.test_anno", - lambda _: None, - lambda m: m, + "scanpydoc.rtd_github_links._testdata.TestCls.test_anno", + None, + _testdata, + "scanpydoc/rtd_github_links/_testdata.py", id="anno", ), ], ) -def test_get_obj_module( - test_mod: ModuleType, - obj_path: str, - get_obj: Callable[[ModuleType], object], - get_mod: Callable[[ModuleType], ModuleType], +def test_get_obj_module_path( + obj_path: str, obj: object, mod: ModuleType, path_expected: PurePosixPath ) -> None: obj_rcv, mod_rcv = _get_obj_module(obj_path) - assert obj_rcv is get_obj(test_mod) - assert mod_rcv is get_mod(test_mod) + assert obj_rcv is obj + assert mod_rcv is mod + path = _module_path(obj_rcv, mod_rcv) + assert path == PurePosixPath(path_expected) def test_linkdoc(prefix: PurePosixPath) -> None: