summaryrefslogtreecommitdiffstats
path: root/Lib/importlib/_bootstrap_external.py
diff options
context:
space:
mode:
authorBarry Warsaw <barry@python.org>2022-10-07 02:32:53 (GMT)
committerGitHub <noreply@github.com>2022-10-07 02:32:53 (GMT)
commit13d44891426e0faf165f974f2e46907ab5b645a9 (patch)
tree3f86e88601b8a5b551b854d30396ce93f87d17f3 /Lib/importlib/_bootstrap_external.py
parent27369ef56ffed8ab68abc201a52b259a065ed8d7 (diff)
downloadcpython-13d44891426e0faf165f974f2e46907ab5b645a9.zip
cpython-13d44891426e0faf165f974f2e46907ab5b645a9.tar.gz
cpython-13d44891426e0faf165f974f2e46907ab5b645a9.tar.bz2
gh-86298: Ensure that __loader__ and __spec__.loader agree in warnings.warn_explicit() (GH-97803)
In `_warnings.c`, in the C equivalent of `warnings.warn_explicit()`, if the module globals are given (and not None), the warning will attempt to get the source line for the issued warning. To do this, it needs the module's loader. Previously, it would only look up `__loader__` in the module globals. In https://github.com/python/cpython/issues/86298 we want to defer to the `__spec__.loader` if available. The first step on this journey is to check that `loader == __spec__.loader` and issue another warning if it is not. This commit does that. Since this is a PoC, only manual testing for now. ```python # /tmp/foo.py import warnings import bar warnings.warn_explicit( 'warning!', RuntimeWarning, 'bar.py', 2, module='bar knee', module_globals=bar.__dict__, ) ``` ```python # /tmp/bar.py import sys import os import pathlib # __loader__ = pathlib.Path() ``` Then running this: `./python.exe -Wdefault /tmp/foo.py` Produces: ``` bar.py:2: RuntimeWarning: warning! import os ``` Uncomment the `__loader__ = ` line in `bar.py` and try it again: ``` sys:1: ImportWarning: Module bar; __loader__ != __spec__.loader (<_frozen_importlib_external.SourceFileLoader object at 0x109f7dfa0> != PosixPath('.')) bar.py:2: RuntimeWarning: warning! import os ``` Automerge-Triggered-By: GH:warsaw
Diffstat (limited to 'Lib/importlib/_bootstrap_external.py')
-rw-r--r--Lib/importlib/_bootstrap_external.py48
1 files changed, 48 insertions, 0 deletions
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
index 39d63ae..fc50e705 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -862,6 +862,54 @@ def spec_from_file_location(name, location=None, *, loader=None,
return spec
+def _bless_my_loader(module_globals):
+ """Helper function for _warnings.c
+
+ See GH#97850 for details.
+ """
+ # 2022-10-06(warsaw): For now, this helper is only used in _warnings.c and
+ # that use case only has the module globals. This function could be
+ # extended to accept either that or a module object. However, in the
+ # latter case, it would be better to raise certain exceptions when looking
+ # at a module, which should have either a __loader__ or __spec__.loader.
+ # For backward compatibility, it is possible that we'll get an empty
+ # dictionary for the module globals, and that cannot raise an exception.
+ if not isinstance(module_globals, dict):
+ return None
+
+ missing = object()
+ loader = module_globals.get('__loader__', None)
+ spec = module_globals.get('__spec__', missing)
+
+ if loader is None:
+ if spec is missing:
+ # If working with a module:
+ # raise AttributeError('Module globals is missing a __spec__')
+ return None
+ elif spec is None:
+ raise ValueError('Module globals is missing a __spec__.loader')
+
+ spec_loader = getattr(spec, 'loader', missing)
+
+ if spec_loader in (missing, None):
+ if loader is None:
+ exc = AttributeError if spec_loader is missing else ValueError
+ raise exc('Module globals is missing a __spec__.loader')
+ _warnings.warn(
+ 'Module globals is missing a __spec__.loader',
+ DeprecationWarning)
+ spec_loader = loader
+
+ assert spec_loader is not None
+ if loader is not None and loader != spec_loader:
+ _warnings.warn(
+ 'Module globals; __loader__ != __spec__.loader',
+ DeprecationWarning)
+ return loader
+
+ return spec_loader
+
+
# Loaders #####################################################################
class WindowsRegistryFinder: