pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/d4fa70706c95a5eec4cca340c6232c92168f6cff

gh-139707: Add mechanism for distributors to supply error messages fo… · python/cpython@d4fa707 · GitHub
Skip to content

Commit d4fa707

Browse files
gh-139707: Add mechanism for distributors to supply error messages for missing stdlib modules (GH-140783)
1 parent b708485 commit d4fa707

9 files changed

Lines changed: 149 additions & 2 deletions

File tree

Doc/using/configure.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,30 @@ General Options
322322

323323
.. versionadded:: 3.11
324324

325+
.. option:: --with-missing-stdlib-config=FILE
326+
327+
Path to a `JSON <https://www.json.org/json-en.html>`_ configuration file
328+
containing custom error messages for missing :term:`standard library` modules.
329+
330+
This option is intended for Python distributors who wish to provide
331+
distribution-specific guidance when users encounter standard library
332+
modules that are missing or packaged separately.
333+
334+
The JSON file should map missing module names to custom error message strings.
335+
For example, if your distribution packages :mod:`tkinter` and
336+
:mod:`_tkinter` separately and excludes :mod:`!_gdbm` for legal reasons,
337+
the configuration could contain:
338+
339+
.. code-block:: json
340+
341+
{
342+
"_gdbm": "The '_gdbm' module is not available in this distribution"
343+
"tkinter": "Install the python-tk package to use tkinter",
344+
"_tkinter": "Install the python-tk package to use tkinter",
345+
}
346+
347+
.. versionadded:: next
348+
325349
.. option:: --enable-pystats
326350

327351
Turn on internal Python performance statistics gathering.

Doc/whatsnew/3.15.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,12 @@ Build changes
12471247
set to ``no`` or with :option:`!--without-system-libmpdec`.
12481248
(Contributed by Sergey B Kirpichev in :gh:`115119`.)
12491249

1250+
* The new configure option :option:`--with-missing-stdlib-config=FILE` allows
1251+
distributors to pass a `JSON <https://www.json.org/json-en.html>`_
1252+
configuration file containing custom error messages for :term:`standard library`
1253+
modules that are missing or packaged separately.
1254+
(Contributed by Stan Ulbrych and Petr Viktorin in :gh:`139707`.)
1255+
12501256

12511257
Porting to Python 3.15
12521258
======================

Lib/test/test_traceback.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5051,7 +5051,7 @@ def test_no_site_package_flavour(self):
50515051
b"or to enable your virtual environment?"), stderr
50525052
)
50535053

5054-
def test_missing_stdlib_package(self):
5054+
def test_missing_stdlib_module(self):
50555055
code = """
50565056
import sys
50575057
sys.stdlib_module_names |= {'spam'}
@@ -5061,6 +5061,27 @@ def test_missing_stdlib_package(self):
50615061

50625062
self.assertIn(b"Standard library module 'spam' was not found", stderr)
50635063

5064+
code = """
5065+
import sys
5066+
import traceback
5067+
traceback._MISSING_STDLIB_MODULE_MESSAGES = {'spam': "Install 'spam4life' for 'spam'"}
5068+
sys.stdlib_module_names |= {'spam'}
5069+
import spam
5070+
"""
5071+
_, _, stderr = assert_python_failure('-S', '-c', code)
5072+
5073+
self.assertIn(b"Install 'spam4life' for 'spam'", stderr)
5074+
5075+
@unittest.skipIf(sys.platform == "win32", "Non-Windows test")
5076+
def test_windows_only_module_error(self):
5077+
try:
5078+
import msvcrt # noqa: F401
5079+
except ModuleNotFoundError:
5080+
formatted = traceback.format_exc()
5081+
self.assertIn("Unsupported platform for Windows-only standard library module 'msvcrt'", formatted)
5082+
else:
5083+
self.fail("ModuleNotFoundError was not raised")
5084+
50645085

50655086
class TestColorizedTraceback(unittest.TestCase):
50665087
maxDiff = None

Lib/traceback.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414

1515
from contextlib import suppress
1616

17+
try:
18+
from _missing_stdlib_info import _MISSING_STDLIB_MODULE_MESSAGES
19+
except ImportError:
20+
_MISSING_STDLIB_MODULE_MESSAGES = {}
21+
1722
__all__ = ['extract_stack', 'extract_tb', 'format_exception',
1823
'format_exception_only', 'format_list', 'format_stack',
1924
'format_tb', 'print_exc', 'format_exc', 'print_exception',
@@ -1110,7 +1115,11 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
11101115
elif exc_type and issubclass(exc_type, ModuleNotFoundError):
11111116
module_name = getattr(exc_value, "name", None)
11121117
if module_name in sys.stdlib_module_names:
1113-
self._str = f"Standard library module '{module_name}' was not found"
1118+
message = _MISSING_STDLIB_MODULE_MESSAGES.get(
1119+
module_name,
1120+
f"Standard library module {module_name!r} was not found"
1121+
)
1122+
self._str = message
11141123
elif sys.flags.no_site:
11151124
self._str += (". Site initialization is disabled, did you forget to "
11161125
+ "add the site-packages directory to sys.path "

Makefile.pre.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,6 +1604,11 @@ sharedmods: $(SHAREDMODS) pybuilddir.txt
16041604
# dependency on BUILDPYTHON ensures that the target is run last
16051605
.PHONY: checksharedmods
16061606
checksharedmods: sharedmods $(PYTHON_FOR_BUILD_DEPS) $(BUILDPYTHON)
1607+
@if [ -n "@MISSING_STDLIB_CONFIG@" ]; then \
1608+
$(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/Tools/build/check_extension_modules.py --generate-missing-stdlib-info --with-missing-stdlib-config="@MISSING_STDLIB_CONFIG@"; \
1609+
else \
1610+
$(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/Tools/build/check_extension_modules.py --generate-missing-stdlib-info; \
1611+
fi
16071612
@$(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/Tools/build/check_extension_modules.py
16081613

16091614
.PHONY: rundsymutil
@@ -2820,6 +2825,7 @@ libinstall: all $(srcdir)/Modules/xxmodule.c
28202825
$(INSTALL_DATA) `cat pybuilddir.txt`/_sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH).py $(DESTDIR)$(LIBDEST); \
28212826
$(INSTALL_DATA) `cat pybuilddir.txt`/_sysconfig_vars_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH).json $(DESTDIR)$(LIBDEST); \
28222827
$(INSTALL_DATA) `cat pybuilddir.txt`/build-details.json $(DESTDIR)$(LIBDEST); \
2828+
$(INSTALL_DATA) `cat pybuilddir.txt`/_missing_stdlib_info.py $(DESTDIR)$(LIBDEST); \
28232829
$(INSTALL_DATA) $(srcdir)/LICENSE $(DESTDIR)$(LIBDEST)/LICENSE.txt
28242830
@ # If app store compliance has been configured, apply the patch to the
28252831
@ # installed library code. The patch has been previously validated against
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Add configure option :option:`--with-missing-stdlib-config=FILE` allows
2+
which distributors to pass a `JSON <https://www.json.org/json-en.html>`_
3+
configuration file containing custom error messages for missing
4+
:term:`standard library` modules.

Tools/build/check_extension_modules.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323
import _imp
2424
import argparse
2525
import enum
26+
import json
2627
import logging
2728
import os
2829
import pathlib
30+
import pprint
2931
import re
3032
import sys
3133
import sysconfig
@@ -116,6 +118,18 @@
116118
help="Print a list of module names to stdout and exit",
117119
)
118120

121+
parser.add_argument(
122+
"--generate-missing-stdlib-info",
123+
action="store_true",
124+
help="Generate file with stdlib module info",
125+
)
126+
127+
parser.add_argument(
128+
"--with-missing-stdlib-config",
129+
metavar="CONFIG_FILE",
130+
help="Path to JSON config file with custom missing module messages",
131+
)
132+
119133

120134
@enum.unique
121135
class ModuleState(enum.Enum):
@@ -281,6 +295,39 @@ def list_module_names(self, *, all: bool = False) -> set[str]:
281295
names.update(WINDOWS_MODULES)
282296
return names
283297

298+
def generate_missing_stdlib_info(self, config_path: str | None = None) -> None:
299+
config_messages = {}
300+
if config_path:
301+
try:
302+
with open(config_path, encoding='utf-8') as f:
303+
config_messages = json.load(f)
304+
except (FileNotFoundError, json.JSONDecodeError) as e:
305+
raise RuntimeError(f"Failed to load missing stdlib config {config_path!r}") from e
306+
307+
messages = {}
308+
for name in WINDOWS_MODULES:
309+
messages[name] = f"Unsupported platform for Windows-only standard library module {name!r}"
310+
311+
for modinfo in self.modules:
312+
if modinfo.state in (ModuleState.DISABLED, ModuleState.DISABLED_SETUP):
313+
messages[modinfo.name] = f"Standard library module disabled during build {modinfo.name!r} was not found"
314+
elif modinfo.state == ModuleState.NA:
315+
messages[modinfo.name] = f"Unsupported platform for standard library module {modinfo.name!r}"
316+
317+
messages.update(config_messages)
318+
319+
content = f'''\
320+
# Standard library information used by the traceback module for more informative
321+
# ModuleNotFound error messages.
322+
# Generated by check_extension_modules.py
323+
324+
_MISSING_STDLIB_MODULE_MESSAGES = {pprint.pformat(messages)}
325+
'''
326+
327+
output_path = self.builddir / "_missing_stdlib_info.py"
328+
with open(output_path, "w", encoding="utf-8") as f:
329+
f.write(content)
330+
284331
def get_builddir(self) -> pathlib.Path:
285332
try:
286333
with open(self.pybuilddir_txt, encoding="utf-8") as f:
@@ -499,6 +546,9 @@ def main() -> None:
499546
names = checker.list_module_names(all=True)
500547
for name in sorted(names):
501548
print(name)
549+
elif args.generate_missing_stdlib_info:
550+
checker.check()
551+
checker.generate_missing_stdlib_info(args.with_missing_stdlib_config)
502552
else:
503553
checker.check()
504554
checker.summary(verbose=args.verbose)

configure

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

configure.ac

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,15 @@ if test "$with_pkg_config" = yes -a -z "$PKG_CONFIG"; then
307307
AC_MSG_ERROR([pkg-config is required])]
308308
fi
309309

310+
dnl Allow distributors to provide custom missing stdlib module error messages
311+
AC_ARG_WITH([missing-stdlib-config],
312+
[AS_HELP_STRING([--with-missing-stdlib-config=FILE],
313+
[File with custom module error messages for missing stdlib modules])],
314+
[MISSING_STDLIB_CONFIG="$withval"],
315+
[MISSING_STDLIB_CONFIG=""]
316+
)
317+
AC_SUBST([MISSING_STDLIB_CONFIG])
318+
310319
# Set name for machine-dependent library files
311320
AC_ARG_VAR([MACHDEP], [name for machine-dependent library files])
312321
AC_MSG_CHECKING([MACHDEP])

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy