@@ -788,7 +788,7 @@ PyObject*
788788_Py_module_getattro_impl (PyModuleObject * m , PyObject * name , int suppress )
789789{
790790 // When suppress=1, this function suppresses AttributeError.
791- PyObject * attr , * mod_name , * getattr , * origin ;
791+ PyObject * attr , * mod_name , * getattr ;
792792 attr = _PyObject_GenericGetAttrWithDict ((PyObject * )m , name , NULL , suppress );
793793 if (attr ) {
794794 return attr ;
@@ -837,48 +837,120 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress)
837837 Py_DECREF (mod_name );
838838 return NULL ;
839839 }
840- int rc = _PyModuleSpec_IsInitializing ( spec ) ;
841- if (rc > 0 ) {
842- int valid_spec = PyObject_GetOptionalAttr (spec , & _Py_ID (origin ), & origin );
843- if (valid_spec == -1 ) {
844- Py_XDECREF (spec );
840+ PyObject * origin = NULL ;
841+ if (spec ) {
842+ int rc = PyObject_GetOptionalAttr (spec , & _Py_ID (origin ), & origin );
843+ if (rc == -1 ) {
844+ Py_DECREF (spec );
845845 Py_DECREF (mod_name );
846846 return NULL ;
847847 }
848- if (valid_spec == 1 && !PyUnicode_Check (origin )) {
849- valid_spec = 0 ;
850- Py_DECREF (origin );
851- }
852- if (valid_spec == 1 ) {
853- PyErr_Format (PyExc_AttributeError ,
854- "partially initialized "
855- "module '%U' from '%U' has no attribute '%U' "
856- "(most likely due to a circular import)" ,
857- mod_name , origin , name );
848+ if (rc == 1 && !PyUnicode_Check (origin )) {
858849 Py_DECREF (origin );
850+ origin = NULL ;
859851 }
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 );
852+ }
853+
854+ int is_script_shadowing_stdlib = 0 ;
855+ // Check mod.__name__ in sys.stdlib_module_names
856+ // and os.path.dirname(mod.__spec__.origin) == os.getcwd()
857+ PyObject * stdlib = NULL ;
858+ if (origin ) {
859+ if (
860+ // avoid bad recursion
861+ PyUnicode_CompareWithASCIIString (mod_name , "sys" ) != 0
862+ && PyUnicode_CompareWithASCIIString (mod_name , "os" ) != 0
863+ && PyUnicode_CompareWithASCIIString (mod_name , "builtins" ) != 0
864+ ) {
865+ stdlib = _PyImport_GetModuleAttrString ("sys" , "stdlib_module_names" );
866+ if (!stdlib ) {
867+ if (PyErr_ExceptionMatches (PyExc_AttributeError )) {
868+ PyErr_Clear ();
869+ } else {
870+ goto done ;
871+ }
872+ }
873+ if (stdlib && PyFrozenSet_Check (stdlib ) && PySet_Contains (stdlib , mod_name )) {
874+ PyObject * os_path = _PyImport_GetModuleAttrString ("os" , "path" );
875+ if (!os_path ) {
876+ goto done ;
877+ }
878+ PyObject * dirname = PyObject_GetAttrString (os_path , "dirname" );
879+ Py_DECREF (os_path );
880+ if (!dirname ) {
881+ goto done ;
882+ }
883+ PyObject * origin_dir = _PyObject_CallOneArg (dirname , origin );
884+ Py_DECREF (dirname );
885+ if (!origin_dir ) {
886+ goto done ;
887+ }
888+
889+ PyObject * getcwd = _PyImport_GetModuleAttrString ("os" , "getcwd" );
890+ if (!getcwd ) {
891+ Py_DECREF (origin_dir );
892+ goto done ;
893+ }
894+ PyObject * cwd = _PyObject_CallNoArgs (getcwd );
895+ Py_DECREF (getcwd );
896+ if (!cwd ) {
897+ Py_DECREF (origin_dir );
898+ goto done ;
899+ }
900+
901+ is_script_shadowing_stdlib = PyObject_RichCompareBool (origin_dir , cwd , Py_EQ );
902+ Py_DECREF (origin_dir );
903+ Py_DECREF (cwd );
904+ if (is_script_shadowing_stdlib < 0 ) {
905+ goto done ;
906+ }
907+ }
866908 }
867909 }
868- else if (rc == 0 ) {
869- rc = _PyModuleSpec_IsUninitializedSubmodule (spec , name );
910+
911+ if (is_script_shadowing_stdlib == 1 ) {
912+ PyErr_Format (PyExc_AttributeError ,
913+ "module '%U' has no attribute '%U' "
914+ "(most likely due to '%U' shadowing the standard library "
915+ "module named '%U')" ,
916+ mod_name , name , origin , mod_name );
917+ } else {
918+ int rc = _PyModuleSpec_IsInitializing (spec );
870919 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 );
920+ if (origin ) {
921+ PyErr_Format (PyExc_AttributeError ,
922+ "partially initialized "
923+ "module '%U' from '%U' has no attribute '%U' "
924+ "(most likely due to a circular import)" ,
925+ mod_name , origin , name );
926+ }
927+ else {
928+ PyErr_Format (PyExc_AttributeError ,
929+ "partially initialized "
930+ "module '%U' has no attribute '%U' "
931+ "(most likely due to a circular import)" ,
932+ mod_name , name );
933+ }
875934 }
876935 else if (rc == 0 ) {
877- PyErr_Format (PyExc_AttributeError ,
878- "module '%U' has no attribute '%U'" ,
879- mod_name , name );
936+ rc = _PyModuleSpec_IsUninitializedSubmodule (spec , name );
937+ if (rc > 0 ) {
938+ PyErr_Format (PyExc_AttributeError ,
939+ "cannot access submodule '%U' of module '%U' "
940+ "(most likely due to a circular import)" ,
941+ name , mod_name );
942+ }
943+ else if (rc == 0 ) {
944+ PyErr_Format (PyExc_AttributeError ,
945+ "module '%U' has no attribute '%U'" ,
946+ mod_name , name );
947+ }
880948 }
881949 }
950+
951+ done :
952+ Py_XDECREF (stdlib );
953+ Py_XDECREF (origin );
882954 Py_XDECREF (spec );
883955 Py_DECREF (mod_name );
884956 return NULL ;
0 commit comments