4
4
import re
5
5
import sys
6
6
import textwrap
7
+ from types import ModuleType , FunctionType
7
8
from typing import TYPE_CHECKING
8
9
from pathlib import Path , PurePosixPath
9
10
from importlib import import_module
11
+ from dataclasses import field , dataclass
10
12
11
13
import pytest
12
14
from sphinx .config import Config
15
+ from legacy_api_wrap import legacy_api
13
16
17
+ import scanpydoc
14
18
from scanpydoc import rtd_github_links
15
19
from scanpydoc .rtd_github_links import (
16
20
github_url ,
17
21
_infer_vars ,
18
22
_get_linenos ,
19
23
_get_obj_module ,
20
24
)
21
- from scanpydoc .rtd_github_links ._linkcode import (
22
- CInfo ,
23
- PyInfo ,
24
- linkcode_resolve ,
25
- )
25
+ from scanpydoc .rtd_github_links ._linkcode import CInfo , PyInfo , linkcode_resolve
26
26
27
27
28
28
if TYPE_CHECKING :
29
- from types import ModuleType
30
29
from typing import Literal
31
- from collections .abc import Callable
30
+ from collections .abc import Callable , Generator
32
31
33
32
from sphinx .application import Sphinx
34
33
from _pytest .monkeypatch import MonkeyPatch
@@ -151,10 +150,55 @@ def test_as_function(
151
150
assert github_url (f"scanpydoc.{ module } .{ name } " ) == f"{ prefix } /{ obj_path } #L{ s } -L{ e } "
152
151
153
152
154
- def test_get_github_url_only_annotation (prefix : PurePosixPath ) -> None :
153
+ class _TestMod :
154
+ modname = "testing.scanpydoc"
155
+
156
+ @dataclass
157
+ class TestDataCls :
158
+ test_attr : dict [str , str ] = field (default_factory = dict )
159
+
160
+ class TestCls :
161
+ test_anno : int
162
+
163
+ def test_func (self ) -> None : # pragma: no cover
164
+ pass
165
+
166
+ test_func_wrap : Callable [[], None ] # is set below
167
+
168
+
169
+ @pytest .fixture ()
170
+ def test_mod () -> Generator [ModuleType , None , None ]:
171
+ mod = sys .modules [_TestMod .modname ] = ModuleType (_TestMod .modname )
172
+ mod .__file__ = str (
173
+ Path (scanpydoc .__file__ ).parent .parent
174
+ / Path (* _TestMod .modname .split ("." ))
175
+ / "__init__.py"
176
+ )
177
+ for name , obj in vars (_TestMod ).items ():
178
+ if not isinstance (obj , (type , FunctionType )):
179
+ continue
180
+ # pretend things are in the same module
181
+ if "test_rtd_github_links" in obj .__module__ :
182
+ obj .__module__ = _TestMod .modname
183
+ setattr (mod , name , obj )
184
+
185
+ mod .test_func_wrap = legacy_api ()(mod .test_func ) # type: ignore[attr-defined]
186
+
187
+ try :
188
+ yield mod
189
+ finally :
190
+ sys .modules .pop (_TestMod .modname , None )
191
+
192
+
193
+ def test_get_github_url_only_annotation (
194
+ prefix : PurePosixPath ,
195
+ test_mod : ModuleType , # noqa: ARG001
196
+ ) -> None :
155
197
"""Doesn’t really work but shouldn’t crash either."""
156
- url = github_url ("scanpydoc.rtd_github_links._TestCls.test_anno" )
157
- assert url == f"{ prefix } /rtd_github_links/__init__.py"
198
+ url = github_url (f"{ _TestMod .modname } .TestCls.test_anno" )
199
+ assert url == str (
200
+ prefix .parent / Path (* _TestMod .modname .split ("." )) / "__init__.py"
201
+ )
158
202
159
203
160
204
def test_get_github_url_error () -> None :
@@ -165,27 +209,50 @@ def test_get_github_url_error() -> None:
165
209
166
210
167
211
@pytest .mark .parametrize (
168
- ("obj_path" , "obj " , "mod " ),
212
+ ("obj_path" , "get_obj " , "get_mod " ),
169
213
[
170
- pytest .param ("scanpydoc.indent" , textwrap .indent , textwrap , id = "reexport" ),
171
214
pytest .param (
172
- "scanpydoc.rtd_github_links" , rtd_github_links , rtd_github_links , id = "mod"
215
+ "scanpydoc.indent" ,
216
+ lambda _ : textwrap .indent ,
217
+ lambda _ : textwrap ,
218
+ id = "reexport" ,
173
219
),
174
220
pytest .param (
175
- "scanpydoc.rtd_github_links._TestDataCls.test_attr" ,
176
- rtd_github_links ._TestDataCls .__dataclass_fields__ ["test_attr" ], # noqa: SLF001
177
- rtd_github_links ,
221
+ "testing.scanpydoc.test_func" ,
222
+ lambda m : m .test_func ,
223
+ lambda m : m ,
224
+ id = "func" ,
225
+ ),
226
+ pytest .param (
227
+ "testing.scanpydoc.test_func_wrap" ,
228
+ lambda m : m .test_func_wrap ,
229
+ lambda m : m ,
230
+ id = "wrapper" ,
231
+ ),
232
+ pytest .param ("testing.scanpydoc" , lambda m : m , lambda m : m , id = "mod" ),
233
+ pytest .param (
234
+ "testing.scanpydoc.TestDataCls.test_attr" ,
235
+ lambda m : m .TestDataCls .__dataclass_fields__ ["test_attr" ],
236
+ lambda m : m ,
178
237
id = "dataclass_field" ,
179
238
),
180
239
pytest .param (
181
- "scanpydoc.rtd_github_links._TestCls.test_anno" , None , rtd_github_links
240
+ "testing.scanpydoc.TestCls.test_anno" ,
241
+ lambda _ : None ,
242
+ lambda m : m ,
243
+ id = "anno" ,
182
244
),
183
245
],
184
246
)
185
- def test_get_obj_module (obj_path : str , obj : object , mod : ModuleType ) -> None :
247
+ def test_get_obj_module (
248
+ test_mod : ModuleType ,
249
+ obj_path : str ,
250
+ get_obj : Callable [[ModuleType ], object ],
251
+ get_mod : Callable [[ModuleType ], ModuleType ],
252
+ ) -> None :
186
253
obj_rcv , mod_rcv = _get_obj_module (obj_path )
187
- assert obj_rcv is obj
188
- assert mod_rcv is mod
254
+ assert obj_rcv is get_obj ( test_mod )
255
+ assert mod_rcv is get_mod ( test_mod )
189
256
190
257
191
258
def test_linkdoc (prefix : PurePosixPath ) -> None :
@@ -202,5 +269,5 @@ def test_linkdoc(prefix: PurePosixPath) -> None:
202
269
("domain" , "info" ),
203
270
[("py" , PyInfo (fullname = "foo" , module = "" )), ("c" , CInfo (names = []))],
204
271
)
205
- def test_linkdoc_skip (domain : Literal [Domain ], info : DomainInfo ) -> None :
272
+ def test_linkcode_skip (domain : Literal [Domain ], info : DomainInfo ) -> None :
206
273
assert linkcode_resolve (domain , info ) is None # type: ignore[arg-type]
0 commit comments