https://github.com/python/cpython/commit/faa3dc7c64b7bda4e74c7630be4c873e0b58a569
commit: faa3dc7c64b7bda4e74c7630be4c873e0b58a569
branch: main
author: Kumar Aditya <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-01-06T22:09:18+05:30
summary:

gh-143469: enable `LOAD_ATTR_MODULE` specialization even if `__getattr__` is 
defined (#143470)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-01-06-12-30-03.gh-issue-143469.vHVhEY.rst
M Lib/test/test_opcache.py
M Python/specialize.c

diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py
index be9b52b8bc45e2..0344f08faecce7 100644
--- a/Lib/test/test_opcache.py
+++ b/Lib/test/test_opcache.py
@@ -1909,6 +1909,44 @@ class MyList(list): pass
         self.assert_no_opcode(my_list_append, "CALL_LIST_APPEND")
         self.assert_no_opcode(my_list_append, "CALL")
 
+    @cpython_only
+    @requires_specialization_ft
+    def test_load_attr_module_with_getattr(self):
+        module = types.ModuleType("test_module_with_getattr")
+        module.__dict__["some_attr"] = "foo"
+
+        def module_getattr(name):
+            if name == "missing_attr":
+                return 42
+            raise AttributeError(f"module has no attribute {name}")
+
+        module.__dict__["__getattr__"] = module_getattr
+
+        import sys
+        sys.modules.pop("test_module_with_getattr", None)
+        sys.modules["test_module_with_getattr"] = module
+        try:
+            def load_module_attr_present():
+                import test_module_with_getattr
+                return test_module_with_getattr.some_attr
+
+            for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
+                self.assertEqual(load_module_attr_present(), "foo")
+
+            self.assert_specialized(load_module_attr_present, 
"LOAD_ATTR_MODULE")
+            self.assert_no_opcode(load_module_attr_present, "LOAD_ATTR")
+
+            def load_module_attr_missing():
+                import test_module_with_getattr
+                return test_module_with_getattr.missing_attr
+
+            for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
+                self.assertEqual(load_module_attr_missing(), 42)
+
+            self.assert_no_opcode(load_module_attr_missing, "LOAD_ATTR_MODULE")
+        finally:
+            sys.modules.pop("test_module_with_getattr", None)
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-01-06-12-30-03.gh-issue-143469.vHVhEY.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-06-12-30-03.gh-issue-143469.vHVhEY.rst
new file mode 100644
index 00000000000000..9bac54497f8998
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-06-12-30-03.gh-issue-143469.vHVhEY.rst
@@ -0,0 +1 @@
+Enable :opcode:`!LOAD_ATTR_MODULE` specialization even if :func:`!__getattr__` 
is defined in module.
diff --git a/Python/specialize.c b/Python/specialize.c
index 62f0373a4c274d..b7fa5f8ad4c04e 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -366,14 +366,8 @@ specialize_module_load_attr_lock_held(PyDictObject *dict, 
_Py_CODEUNIT *instr, P
         SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NON_STRING);
         return -1;
     }
-    Py_ssize_t index = _PyDict_LookupIndex(dict, &_Py_ID(__getattr__));
+    Py_ssize_t index = _PyDict_LookupIndex(dict, name);
     assert(index != DKIX_ERROR);
-    if (index != DKIX_EMPTY) {
-        SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_MODULE_ATTR_NOT_FOUND);
-        return -1;
-    }
-    index = _PyDict_LookupIndex(dict, name);
-    assert (index != DKIX_ERROR);
     if (index != (uint16_t)index) {
         SPECIALIZATION_FAIL(LOAD_ATTR,
                             index == DKIX_EMPTY ?

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to