| # This script lists the names of standard library modules |
| # to update Python/stdlib_mod_names.h |
| import _imp |
| import os.path |
| import re |
| import subprocess |
| import sys |
| import sysconfig |
| |
| |
| SRC_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) |
| STDLIB_PATH = os.path.join(SRC_DIR, 'Lib') |
| MODULES_SETUP = os.path.join(SRC_DIR, 'Modules', 'Setup') |
| SETUP_PY = os.path.join(SRC_DIR, 'setup.py') |
| |
| IGNORE = { |
| '__init__', |
| '__pycache__', |
| 'site-packages', |
| |
| # Test modules and packages |
| '__hello__', |
| '__phello__', |
| '__hello_alias__', |
| '__phello_alias__', |
| '__hello_only__', |
| '_ctypes_test', |
| '_testbuffer', |
| '_testcapi', |
| '_testconsole', |
| '_testimportmultiple', |
| '_testinternalcapi', |
| '_testmultiphase', |
| '_xxsubinterpreters', |
| '_xxtestfuzz', |
| 'distutils.tests', |
| 'idlelib.idle_test', |
| 'lib2to3.tests', |
| 'test', |
| 'xxlimited', |
| 'xxlimited_35', |
| 'xxsubtype', |
| } |
| |
| # Windows extension modules |
| WINDOWS_MODULES = ( |
| '_msi', |
| '_overlapped', |
| '_testconsole', |
| '_winapi', |
| 'msvcrt', |
| 'nt', |
| 'winreg', |
| 'winsound' |
| ) |
| |
| # macOS extension modules |
| MACOS_MODULES = ( |
| '_scproxy', |
| ) |
| |
| # Pure Python modules (Lib/*.py) |
| def list_python_modules(names): |
| for filename in os.listdir(STDLIB_PATH): |
| if not filename.endswith(".py"): |
| continue |
| name = filename.removesuffix(".py") |
| names.add(name) |
| |
| |
| # Packages in Lib/ |
| def list_packages(names): |
| for name in os.listdir(STDLIB_PATH): |
| if name in IGNORE: |
| continue |
| package_path = os.path.join(STDLIB_PATH, name) |
| if not os.path.isdir(package_path): |
| continue |
| if any(package_file.endswith(".py") |
| for package_file in os.listdir(package_path)): |
| names.add(name) |
| |
| |
| # Extension modules built by setup.py |
| def list_setup_extensions(names): |
| cmd = [sys.executable, SETUP_PY, "-q", "build", "--list-module-names"] |
| output = subprocess.check_output(cmd) |
| output = output.decode("utf8") |
| extensions = output.splitlines() |
| names |= set(extensions) |
| |
| |
| # Built-in and extension modules built by Modules/Setup |
| def list_modules_setup_extensions(names): |
| assign_var = re.compile("^[A-Z]+=") |
| |
| with open(MODULES_SETUP, encoding="utf-8") as modules_fp: |
| for line in modules_fp: |
| # Strip comment |
| line = line.partition("#")[0] |
| line = line.rstrip() |
| if not line: |
| continue |
| if assign_var.match(line): |
| # Ignore "VAR=VALUE" |
| continue |
| if line in ("*disabled*", "*shared*"): |
| continue |
| parts = line.split() |
| if len(parts) < 2: |
| continue |
| # "errno errnomodule.c" => write "errno" |
| name = parts[0] |
| names.add(name) |
| |
| |
| # List frozen modules of the PyImport_FrozenModules list (Python/frozen.c). |
| # Use the "./Programs/_testembed list_frozen" command. |
| def list_frozen(names): |
| submodules = set() |
| for name in _imp._frozen_module_names(): |
| # To skip __hello__, __hello_alias__ and etc. |
| if name.startswith('__'): |
| continue |
| if '.' in name: |
| submodules.add(name) |
| else: |
| names.add(name) |
| # Make sure all frozen submodules have a known parent. |
| for name in list(submodules): |
| if name.partition('.')[0] in names: |
| submodules.remove(name) |
| if submodules: |
| raise Exception(f'unexpected frozen submodules: {sorted(submodules)}') |
| |
| |
| def list_modules(): |
| names = set(sys.builtin_module_names) | set(WINDOWS_MODULES) | set(MACOS_MODULES) |
| list_modules_setup_extensions(names) |
| list_setup_extensions(names) |
| list_packages(names) |
| list_python_modules(names) |
| list_frozen(names) |
| |
| # Remove ignored packages and modules |
| for name in list(names): |
| package_name = name.split('.')[0] |
| # package_name can be equal to name |
| if package_name in IGNORE: |
| names.discard(name) |
| |
| for name in names: |
| if "." in name: |
| raise Exception("sub-modules must not be listed") |
| |
| return names |
| |
| |
| def write_modules(fp, names): |
| print("// Auto-generated by Tools/scripts/generate_stdlib_module_names.py.", |
| file=fp) |
| print("// List used to create sys.stdlib_module_names.", file=fp) |
| print(file=fp) |
| print("static const char* _Py_stdlib_module_names[] = {", file=fp) |
| for name in sorted(names): |
| print(f'"{name}",', file=fp) |
| print("};", file=fp) |
| |
| |
| def main(): |
| if not sysconfig.is_python_build(): |
| print(f"ERROR: {sys.executable} is not a Python build", |
| file=sys.stderr) |
| sys.exit(1) |
| |
| fp = sys.stdout |
| names = list_modules() |
| write_modules(fp, names) |
| |
| |
| if __name__ == "__main__": |
| main() |