summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorShantanu <12621235+hauntsaninja@users.noreply.github.com>2024-10-24 19:11:12 (GMT)
committerGitHub <noreply@github.com>2024-10-24 19:11:12 (GMT)
commit500f5338a8fe13719478589333fcd296e8e8eb02 (patch)
treeb44c7630f1d618e01647d1e8c01eeecde706586c /Python
parent3f24bde0b6689b8f05872a8118a97908b5a94659 (diff)
downloadcpython-500f5338a8fe13719478589333fcd296e8e8eb02.zip
cpython-500f5338a8fe13719478589333fcd296e8e8eb02.tar.gz
cpython-500f5338a8fe13719478589333fcd296e8e8eb02.tar.bz2
gh-123930: Better error for "from imports" when script shadows module (#123929)
Diffstat (limited to 'Python')
-rw-r--r--Python/ceval.c150
1 files changed, 103 insertions, 47 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index ece7ef1..beee532 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -2802,7 +2802,7 @@ PyObject *
_PyEval_ImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name)
{
PyObject *x;
- PyObject *fullmodname, *pkgname, *pkgpath, *pkgname_or_unknown, *errmsg;
+ PyObject *fullmodname, *mod_name, *origin, *mod_name_or_unknown, *errmsg, *spec;
if (PyObject_GetOptionalAttr(v, name, &x) != 0) {
return x;
@@ -2810,16 +2810,16 @@ _PyEval_ImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name)
/* Issue #17636: in case this failed because of a circular relative
import, try to fallback on reading the module directly from
sys.modules. */
- if (PyObject_GetOptionalAttr(v, &_Py_ID(__name__), &pkgname) < 0) {
+ if (PyObject_GetOptionalAttr(v, &_Py_ID(__name__), &mod_name) < 0) {
return NULL;
}
- if (pkgname == NULL || !PyUnicode_Check(pkgname)) {
- Py_CLEAR(pkgname);
+ if (mod_name == NULL || !PyUnicode_Check(mod_name)) {
+ Py_CLEAR(mod_name);
goto error;
}
- fullmodname = PyUnicode_FromFormat("%U.%U", pkgname, name);
+ fullmodname = PyUnicode_FromFormat("%U.%U", mod_name, name);
if (fullmodname == NULL) {
- Py_DECREF(pkgname);
+ Py_DECREF(mod_name);
return NULL;
}
x = PyImport_GetModule(fullmodname);
@@ -2827,63 +2827,121 @@ _PyEval_ImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name)
if (x == NULL && !_PyErr_Occurred(tstate)) {
goto error;
}
- Py_DECREF(pkgname);
+ Py_DECREF(mod_name);
return x;
+
error:
- if (pkgname == NULL) {
- pkgname_or_unknown = PyUnicode_FromString("<unknown module name>");
- if (pkgname_or_unknown == NULL) {
+ if (mod_name == NULL) {
+ mod_name_or_unknown = PyUnicode_FromString("<unknown module name>");
+ if (mod_name_or_unknown == NULL) {
return NULL;
}
} else {
- pkgname_or_unknown = pkgname;
+ mod_name_or_unknown = mod_name;
}
+ // mod_name is no longer an owned reference
+ assert(mod_name_or_unknown);
+ assert(mod_name == NULL || mod_name == mod_name_or_unknown);
- pkgpath = NULL;
- if (PyModule_Check(v)) {
- pkgpath = PyModule_GetFilenameObject(v);
- if (pkgpath == NULL) {
- if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
- Py_DECREF(pkgname_or_unknown);
- return NULL;
+ origin = NULL;
+ if (PyObject_GetOptionalAttr(v, &_Py_ID(__spec__), &spec) < 0) {
+ Py_DECREF(mod_name_or_unknown);
+ return NULL;
+ }
+ if (spec == NULL) {
+ errmsg = PyUnicode_FromFormat(
+ "cannot import name %R from %R (unknown location)",
+ name, mod_name_or_unknown
+ );
+ goto done_with_errmsg;
+ }
+ if (_PyModuleSpec_GetFileOrigin(spec, &origin) < 0) {
+ goto done;
+ }
+
+ int is_possibly_shadowing = _PyModule_IsPossiblyShadowing(origin);
+ if (is_possibly_shadowing < 0) {
+ goto done;
+ }
+ int is_possibly_shadowing_stdlib = 0;
+ if (is_possibly_shadowing) {
+ PyObject *stdlib_modules = PySys_GetObject("stdlib_module_names");
+ if (stdlib_modules && PyAnySet_Check(stdlib_modules)) {
+ is_possibly_shadowing_stdlib = PySet_Contains(stdlib_modules, mod_name_or_unknown);
+ if (is_possibly_shadowing_stdlib < 0) {
+ goto done;
}
- // module filename missing
- _PyErr_Clear(tstate);
}
}
- if (pkgpath == NULL || !PyUnicode_Check(pkgpath)) {
- Py_CLEAR(pkgpath);
+
+ if (is_possibly_shadowing_stdlib) {
+ assert(origin);
errmsg = PyUnicode_FromFormat(
- "cannot import name %R from %R (unknown location)",
- name, pkgname_or_unknown
+ "cannot import name %R from %R "
+ "(consider renaming %R since it has the same "
+ "name as the standard library module named %R "
+ "and prevents importing that standard library module)",
+ name, mod_name_or_unknown, origin, mod_name_or_unknown
);
}
else {
- PyObject *spec;
- int rc = PyObject_GetOptionalAttr(v, &_Py_ID(__spec__), &spec);
- if (rc > 0) {
- rc = _PyModuleSpec_IsInitializing(spec);
- Py_DECREF(spec);
- }
+ int rc = _PyModuleSpec_IsInitializing(spec);
if (rc < 0) {
- Py_DECREF(pkgname_or_unknown);
- Py_DECREF(pkgpath);
- return NULL;
+ goto done;
+ }
+ else if (rc > 0) {
+ if (is_possibly_shadowing) {
+ assert(origin);
+ // For non-stdlib modules, only mention the possibility of
+ // shadowing if the module is being initialized.
+ errmsg = PyUnicode_FromFormat(
+ "cannot import name %R from %R "
+ "(consider renaming %R if it has the same name "
+ "as a library you intended to import)",
+ name, mod_name_or_unknown, origin
+ );
+ }
+ else if (origin) {
+ errmsg = PyUnicode_FromFormat(
+ "cannot import name %R from partially initialized module %R "
+ "(most likely due to a circular import) (%S)",
+ name, mod_name_or_unknown, origin
+ );
+ }
+ else {
+ errmsg = PyUnicode_FromFormat(
+ "cannot import name %R from partially initialized module %R "
+ "(most likely due to a circular import)",
+ name, mod_name_or_unknown
+ );
+ }
+ }
+ else {
+ assert(rc == 0);
+ if (origin) {
+ errmsg = PyUnicode_FromFormat(
+ "cannot import name %R from %R (%S)",
+ name, mod_name_or_unknown, origin
+ );
+ }
+ else {
+ errmsg = PyUnicode_FromFormat(
+ "cannot import name %R from %R (unknown location)",
+ name, mod_name_or_unknown
+ );
+ }
}
- const char *fmt =
- rc ?
- "cannot import name %R from partially initialized module %R "
- "(most likely due to a circular import) (%S)" :
- "cannot import name %R from %R (%S)";
-
- errmsg = PyUnicode_FromFormat(fmt, name, pkgname_or_unknown, pkgpath);
}
- /* NULL checks for errmsg and pkgname done by PyErr_SetImportError. */
- _PyErr_SetImportErrorWithNameFrom(errmsg, pkgname, pkgpath, name);
- Py_XDECREF(errmsg);
- Py_DECREF(pkgname_or_unknown);
- Py_XDECREF(pkgpath);
+done_with_errmsg:
+ /* NULL checks for errmsg, mod_name, origin done by PyErr_SetImportError. */
+ _PyErr_SetImportErrorWithNameFrom(errmsg, mod_name, origin, name);
+ Py_DECREF(errmsg);
+
+done:
+ Py_XDECREF(origin);
+ Py_XDECREF(spec);
+ Py_DECREF(mod_name_or_unknown);
return NULL;
}
@@ -3243,5 +3301,3 @@ _PyEval_LoadName(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject *na
}
return value;
}
-
-