Skip to content

Commit

Permalink
Check for script shadowing stdlib
Browse files Browse the repository at this point in the history
  • Loading branch information
hauntsaninja committed Jan 6, 2024
1 parent d479931 commit 8ebab58
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 24 deletions.
22 changes: 22 additions & 0 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,28 @@ def test_issue105979(self):
self.assertIn("Frozen object named 'x' is invalid",
str(cm.exception))

def test_delete_sys_stdlib_module_names(self):
args = ["-c", "import sys; del sys.stdlib_module_names; sys.stdlib_module_names"]
popen = script_helper.spawn_python(*args)
stdout, stderr = popen.communicate()
self.assertIn(
b"AttributeError: module 'sys' has no attribute 'stdlib_module_names'",
stdout
)

def test_cwd_script_shadowing_stdlib(self):
with CleanImport('collections'):
import collections
collections.__spec__ = types.SimpleNamespace()
collections.__spec__.origin = os.path.join(os.getcwd(), 'collections.py')
with self.assertRaisesRegex(
AttributeError,
r"module 'collections' has no attribute 'does_not_exist' \(most "
r"likely due to '.*collections.py' shadowing the standard "
r"library module named 'collections'\)"
):
collections.does_not_exist


@skip_if_dont_write_bytecode
class FilePermissionTests(unittest.TestCase):
Expand Down
112 changes: 88 additions & 24 deletions Objects/moduleobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -848,39 +848,103 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress)
Py_DECREF(origin);
}

int rc = _PyModuleSpec_IsInitializing(spec);
if (rc > 0) {
if (valid_origin == 1) {
PyErr_Format(PyExc_AttributeError,
"partially initialized "
"module '%U' from '%U' has no attribute '%U' "
"(most likely due to a circular import)",
mod_name, origin, name);
}
else {
PyErr_Format(PyExc_AttributeError,
"partially initialized "
"module '%U' has no attribute '%U' "
"(most likely due to a circular import)",
mod_name, name);
int is_script_shadowing_stdlib = 0;
// Check mod.__name__ in sys.stdlib_module_names
// and os.path.dirname(mod.__spec__.origin) == os.getcwd()
PyObject *stdlib = NULL;
if (valid_origin == 1) {
if (
// avoid bad recursion
PyUnicode_CompareWithASCIIString(mod_name, "sys") != 0
&& PyUnicode_CompareWithASCIIString(mod_name, "os") != 0
&& PyUnicode_CompareWithASCIIString(mod_name, "builtins") != 0
) {
stdlib = _PyImport_GetModuleAttrString("sys", "stdlib_module_names");
if (!stdlib) {
goto done;
}
if (PySequence_Contains(stdlib, mod_name)) {
PyObject *os_path = _PyImport_GetModuleAttrString("os", "path");
if (!os_path) {
goto done;
}
PyObject *dirname = PyObject_GetAttrString(os_path, "dirname");
Py_DECREF(os_path);
if (!dirname) {
goto done;
}
PyObject *origin_dir = _PyObject_CallOneArg(dirname, origin);
Py_DECREF(dirname);
if (!origin_dir) {
goto done;
}

PyObject *getcwd = _PyImport_GetModuleAttrString("os", "getcwd");
if (!getcwd) {
Py_DECREF(origin_dir);
goto done;
}
PyObject *cwd = _PyObject_CallNoArgs(getcwd);
Py_DECREF(getcwd);
if (!cwd) {
Py_DECREF(origin_dir);
goto done;
}

is_script_shadowing_stdlib = PyObject_RichCompareBool(origin_dir, cwd, Py_EQ);
Py_DECREF(origin_dir);
Py_DECREF(cwd);
if (is_script_shadowing_stdlib < 0) {
goto done;
}
}
}
}
else if (rc == 0) {
rc = _PyModuleSpec_IsUninitializedSubmodule(spec, name);

if (is_script_shadowing_stdlib == 1) {
PyErr_Format(PyExc_AttributeError,
"module '%U' has no attribute '%U' "
"(most likely due to '%U' shadowing the standard library "
"module named '%U')",
mod_name, name, origin, mod_name);
} else {
int rc = _PyModuleSpec_IsInitializing(spec);
if (rc > 0) {
PyErr_Format(PyExc_AttributeError,
"cannot access submodule '%U' of module '%U' "
"(most likely due to a circular import)",
name, mod_name);
if (valid_origin == 1) {
PyErr_Format(PyExc_AttributeError,
"partially initialized "
"module '%U' from '%U' has no attribute '%U' "
"(most likely due to a circular import)",
mod_name, origin, name);
}
else {
PyErr_Format(PyExc_AttributeError,
"partially initialized "
"module '%U' has no attribute '%U' "
"(most likely due to a circular import)",
mod_name, name);
}
}
else if (rc == 0) {
PyErr_Format(PyExc_AttributeError,
"module '%U' has no attribute '%U'",
mod_name, name);
rc = _PyModuleSpec_IsUninitializedSubmodule(spec, name);
if (rc > 0) {
PyErr_Format(PyExc_AttributeError,
"cannot access submodule '%U' of module '%U' "
"(most likely due to a circular import)",
name, mod_name);
}
else if (rc == 0) {
PyErr_Format(PyExc_AttributeError,
"module '%U' has no attribute '%U'",
mod_name, name);
}
}
}

done:
Py_XDECREF(spec);
Py_XDECREF(origin);
Py_XDECREF(stdlib);
Py_DECREF(mod_name);
return NULL;
}
Expand Down

0 comments on commit 8ebab58

Please sign in to comment.