https://github.com/python/cpython/commit/e371ce10cd4c26bf5210a3fe2bf6fc7d87cc0efb
commit: e371ce10cd4c26bf5210a3fe2bf6fc7d87cc0efb
branch: main
author: Kumar Aditya <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-04-07T21:43:50+05:30
summary:

gh-95004: specialize access to enums and fix scaling on free-threading (#148184)

Co-authored-by: Ken Jin <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-04-06-18-25-53.gh-issue-95004.CQeT_H.rst
M Lib/test/test_opcache.py
M Python/specialize.c
M Tools/ftscalingbench/ftscalingbench.py

diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py
index 4ca108cd6ca43e..5f40ad1d7a0f24 100644
--- a/Lib/test/test_opcache.py
+++ b/Lib/test/test_opcache.py
@@ -2047,5 +2047,25 @@ def load_module_attr_missing():
             sys.modules.pop("test_module_with_getattr", None)
 
 
+    @cpython_only
+    @requires_specialization
+    def test_load_attr_enum(self):
+        import enum
+
+        class Color(enum.IntEnum):
+            RED = 1
+            GREEN = 2
+            BLUE = 3
+
+        def load_enum_member():
+            for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
+                x = Color.RED
+                assert x == 1
+
+        load_enum_member()
+        self.assert_specialized(load_enum_member,
+                                "LOAD_ATTR_CLASS_WITH_METACLASS_CHECK")
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-06-18-25-53.gh-issue-95004.CQeT_H.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-06-18-25-53.gh-issue-95004.CQeT_H.rst
new file mode 100644
index 00000000000000..a492982bc62da7
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-06-18-25-53.gh-issue-95004.CQeT_H.rst
@@ -0,0 +1 @@
+The specializing interpreter now specializes for :class:`enum.Enum` improving 
performance and scaling in free-threading. Patch by Kumar Aditya.
diff --git a/Python/specialize.c b/Python/specialize.c
index 0fe225dcbb6b5f..bfa7b8148e46de 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -1201,22 +1201,33 @@ specialize_class_load_attr(PyObject *owner, 
_Py_CODEUNIT *instr,
         }
     }
     switch (kind) {
-        case METHOD:
-        case NON_DESCRIPTOR:
-            #ifdef Py_GIL_DISABLED
-            if (!_PyObject_HasDeferredRefcount(descr)) {
-                SPECIALIZATION_FAIL(LOAD_ATTR, 
SPEC_FAIL_ATTR_DESCR_NOT_DEFERRED);
+        case MUTABLE:
+            // special case for enums which has Py_TYPE(descr) == cls
+            // so guarding on type version is sufficient
+            if (Py_TYPE(descr) != cls) {
+                SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_MUTABLE_CLASS);
                 Py_XDECREF(descr);
                 return -1;
             }
-            #endif
-            write_u32(cache->type_version, tp_version);
+            if (Py_TYPE(descr)->tp_descr_get || Py_TYPE(descr)->tp_descr_set) {
+                SPECIALIZATION_FAIL(LOAD_ATTR, 
SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR);
+                Py_XDECREF(descr);
+                return -1;
+            }
+            _Py_FALLTHROUGH;
+        case METHOD:
+        case NON_DESCRIPTOR:
+#ifdef Py_GIL_DISABLED
+            maybe_enable_deferred_ref_count(descr);
+#endif
             write_ptr(cache->descr, descr);
             if (metaclass_check) {
-                write_u32(cache->keys_version, meta_version);
+                write_u32(cache->keys_version, tp_version);
+                write_u32(cache->type_version, meta_version);
                 specialize(instr, LOAD_ATTR_CLASS_WITH_METACLASS_CHECK);
             }
             else {
+                write_u32(cache->type_version, tp_version);
                 specialize(instr, LOAD_ATTR_CLASS);
             }
             Py_XDECREF(descr);
diff --git a/Tools/ftscalingbench/ftscalingbench.py 
b/Tools/ftscalingbench/ftscalingbench.py
index a3d87e1f855dcb..60f43b99c0f69d 100644
--- a/Tools/ftscalingbench/ftscalingbench.py
+++ b/Tools/ftscalingbench/ftscalingbench.py
@@ -295,6 +295,20 @@ def setattr_non_interned():
         setattr(obj, f"{prefix}_c", None)
 
 
+from enum import Enum
+class MyEnum(Enum):
+    X = 1
+    Y = 2
+    Z = 3
+
+@register_benchmark
+def enum_attr():
+    for _ in range(1000 * WORK_SCALE):
+        MyEnum.X
+        MyEnum.Y
+        MyEnum.Z
+
+
 def bench_one_thread(func):
     t0 = time.perf_counter_ns()
     func()

_______________________________________________
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