From e5d5522612e03af3941db1d270bf6caebf330b8a Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Mon, 24 Jul 2023 14:14:56 -0600 Subject: gh-106917: fix super classmethod calls to non-classmethods (#106977) --- Lib/test/test_super.py | 46 +++++++++++++++++++++- .../2023-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, 51 insertions(+), 5 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 664cf70..43162c5 100644 --- a/Lib/test/test_super.py +++ b/Lib/test/test_super.py @@ -5,6 +5,9 @@ from unittest.mock import patch from test import shadowed_super +ADAPTIVE_WARMUP_DELAY = 2 + + class A: def f(self): return 'A' @@ -419,8 +422,47 @@ class TestSuper(unittest.TestCase): 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: + 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) + + for _ in range(ADAPTIVE_WARMUP_DELAY): + 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) + + for _ in range(ADAPTIVE_WARMUP_DELAY): + C.some(C) if __name__ == "__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 0000000..82c74d5 --- /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 7ce8978..0705f5b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1721,7 +1721,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 064965a..0f04b42 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 9d25fc6..02ad69a 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) { -- cgit v0.12