gh-139707: Add mechanism for distributors to supply error messages for missing stdlib modules (GH-140783)

This commit is contained in:
Stan Ulbrych 2025-12-01 13:36:17 +00:00 committed by GitHub
parent b708485d1a
commit d4fa70706c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 149 additions and 2 deletions

View file

@ -23,9 +23,11 @@
import _imp
import argparse
import enum
import json
import logging
import os
import pathlib
import pprint
import re
import sys
import sysconfig
@ -116,6 +118,18 @@
help="Print a list of module names to stdout and exit",
)
parser.add_argument(
"--generate-missing-stdlib-info",
action="store_true",
help="Generate file with stdlib module info",
)
parser.add_argument(
"--with-missing-stdlib-config",
metavar="CONFIG_FILE",
help="Path to JSON config file with custom missing module messages",
)
@enum.unique
class ModuleState(enum.Enum):
@ -281,6 +295,39 @@ def list_module_names(self, *, all: bool = False) -> set[str]:
names.update(WINDOWS_MODULES)
return names
def generate_missing_stdlib_info(self, config_path: str | None = None) -> None:
config_messages = {}
if config_path:
try:
with open(config_path, encoding='utf-8') as f:
config_messages = json.load(f)
except (FileNotFoundError, json.JSONDecodeError) as e:
raise RuntimeError(f"Failed to load missing stdlib config {config_path!r}") from e
messages = {}
for name in WINDOWS_MODULES:
messages[name] = f"Unsupported platform for Windows-only standard library module {name!r}"
for modinfo in self.modules:
if modinfo.state in (ModuleState.DISABLED, ModuleState.DISABLED_SETUP):
messages[modinfo.name] = f"Standard library module disabled during build {modinfo.name!r} was not found"
elif modinfo.state == ModuleState.NA:
messages[modinfo.name] = f"Unsupported platform for standard library module {modinfo.name!r}"
messages.update(config_messages)
content = f'''\
# Standard library information used by the traceback module for more informative
# ModuleNotFound error messages.
# Generated by check_extension_modules.py
_MISSING_STDLIB_MODULE_MESSAGES = {pprint.pformat(messages)}
'''
output_path = self.builddir / "_missing_stdlib_info.py"
with open(output_path, "w", encoding="utf-8") as f:
f.write(content)
def get_builddir(self) -> pathlib.Path:
try:
with open(self.pybuilddir_txt, encoding="utf-8") as f:
@ -499,6 +546,9 @@ def main() -> None:
names = checker.list_module_names(all=True)
for name in sorted(names):
print(name)
elif args.generate_missing_stdlib_info:
checker.check()
checker.generate_missing_stdlib_info(args.with_missing_stdlib_config)
else:
checker.check()
checker.summary(verbose=args.verbose)