1
1
#!/usr/bin/env python
2
+ from __future__ import annotations
2
3
4
+ import importlib
3
5
import inspect
4
6
import pkgutil
5
7
import sys
6
8
9
+ TYPE_CHECKING = False
10
+ if TYPE_CHECKING :
11
+ from collections .abc import Sequence
12
+ from types import ModuleType
13
+ from typing import Protocol
14
+
15
+ class Writer (Protocol ):
16
+ def write (self , s : str , / ) -> object : ...
17
+
7
18
SEEN_MODS = set ()
8
19
9
20
10
- def walk_pkgutil (mod_name , mod , io ) :
11
- for pkg in pkgutil .walk_packages (mod . __path__ , mod_name + " ." ):
21
+ def walk_pkgutil (mod_name : str , locations : Sequence [ str ] , io : Writer ) -> None :
22
+ for pkg in pkgutil .walk_packages (locations , f" { mod_name } ." ):
12
23
if pkg .name in SEEN_MODS :
13
24
continue
14
25
else :
@@ -18,7 +29,7 @@ def walk_pkgutil(mod_name, mod, io):
18
29
print (pkg .name , file = io )
19
30
20
31
21
- def walk_naive (mod_name , mod , io ) :
32
+ def walk_naive (mod_name : str , mod : ModuleType , io : Writer ) -> None :
22
33
for attr in dir (mod ):
23
34
attr_obj = getattr (mod , attr , None )
24
35
# Shouldn't happen, but who knows.
@@ -33,8 +44,8 @@ def walk_naive(mod_name, mod, io):
33
44
# and import the submodule by its qualified name.
34
45
# If the import fails, we know it's a re-exported module.
35
46
try :
36
- submod_name = mod_name + "." + attr
37
- __import__ (submod_name )
47
+ submod_name = f" { mod_name } . { attr } "
48
+ importlib . import_module (submod_name )
38
49
walk (submod_name , io )
39
50
except ImportError :
40
51
# ...but sometimes we do want to include re-exports, since
@@ -45,13 +56,13 @@ def walk_naive(mod_name, mod, io):
45
56
# Again, try and import to avoid module-looking object
46
57
# that don't actually appear on disk. Experimentally,
47
58
# there are a few of these (like "TK").
48
- __import__ (attr )
59
+ importlib . import_module (attr )
49
60
walk (attr , io )
50
61
except ImportError :
51
62
continue
52
63
53
64
54
- def walk (mod_name , io ) :
65
+ def walk (mod_name : str , io : Writer ) -> None :
55
66
if mod_name in SEEN_MODS :
56
67
return
57
68
else :
@@ -60,15 +71,16 @@ def walk(mod_name, io):
60
71
61
72
# Try and import it.
62
73
try :
63
- mod = __import__ (mod_name )
64
-
65
- if hasattr (mod , "__path__" ):
66
- walk_pkgutil (mod_name , mod , io )
67
- else :
68
- walk_naive (mod_name , mod , io )
69
-
74
+ mod = importlib .import_module (mod_name )
70
75
except ImportError :
71
- pass
76
+ return
77
+
78
+ try :
79
+ locations = mod .__spec__ .submodule_search_locations
80
+ except AttributeError :
81
+ walk_naive (mod_name , mod , io )
82
+ else :
83
+ walk_pkgutil (mod_name , locations , io )
72
84
73
85
74
86
if __name__ == "__main__" :
0 commit comments