From c603b7ecf7fa14e0fb9d21ee9a39beab77c2df55 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 21 Jul 2023 14:21:26 -0700 Subject: [PATCH 1/2] gh-106917: fix super classmethod calls to non-classmethods --- Lib/test/test_super.py | 39 +++++++++++++++++++ ...-07-21-14-37-48.gh-issue-106917.1jWp_m.rst | 4 ++ Python/bytecodes.c | 2 +- Python/executor_cases.c.h | 2 +- Python/generated_cases.c.h | 2 +- 5 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-21-14-37-48.gh-issue-106917.1jWp_m.rst diff --git a/Lib/test/test_super.py b/Lib/test/test_super.py index 664cf70b3cf0fa..0060f9c761cc55 100644 --- a/Lib/test/test_super.py +++ b/Lib/test/test_super.py @@ -422,6 +422,45 @@ def test(name): test("foo1") test("foo2") + def test_reassigned_new(self): + class A: + def __new__(cls): + pass + + def __init_subclass__(cls): + if "__new__" not in cls.__dict__: + cls.__new__ = cls.__new__ + + class B(A): + pass + + class C(B): + def __new__(cls): + return super().__new__(cls) + + C() + C() + + def test_mixed_staticmethod_hierarchy(self): + # This test is just a desugared version of `test_reassigned_new` + class A: + @staticmethod + def some(cls, *args, **kwargs): + self.assertFalse(args) + self.assertFalse(kwargs) + + class B(A): + def some(cls, *args, **kwargs): + return super().some(cls, *args, **kwargs) + + class C(B): + @staticmethod + def some(cls): + return super().some(cls) + + C.some(C) + C.some(C) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-21-14-37-48.gh-issue-106917.1jWp_m.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-21-14-37-48.gh-issue-106917.1jWp_m.rst new file mode 100644 index 00000000000000..82c74d5465458a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-21-14-37-48.gh-issue-106917.1jWp_m.rst @@ -0,0 +1,4 @@ +Fix classmethod-style :func:`super` method calls (i.e., where the second +argument to :func:`super`, or the implied second argument drawn from +``self/cls`` in the case of zero-arg super, is a type) when the target of +the call is not a classmethod. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 3a191aca730e91..b3a9918e1cf28b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1720,7 +1720,7 @@ dummy_func( PyTypeObject *cls = (PyTypeObject *)class; int method_found = 0; res2 = _PySuper_Lookup(cls, self, name, - cls->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); + Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); Py_DECREF(global_super); Py_DECREF(class); if (res2 == NULL) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 064965a1a1ae7b..0f04b428ba8058 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1406,7 +1406,7 @@ PyTypeObject *cls = (PyTypeObject *)class; int method_found = 0; res2 = _PySuper_Lookup(cls, self, name, - cls->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); + Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); Py_DECREF(global_super); Py_DECREF(class); if (res2 == NULL) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 9d25fc604314a8..02ad69a6bc4daf 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2154,7 +2154,7 @@ PyTypeObject *cls = (PyTypeObject *)class; int method_found = 0; res2 = _PySuper_Lookup(cls, self, name, - cls->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); + Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); Py_DECREF(global_super); Py_DECREF(class); if (res2 == NULL) { From 7003e965b7f5e0da0cec7d7c9a8eee97ff081830 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Mon, 24 Jul 2023 10:53:04 -0700 Subject: [PATCH 2/2] make specialization warmup more explicit in tests --- Lib/test/test_super.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_super.py b/Lib/test/test_super.py index 0060f9c761cc55..43162c540b55ae 100644 --- a/Lib/test/test_super.py +++ b/Lib/test/test_super.py @@ -5,6 +5,9 @@ from test import shadowed_super +ADAPTIVE_WARMUP_DELAY = 2 + + class A: def f(self): return 'A' @@ -419,8 +422,8 @@ def test(name): super(MyType, type(mytype)).__setattr__(mytype, "bar", 1) self.assertEqual(mytype.bar, 1) - test("foo1") - test("foo2") + for _ in range(ADAPTIVE_WARMUP_DELAY): + test("foo1") def test_reassigned_new(self): class A: @@ -438,8 +441,8 @@ class C(B): def __new__(cls): return super().__new__(cls) - C() - C() + for _ in range(ADAPTIVE_WARMUP_DELAY): + C() def test_mixed_staticmethod_hierarchy(self): # This test is just a desugared version of `test_reassigned_new` @@ -458,8 +461,8 @@ class C(B): def some(cls): return super().some(cls) - C.some(C) - C.some(C) + for _ in range(ADAPTIVE_WARMUP_DELAY): + C.some(C) if __name__ == "__main__":