6
6
import types
7
7
import importlib
8
8
import inspect
9
+ import itertools
9
10
10
- from typing import Union , Optional , cast , Dict
11
+ from typing import Union , Optional , cast
11
12
from .abc import ResourceReader , Traversable
12
13
13
14
from ._compat import wrap_spec
14
15
15
16
Package = Union [types .ModuleType , str ]
16
17
Anchor = Union [Package , None ]
17
- ModuleContext = Dict [str , str ]
18
18
19
19
20
20
def files (package : Anchor = None ) -> Traversable :
21
21
"""
22
22
Get a Traversable resource for an anchor.
23
23
"""
24
- context = inspect .stack ()[1 ].frame .f_globals # type: ignore
25
- return from_package (resolve (package , context ))
24
+ return from_package (resolve (package ))
26
25
27
26
28
27
def get_resource_reader (package : types .ModuleType ) -> Optional [ResourceReader ]:
@@ -42,18 +41,35 @@ def get_resource_reader(package: types.ModuleType) -> Optional[ResourceReader]:
42
41
43
42
44
43
@functools .singledispatch
45
- def resolve (cand : Anchor , context ) -> types .ModuleType :
44
+ def resolve (cand : Anchor ) -> types .ModuleType :
46
45
return cast (types .ModuleType , cand )
47
46
48
47
49
48
@resolve .register
50
- def _ (cand : str , context ) -> types .ModuleType :
49
+ def _ (cand : str ) -> types .ModuleType :
51
50
return importlib .import_module (cand )
52
51
53
52
54
53
@resolve .register
55
- def _ (cand : None , context : ModuleContext ) -> types .ModuleType :
56
- return resolve (context ['__name__' ], context )
54
+ def _ (cand : None ) -> types .ModuleType :
55
+ return resolve (_infer_caller ().f_globals ['__name__' ])
56
+
57
+
58
+ def _infer_caller ():
59
+ """
60
+ Walk the stack and find the frame of the first caller not in this module.
61
+ """
62
+
63
+ def is_this_file (frame_info ):
64
+ return frame_info .filename == __file__
65
+
66
+ def is_wrapper (frame_info ):
67
+ return frame_info .function == 'wrapper'
68
+
69
+ not_this_file = itertools .filterfalse (is_this_file , inspect .stack ())
70
+ # also exclude 'wrapper' due to singledispatch in the call stack
71
+ callers = itertools .filterfalse (is_wrapper , not_this_file )
72
+ return next (callers ).frame
57
73
58
74
59
75
def from_package (package : types .ModuleType ):
0 commit comments