Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
PoC for checking both __loader__ and __spec__.loader
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
#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
import warnings

import bar

warnings.warn_explicit(
    'warning!',
    RuntimeWarning,
    'bar.py', 2,
    module='bar knee',
    module_globals=bar.__dict__,
    )
```

```python
import sys
import os
import pathlib

```

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
```
  • Loading branch information
warsaw committed Oct 3, 2022
commit 37b581aa480cb309fa8f68f649802af96f6d25c5
48 changes: 41 additions & 7 deletions Python/_warnings.c
Original file line number Diff line number Diff line change
Expand Up @@ -977,25 +977,59 @@ warnings_warn_impl(PyObject *module, PyObject *message, PyObject *category,
static PyObject *
get_source_line(PyInterpreterState *interp, PyObject *module_globals, int lineno)
{
PyObject *loader;
PyObject *loader, *spec_loader;
PyObject *spec;
PyObject *module_name;
PyObject *get_source;
PyObject *source;
PyObject *source_list;
PyObject *source_line;

/* Check/get the requisite pieces needed for the loader. */
module_name = _PyDict_GetItemWithError(module_globals, &_Py_ID(__name__));
if (!module_name) {
return NULL;
}
Py_INCREF(module_name);

/* Check/get the requisite pieces needed for the loader. Get both the
__spec__.loader and __loader__ attributes and warn if they are not the
same.
*/
spec = _PyDict_GetItemWithError(module_globals, &_Py_ID(__spec__));
if (spec == NULL) {
Py_DECREF(module_name);
return NULL;
Comment thread
warsaw marked this conversation as resolved.
Outdated
}
Py_INCREF(spec);

spec_loader = PyObject_GetAttrString(spec, "loader");
Py_DECREF(spec);
if (spec_loader == NULL) {
Py_DECREF(module_name);
return NULL;
}

loader = _PyDict_GetItemWithError(module_globals, &_Py_ID(__loader__));
if (loader == NULL) {
Py_DECREF(spec_loader);
Py_DECREF(module_name);
return NULL;
}
Py_INCREF(loader);
module_name = _PyDict_GetItemWithError(module_globals, &_Py_ID(__name__));
if (!module_name) {
Py_DECREF(loader);
return NULL;

if (spec_loader != loader) {
int error = PyErr_WarnFormat(
PyExc_ImportWarning, 2,
"Module %U; __loader__ != __spec__.loader (%R != %R)",
module_name, spec_loader, loader);
if (error < 0) {
Py_DECREF(module_name);
Py_DECREF(spec_loader);
Py_DECREF(loader);
return NULL;
}
}
Py_INCREF(module_name);
Py_DECREF(spec_loader);

/* Make sure the loader implements the optional get_source() method. */
(void)_PyObject_LookupAttr(loader, &_Py_ID(get_source), &get_source);
Expand Down