11
11
import sys
12
12
import threading
13
13
from types import ModuleType
14
+ from typing import Any
15
+ from typing import Callable
16
+ from typing import cast
17
+ from typing import Iterable
14
18
15
19
from ._importing import _py_abspath
16
- from ._importing import distribution_version as distribution_version
20
+ from ._importing import distribution_version as distribution_version # NOQA:F401
21
+ from ._importing import importobj
17
22
from ._version import version as __version__ # NOQA:F401
18
23
19
24
_PRESERVED_MODULE_ATTRS = {
28
33
}
29
34
30
35
31
- def initpkg (pkgname , exportdefs , attr = None , eager = False ):
36
+ def initpkg (
37
+ pkgname : str ,
38
+ exportdefs : dict [str , Any ],
39
+ attr : dict [str , Any ] | None = None ,
40
+ eager : bool = False ,
41
+ ) -> ApiModule :
32
42
"""initialize given package from the export definitions."""
33
43
attr = attr or {}
34
44
mod = sys .modules .get (pkgname )
@@ -39,12 +49,12 @@ def initpkg(pkgname, exportdefs, attr=None, eager=False):
39
49
if "bpython" in sys .modules or eager :
40
50
for module in list (sys .modules .values ()):
41
51
if isinstance (module , ApiModule ):
42
- module . __dict__
52
+ getattr ( module , " __dict__" )
43
53
44
54
return mod
45
55
46
56
47
- def _initpkg (mod , pkgname , exportdefs , attr = None ):
57
+ def _initpkg (mod : ModuleType | None , pkgname , exportdefs , attr = None ) -> ApiModule :
48
58
"""Helper for initpkg.
49
59
50
60
Python 3.3+ uses finer grained locking for imports, and checks sys.modules before
@@ -59,6 +69,7 @@ def _initpkg(mod, pkgname, exportdefs, attr=None):
59
69
d .update (attr )
60
70
mod = ApiModule (pkgname , exportdefs , implprefix = pkgname , attr = d )
61
71
sys .modules [pkgname ] = mod
72
+ return mod
62
73
else :
63
74
f = getattr (mod , "__file__" , None )
64
75
if f :
@@ -74,21 +85,9 @@ def _initpkg(mod, pkgname, exportdefs, attr=None):
74
85
75
86
# Updating class of existing module as per importlib.util.LazyLoader
76
87
mod .__class__ = ApiModule
77
- mod .__init__ (pkgname , exportdefs , implprefix = pkgname , attr = attr )
78
- return mod
79
-
80
-
81
- def importobj (modpath : str , attrname : str ) -> object :
82
- """imports a module, then resolves the attrname on it"""
83
- module = __import__ (modpath , None , None , ["__doc__" ])
84
- if not attrname :
85
- return module
86
-
87
- retval = module
88
- names = attrname .split ("." )
89
- for x in names :
90
- retval = getattr (retval , x )
91
- return retval
88
+ apimod = cast (ApiModule , mod )
89
+ ApiModule .__init__ (apimod , pkgname , exportdefs , implprefix = pkgname , attr = attr )
90
+ return apimod
92
91
93
92
94
93
def _synchronized (wrapped_function ):
@@ -113,16 +112,24 @@ def __docget(self) -> str | None:
113
112
return self .__doc
114
113
except AttributeError :
115
114
if "__doc__" in self .__map__ :
116
- return self .__makeattr ("__doc__" )
115
+ return cast ( str , self .__makeattr ("__doc__" ) )
117
116
else :
118
117
return None
119
118
120
119
def __docset (self , value : str ) -> None :
121
120
self .__doc = value
122
121
123
122
__doc__ = property (__docget , __docset ) # type: ignore
124
-
125
- def __init__ (self , name , importspec , implprefix = None , attr = None ):
123
+ __map__ : dict [str , tuple [str , str ]]
124
+
125
+ def __init__ (
126
+ self ,
127
+ name : str ,
128
+ importspec : dict [str , Any ],
129
+ implprefix : str | None = None ,
130
+ attr : dict [str , Any ] | None = None ,
131
+ ) -> None :
132
+ super ().__init__ (name )
126
133
self .__name__ = name
127
134
self .__all__ = [x for x in importspec if x != "__onfirstaccess__" ]
128
135
self .__map__ = {}
@@ -168,7 +175,8 @@ def __makeattr(self, name, isgetattr=False):
168
175
target = None
169
176
if "__onfirstaccess__" in self .__map__ :
170
177
target = self .__map__ .pop ("__onfirstaccess__" )
171
- importobj (* target )()
178
+ fn = cast (Callable [[], None ], importobj (* target ))
179
+ fn ()
172
180
try :
173
181
modpath , attrname = self .__map__ [name ]
174
182
except KeyError :
@@ -192,53 +200,50 @@ def __makeattr(self, name, isgetattr=False):
192
200
else :
193
201
result = importobj (modpath , attrname )
194
202
setattr (self , name , result )
195
- try :
196
- del self .__map__ [name ]
197
- except KeyError :
198
- pass # in a recursive-import situation a double-del can happen
203
+ # in a recursive-import situation a double-del can happen
204
+ self .__map__ .pop (name , None )
199
205
return result
200
206
201
207
def __getattr__ (self , name ):
202
208
return self .__makeattr (name , isgetattr = True )
203
209
210
+ def __dir__ (self ) -> Iterable [str ]:
211
+ yield from super ().__dir__ ()
212
+ yield from self .__map__
213
+
204
214
@property
205
- def __dict__ (self ):
215
+ def __dict__ (self ) -> dict [ str , Any ]: # type: ignore
206
216
# force all the content of the module
207
217
# to be loaded when __dict__ is read
208
- dictdescr = ModuleType .__dict__ ["__dict__" ]
209
- dict = dictdescr .__get__ (self )
210
- if dict is not None :
218
+ dictdescr = ModuleType .__dict__ ["__dict__" ] # type: ignore
219
+ ns : dict [ str , Any ] = dictdescr .__get__ (self )
220
+ if ns is not None :
211
221
hasattr (self , "some" )
212
222
for name in self .__all__ :
213
223
try :
214
224
self .__makeattr (name )
215
225
except AttributeError :
216
226
pass
217
- return dict
227
+ return ns
218
228
219
229
220
- def AliasModule (modname : str , modpath : str , attrname : str | None = None ):
221
- mod : object | None = None
230
+ def AliasModule (modname : str , modpath : str , attrname : str | None = None ) -> ModuleType :
231
+ cached_obj : object | None = None
222
232
223
233
def getmod () -> object :
224
- nonlocal mod
225
- if mod is None :
226
- imported = importobj (modpath , None )
227
- if attrname is not None :
228
- mod = getattr (imported , attrname )
229
- else :
230
- mod = imported
231
-
232
- return mod
234
+ nonlocal cached_obj
235
+ if cached_obj is None :
236
+ cached_obj = importobj (modpath , attrname )
237
+ return cached_obj
233
238
234
239
x = modpath + ("." + attrname if attrname else "" )
235
240
repr_result = f"<AliasModule { modname !r} for { x !r} >"
236
241
237
242
class AliasModule (ModuleType ):
238
- def __repr__ (self ):
243
+ def __repr__ (self ) -> str :
239
244
return repr_result
240
245
241
- def __getattribute__ (self , name ) :
246
+ def __getattribute__ (self , name : str ) -> object :
242
247
try :
243
248
return getattr (getmod (), name )
244
249
except ImportError :
@@ -248,10 +253,10 @@ def __getattribute__(self, name):
248
253
else :
249
254
raise
250
255
251
- def __setattr__ (self , name , value ) :
256
+ def __setattr__ (self , name : str , value : object ) -> None :
252
257
setattr (getmod (), name , value )
253
258
254
- def __delattr__ (self , name ) :
259
+ def __delattr__ (self , name : str ) -> None :
255
260
delattr (getmod (), name )
256
261
257
262
return AliasModule (str (modname ))
0 commit comments