1 # This script lists the names of standard library modules
2 # to update Python/stdlib_module_names.h
3 import _imp
4 import os.path
5 import re
6 import subprocess
7 import sys
8 import sysconfig
9
10 from check_extension_modules import ModuleChecker
11
12
13 SCRIPT_NAME = 'Tools/build/generate_stdlib_module_names.py'
14
15 SRC_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
16 STDLIB_PATH = os.path.join(SRC_DIR, 'Lib')
17
18 IGNORE = {
19 '__init__',
20 '__pycache__',
21 'site-packages',
22
23 # Test modules and packages
24 '__hello__',
25 '__phello__',
26 '__hello_alias__',
27 '__phello_alias__',
28 '__hello_only__',
29 '_ctypes_test',
30 '_testbuffer',
31 '_testcapi',
32 '_testclinic',
33 '_testconsole',
34 '_testimportmultiple',
35 '_testinternalcapi',
36 '_testmultiphase',
37 '_testsinglephase',
38 '_xxsubinterpreters',
39 '_xxinterpchannels',
40 '_xxtestfuzz',
41 'idlelib.idle_test',
42 'test',
43 'xxlimited',
44 'xxlimited_35',
45 'xxsubtype',
46 }
47
48 # Pure Python modules (Lib/*.py)
49 def list_python_modules(names):
50 for filename in os.listdir(STDLIB_PATH):
51 if not filename.endswith(".py"):
52 continue
53 name = filename.removesuffix(".py")
54 names.add(name)
55
56
57 # Packages in Lib/
58 def list_packages(names):
59 for name in os.listdir(STDLIB_PATH):
60 if name in IGNORE:
61 continue
62 package_path = os.path.join(STDLIB_PATH, name)
63 if not os.path.isdir(package_path):
64 continue
65 if any(package_file.endswith(".py")
66 for package_file in os.listdir(package_path)):
67 names.add(name)
68
69
70 # Built-in and extension modules built by Modules/Setup*
71 # includes Windows and macOS extensions.
72 def list_modules_setup_extensions(names):
73 checker = ModuleChecker()
74 names.update(checker.list_module_names(all=True))
75
76
77 # List frozen modules of the PyImport_FrozenModules list (Python/frozen.c).
78 # Use the "./Programs/_testembed list_frozen" command.
79 def list_frozen(names):
80 submodules = set()
81 for name in _imp._frozen_module_names():
82 # To skip __hello__, __hello_alias__ and etc.
83 if name.startswith('__'):
84 continue
85 if '.' in name:
86 submodules.add(name)
87 else:
88 names.add(name)
89 # Make sure all frozen submodules have a known parent.
90 for name in list(submodules):
91 if name.partition('.')[0] in names:
92 submodules.remove(name)
93 if submodules:
94 raise Exception(f'unexpected frozen submodules: {sorted(submodules)}')
95
96
97 def list_modules():
98 names = set(sys.builtin_module_names)
99 list_modules_setup_extensions(names)
100 list_packages(names)
101 list_python_modules(names)
102 list_frozen(names)
103
104 # Remove ignored packages and modules
105 for name in list(names):
106 package_name = name.split('.')[0]
107 # package_name can be equal to name
108 if package_name in IGNORE:
109 names.discard(name)
110
111 for name in names:
112 if "." in name:
113 raise Exception("sub-modules must not be listed")
114
115 return names
116
117
118 def write_modules(fp, names):
119 print(f"// Auto-generated by {SCRIPT_NAME}.",
120 file=fp)
121 print("// List used to create sys.stdlib_module_names.", file=fp)
122 print(file=fp)
123 print("static const char* _Py_stdlib_module_names[] = {", file=fp)
124 for name in sorted(names):
125 print(f'"{name}",', file=fp)
126 print("};", file=fp)
127
128
129 def main():
130 if not sysconfig.is_python_build():
131 print(f"ERROR: {sys.executable} is not a Python build",
132 file=sys.stderr)
133 sys.exit(1)
134
135 fp = sys.stdout
136 names = list_modules()
137 write_modules(fp, names)
138
139
140 if __name__ == "__main__":
141 main()