Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6bb3ecb

Browse files
committed
Check for script shadowing stdlib
1 parent d479931 commit 6bb3ecb

File tree

2 files changed

+126
-24
lines changed

2 files changed

+126
-24
lines changed

Lib/test/test_import/__init__.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,40 @@ def test_issue105979(self):
790790
self.assertIn("Frozen object named 'x' is invalid",
791791
str(cm.exception))
792792

793+
def test_cwd_script_shadowing_stdlib(self):
794+
with CleanImport('collections'):
795+
import collections
796+
collections.__spec__ = types.SimpleNamespace()
797+
collections.__spec__.origin = os.path.join(os.getcwd(), 'collections.py')
798+
with self.assertRaisesRegex(
799+
AttributeError,
800+
r"module 'collections' has no attribute 'does_not_exist' \(most "
801+
r"likely due to '.*collections.py' shadowing the standard "
802+
r"library module named 'collections'\)"
803+
):
804+
collections.does_not_exist
805+
806+
def test_delete_sys_stdlib_module_names(self):
807+
with CleanImport('collections'):
808+
import collections
809+
collections.__spec__ = types.SimpleNamespace()
810+
collections.__spec__.origin = os.path.join(os.getcwd(), 'collections.py')
811+
with CleanImport('sys'):
812+
import sys
813+
sys.stdlib_module_names = None
814+
with self.assertRaisesRegex(
815+
AttributeError,
816+
r"module 'collections' has no attribute 'does_not_exist'"
817+
):
818+
collections.does_not_exist
819+
820+
del sys.stdlib_module_names
821+
with self.assertRaisesRegex(
822+
AttributeError,
823+
r"module 'collections' has no attribute 'does_not_exist'"
824+
):
825+
collections.does_not_exist
826+
793827

794828
@skip_if_dont_write_bytecode
795829
class FilePermissionTests(unittest.TestCase):

Objects/moduleobject.c

Lines changed: 92 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -848,39 +848,107 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress)
848848
Py_DECREF(origin);
849849
}
850850

851-
int rc = _PyModuleSpec_IsInitializing(spec);
852-
if (rc > 0) {
853-
if (valid_origin == 1) {
854-
PyErr_Format(PyExc_AttributeError,
855-
"partially initialized "
856-
"module '%U' from '%U' has no attribute '%U' "
857-
"(most likely due to a circular import)",
858-
mod_name, origin, name);
859-
}
860-
else {
861-
PyErr_Format(PyExc_AttributeError,
862-
"partially initialized "
863-
"module '%U' has no attribute '%U' "
864-
"(most likely due to a circular import)",
865-
mod_name, name);
851+
int is_script_shadowing_stdlib = 0;
852+
// Check mod.__name__ in sys.stdlib_module_names
853+
// and os.path.dirname(mod.__spec__.origin) == os.getcwd()
854+
PyObject *stdlib = NULL;
855+
if (valid_origin == 1) {
856+
if (
857+
// avoid bad recursion
858+
PyUnicode_CompareWithASCIIString(mod_name, "sys") != 0
859+
&& PyUnicode_CompareWithASCIIString(mod_name, "os") != 0
860+
&& PyUnicode_CompareWithASCIIString(mod_name, "builtins") != 0
861+
) {
862+
stdlib = _PyImport_GetModuleAttrString("sys", "stdlib_module_names");
863+
if (!stdlib) {
864+
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
865+
PyErr_Clear();
866+
} else {
867+
goto done;
868+
}
869+
}
870+
if (stdlib && PyFrozenSet_Check(stdlib) && PySet_Contains(stdlib, mod_name)) {
871+
PyObject *os_path = _PyImport_GetModuleAttrString("os", "path");
872+
if (!os_path) {
873+
goto done;
874+
}
875+
PyObject *dirname = PyObject_GetAttrString(os_path, "dirname");
876+
Py_DECREF(os_path);
877+
if (!dirname) {
878+
goto done;
879+
}
880+
PyObject *origin_dir = _PyObject_CallOneArg(dirname, origin);
881+
Py_DECREF(dirname);
882+
if (!origin_dir) {
883+
goto done;
884+
}
885+
886+
PyObject *getcwd = _PyImport_GetModuleAttrString("os", "getcwd");
887+
if (!getcwd) {
888+
Py_DECREF(origin_dir);
889+
goto done;
890+
}
891+
PyObject *cwd = _PyObject_CallNoArgs(getcwd);
892+
Py_DECREF(getcwd);
893+
if (!cwd) {
894+
Py_DECREF(origin_dir);
895+
goto done;
896+
}
897+
898+
is_script_shadowing_stdlib = PyObject_RichCompareBool(origin_dir, cwd, Py_EQ);
899+
Py_DECREF(origin_dir);
900+
Py_DECREF(cwd);
901+
if (is_script_shadowing_stdlib < 0) {
902+
goto done;
903+
}
904+
}
866905
}
867906
}
868-
else if (rc == 0) {
869-
rc = _PyModuleSpec_IsUninitializedSubmodule(spec, name);
907+
908+
if (is_script_shadowing_stdlib == 1) {
909+
PyErr_Format(PyExc_AttributeError,
910+
"module '%U' has no attribute '%U' "
911+
"(most likely due to '%U' shadowing the standard library "
912+
"module named '%U')",
913+
mod_name, name, origin, mod_name);
914+
} else {
915+
int rc = _PyModuleSpec_IsInitializing(spec);
870916
if (rc > 0) {
871-
PyErr_Format(PyExc_AttributeError,
872-
"cannot access submodule '%U' of module '%U' "
873-
"(most likely due to a circular import)",
874-
name, mod_name);
917+
if (valid_origin == 1) {
918+
PyErr_Format(PyExc_AttributeError,
919+
"partially initialized "
920+
"module '%U' from '%U' has no attribute '%U' "
921+
"(most likely due to a circular import)",
922+
mod_name, origin, name);
923+
}
924+
else {
925+
PyErr_Format(PyExc_AttributeError,
926+
"partially initialized "
927+
"module '%U' has no attribute '%U' "
928+
"(most likely due to a circular import)",
929+
mod_name, name);
930+
}
875931
}
876932
else if (rc == 0) {
877-
PyErr_Format(PyExc_AttributeError,
878-
"module '%U' has no attribute '%U'",
879-
mod_name, name);
933+
rc = _PyModuleSpec_IsUninitializedSubmodule(spec, name);
934+
if (rc > 0) {
935+
PyErr_Format(PyExc_AttributeError,
936+
"cannot access submodule '%U' of module '%U' "
937+
"(most likely due to a circular import)",
938+
name, mod_name);
939+
}
940+
else if (rc == 0) {
941+
PyErr_Format(PyExc_AttributeError,
942+
"module '%U' has no attribute '%U'",
943+
mod_name, name);
944+
}
880945
}
881946
}
947+
948+
done:
882949
Py_XDECREF(spec);
883950
Py_XDECREF(origin);
951+
Py_XDECREF(stdlib);
884952
Py_DECREF(mod_name);
885953
return NULL;
886954
}

0 commit comments

Comments
 (0)