11#!/usr/bin/env python
2+ from __future__ import annotations
23
4+ import importlib
35import inspect
46import pkgutil
57import sys
68
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+
718SEEN_MODS = set ()
819
920
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 } ." ):
1223 if pkg .name in SEEN_MODS :
1324 continue
1425 else :
@@ -18,7 +29,7 @@ def walk_pkgutil(mod_name, mod, io):
1829 print (pkg .name , file = io )
1930
2031
21- def walk_naive (mod_name , mod , io ) :
32+ def walk_naive (mod_name : str , mod : ModuleType , io : Writer ) -> None :
2233 for attr in dir (mod ):
2334 attr_obj = getattr (mod , attr , None )
2435 # Shouldn't happen, but who knows.
@@ -33,8 +44,8 @@ def walk_naive(mod_name, mod, io):
3344 # and import the submodule by its qualified name.
3445 # If the import fails, we know it's a re-exported module.
3546 try :
36- submod_name = mod_name + "." + attr
37- __import__ (submod_name )
47+ submod_name = f" { mod_name } . { attr } "
48+ importlib . import_module (submod_name )
3849 walk (submod_name , io )
3950 except ImportError :
4051 # ...but sometimes we do want to include re-exports, since
@@ -45,13 +56,13 @@ def walk_naive(mod_name, mod, io):
4556 # Again, try and import to avoid module-looking object
4657 # that don't actually appear on disk. Experimentally,
4758 # there are a few of these (like "TK").
48- __import__ (attr )
59+ importlib . import_module (attr )
4960 walk (attr , io )
5061 except ImportError :
5162 continue
5263
5364
54- def walk (mod_name , io ) :
65+ def walk (mod_name : str , io : Writer ) -> None :
5566 if mod_name in SEEN_MODS :
5667 return
5768 else :
@@ -60,15 +71,16 @@ def walk(mod_name, io):
6071
6172 # Try and import it.
6273 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 )
7075 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 )
7284
7385
7486if __name__ == "__main__" :
0 commit comments