@@ -848,39 +848,103 @@ _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+ goto done ;
865+ }
866+ if (PySequence_Contains (stdlib , mod_name )) {
867+ PyObject * os_path = _PyImport_GetModuleAttrString ("os" , "path" );
868+ if (!os_path ) {
869+ goto done ;
870+ }
871+ PyObject * dirname = PyObject_GetAttrString (os_path , "dirname" );
872+ Py_DECREF (os_path );
873+ if (!dirname ) {
874+ goto done ;
875+ }
876+ PyObject * origin_dir = _PyObject_CallOneArg (dirname , origin );
877+ Py_DECREF (dirname );
878+ if (!origin_dir ) {
879+ goto done ;
880+ }
881+
882+ PyObject * getcwd = _PyImport_GetModuleAttrString ("os" , "getcwd" );
883+ if (!getcwd ) {
884+ Py_DECREF (origin_dir );
885+ goto done ;
886+ }
887+ PyObject * cwd = _PyObject_CallNoArgs (getcwd );
888+ Py_DECREF (getcwd );
889+ if (!cwd ) {
890+ Py_DECREF (origin_dir );
891+ goto done ;
892+ }
893+
894+ is_script_shadowing_stdlib = PyObject_RichCompareBool (origin_dir , cwd , Py_EQ );
895+ Py_DECREF (origin_dir );
896+ Py_DECREF (cwd );
897+ if (is_script_shadowing_stdlib < 0 ) {
898+ goto done ;
899+ }
900+ }
866901 }
867902 }
868- else if (rc == 0 ) {
869- rc = _PyModuleSpec_IsUninitializedSubmodule (spec , name );
903+
904+ if (is_script_shadowing_stdlib == 1 ) {
905+ PyErr_Format (PyExc_AttributeError ,
906+ "module '%U' has no attribute '%U' "
907+ "(most likely due to '%U' shadowing the standard library "
908+ "module named '%U')" ,
909+ mod_name , name , origin , mod_name );
910+ } else {
911+ int rc = _PyModuleSpec_IsInitializing (spec );
870912 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 );
913+ if (valid_origin == 1 ) {
914+ PyErr_Format (PyExc_AttributeError ,
915+ "partially initialized "
916+ "module '%U' from '%U' has no attribute '%U' "
917+ "(most likely due to a circular import)" ,
918+ mod_name , origin , name );
919+ }
920+ else {
921+ PyErr_Format (PyExc_AttributeError ,
922+ "partially initialized "
923+ "module '%U' has no attribute '%U' "
924+ "(most likely due to a circular import)" ,
925+ mod_name , name );
926+ }
875927 }
876928 else if (rc == 0 ) {
877- PyErr_Format (PyExc_AttributeError ,
878- "module '%U' has no attribute '%U'" ,
879- mod_name , name );
929+ rc = _PyModuleSpec_IsUninitializedSubmodule (spec , name );
930+ if (rc > 0 ) {
931+ PyErr_Format (PyExc_AttributeError ,
932+ "cannot access submodule '%U' of module '%U' "
933+ "(most likely due to a circular import)" ,
934+ name , mod_name );
935+ }
936+ else if (rc == 0 ) {
937+ PyErr_Format (PyExc_AttributeError ,
938+ "module '%U' has no attribute '%U'" ,
939+ mod_name , name );
940+ }
880941 }
881942 }
943+
944+ done :
882945 Py_XDECREF (spec );
883946 Py_XDECREF (origin );
947+ Py_XDECREF (stdlib );
884948 Py_DECREF (mod_name );
885949 return NULL ;
886950}
0 commit comments