From 92a0ceaa4e4ece4a092e4ff7f619b5ad1132b441 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 29 Nov 2025 18:44:41 +0200 Subject: [PATCH 1/3] Update `test_descr.py` from 3.13.9 --- Lib/test/test_descr.py | 265 +++++++++++++++++++++-------------------- 1 file changed, 136 insertions(+), 129 deletions(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 8a424c8be8..f592a88fc2 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -15,6 +15,7 @@ from copy import deepcopy from contextlib import redirect_stdout from test import support +from test.support.testcase import ExtraAssertions try: import _testcapi @@ -403,15 +404,7 @@ def test_wrap_lenfunc_bad_cast(self): self.assertEqual(range(sys.maxsize).__len__(), sys.maxsize) -class ClassPropertiesAndMethods(unittest.TestCase): - - def assertHasAttr(self, obj, name): - self.assertTrue(hasattr(obj, name), - '%r has no attribute %r' % (obj, name)) - - def assertNotHasAttr(self, obj, name): - self.assertFalse(hasattr(obj, name), - '%r has unexpected attribute %r' % (obj, name)) +class ClassPropertiesAndMethods(unittest.TestCase, ExtraAssertions): def test_python_dicts(self): # Testing Python subclass of dict... @@ -989,8 +982,8 @@ class EditableScrollablePane(ScrollablePane,EditablePane): pass def test_mro_disagreement(self): # Testing error messages for MRO disagreement... - mro_err_msg = """Cannot create a consistent method resolution -order (MRO) for bases """ + mro_err_msg = ("Cannot create a consistent method resolution " + "order (MRO) for bases ") def raises(exc, expected, callable, *args): try: @@ -1109,8 +1102,7 @@ class MyFrozenSet(frozenset): with self.assertRaises(TypeError): frozenset().__class__ = MyFrozenSet - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_slots(self): # Testing __slots__... class C0(object): @@ -1199,10 +1191,9 @@ class C(object): pass else: self.fail("[''] slots not caught") - class C(object): + + class WithValidIdentifiers(object): __slots__ = ["a", "a_b", "_a", "A0123456789Z"] - # XXX(nnorwitz): was there supposed to be something tested - # from the class above? # Test a single string is not expanded as a sequence. class C(object): @@ -1316,7 +1307,7 @@ class X(object): # Inherit from object on purpose to check some backwards compatibility paths class X(object): __slots__ = "a" - with self.assertRaisesRegex(AttributeError, "'X' object has no attribute 'a'"): + with self.assertRaisesRegex(AttributeError, "'test.test_descr.ClassPropertiesAndMethods.test_slots..X' object has no attribute 'a'"): X().a # Test string subclass in `__slots__`, see gh-98783 @@ -1328,8 +1319,7 @@ class X(object): with self.assertRaisesRegex(AttributeError, "'X' object has no attribute 'a'"): X().a - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_slots_special(self): # Testing __dict__ and __weakref__ in __slots__... class D(object): @@ -1368,8 +1358,7 @@ class C2(D, W): a.foo = 42 self.assertEqual(a.__dict__, {"foo": 42}) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_slots_special2(self): # Testing __qualname__ and __classcell__ in __slots__ class Meta(type): @@ -1410,8 +1399,7 @@ class Q2: __qualname__ = object() __slots__ = ["__qualname__"] - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_slots_descriptor(self): # Issue2115: slot descriptors did not correctly check # the type of the given object @@ -1602,7 +1590,11 @@ def f(cls, arg): cm = classmethod(f) cm_dict = {'__annotations__': {}, - '__doc__': "f docstring", + '__doc__': ( + "f docstring" + if support.HAVE_PY_DOCSTRINGS + else None + ), '__module__': __name__, '__name__': 'f', '__qualname__': f.__qualname__} @@ -1674,8 +1666,6 @@ class SubSpam(spam.spamlist): pass spam_cm.__get__(None, list) self.assertEqual(str(cm.exception), expected_errmsg) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_staticmethods(self): # Testing static methods... class C(object): @@ -1693,10 +1683,10 @@ class D(C): self.assertEqual(d.foo(1), (d, 1)) self.assertEqual(D.foo(d, 1), (d, 1)) sm = staticmethod(None) - self.assertEqual(sm.__dict__, {'__doc__': None}) + self.assertEqual(sm.__dict__, {'__doc__': None.__doc__}) sm.x = 42 self.assertEqual(sm.x, 42) - self.assertEqual(sm.__dict__, {"x" : 42, '__doc__': None}) + self.assertEqual(sm.__dict__, {"x" : 42, '__doc__': None.__doc__}) del sm.x self.assertNotHasAttr(sm, "x") @@ -1811,8 +1801,7 @@ class C(list): __new__ = object.__new__ self.assertRaises(TypeError, C) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_object_new(self): class A(object): pass @@ -1847,6 +1836,8 @@ def __init__(self, foo): object.__init__(A(3)) self.assertRaises(TypeError, object.__init__, A(3), 5) + @unittest.skip('TODO: RUSTPYTHON; This passes, but the `expectedFailure` here is from CPython, so this test is an "UNEXPECTED SUCCESS" (not good)') + @unittest.expectedFailure def test_restored_object_new(self): class A(object): def __new__(cls, *args, **kwargs): @@ -1870,8 +1861,7 @@ def __init__(self, foo): self.assertEqual(b.foo, 3) self.assertEqual(b.__class__, B) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_altmro(self): # Testing mro() and overriding it... class A(object): @@ -2050,8 +2040,7 @@ def test_methods_in_c(self): set_add.__get__(0) self.assertEqual(cm.exception.args[0], expected_errmsg) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_special_method_lookup(self): # The lookup of special methods bypasses __getattr__ and # __getattribute__, but they still can be descriptors. @@ -2254,8 +2243,7 @@ def __contains__(self, value): self.assertIn(i, p10) self.assertNotIn(10, p10) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_weakrefs(self): # Testing weak references... import weakref @@ -2287,8 +2275,7 @@ class Weak(object): self.assertEqual(r(), None) del r - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_properties(self): # Testing property... class C(object): @@ -2616,8 +2603,7 @@ def __getclass(self): dir(C()) # This used to segfault - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_supers(self): # Testing super... @@ -2730,8 +2716,7 @@ def test(klass): with self.assertRaises(TypeError): super(Base, kw=1) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_basic_inheritance(self): # Testing inheritance from basic types... @@ -3064,8 +3049,7 @@ class sublist(list): ## pass ## os_helper.unlink(os_helper.TESTFN) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_keywords(self): # Testing keyword args to basic type constructors ... with self.assertRaisesRegex(TypeError, 'keyword argument'): @@ -3268,8 +3252,7 @@ def __ge__(self, other): eval("x %s y" % op), "x=%d, y=%d" % (x, y)) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_descrdoc(self): # Testing descriptor doc strings... from _io import FileIO @@ -3293,8 +3276,7 @@ class NewClass: self.assertEqual(NewClass.__doc__, 'object=None; type=NewClass') self.assertEqual(NewClass().__doc__, 'object=NewClass instance; type=NewClass') - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_set_class(self): # Testing __class__ assignment... class C(object): pass @@ -3384,8 +3366,7 @@ def __del__(self): l = [A() for x in range(100)] del l - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_set_dict(self): # Testing __dict__ assignment... class C(object): pass @@ -3644,7 +3625,7 @@ def f(a): return a encoding='latin1', errors='replace') self.assertEqual(ba, b'abc\xbd?') - @unittest.skip("TODO: RUSTPYTHON, rustpython segmentation fault") + @unittest.skip('TODO: RUSTPYTHON; rustpython segmentation fault') def test_recursive_call(self): # Testing recursive __call__() by setting to instance of class... class A(object): @@ -3763,8 +3744,7 @@ def test_uninitialized_modules(self): m.foo = 1 self.assertEqual(m.__dict__, {"foo": 1}) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_funny_new(self): # Testing __new__ returning something unexpected... class C(object): @@ -3926,7 +3906,7 @@ def __del__(self): # it as a leak. del C.__del__ - @unittest.skip("TODO: RUSTPYTHON, rustpython segmentation fault") + @unittest.skip('TODO: RUSTPYTHON; rustpython segmentation fault') def test_slots_trash(self): # Testing slot trash... # Deallocating deeply nested slotted trash caused stack overflows @@ -3939,8 +3919,7 @@ def __init__(self, x): o = trash(o) del o - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_slots_multiple_inheritance(self): # SF bug 575229, multiple inheritance w/ slots dumps core class A(object): @@ -4085,8 +4064,7 @@ class E(D): else: self.fail("shouldn't be able to create inheritance cycles") - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_builtin_bases(self): # Make sure all the builtin types can have their base queried without # segfaulting. See issue #5787. @@ -4131,8 +4109,7 @@ class D(C): else: self.fail("best_base calculation found wanting") - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_unsubclassable_types(self): with self.assertRaises(TypeError): class X(type(None)): @@ -4165,8 +4142,7 @@ class X(object): with self.assertRaises(TypeError): X.__bases__ = type(None), O - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_mutable_bases_with_failing_mro(self): # Testing mutable bases with failing mro... class WorkOnce(type): @@ -4273,8 +4249,7 @@ class C: C.__name__ = Nasty("abc") C.__name__ = "normal" - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_subclass_right_op(self): # Testing correct dispatch of subclass overloading __r__... @@ -4409,8 +4384,7 @@ class D(C): self.assertIsInstance(a, C) # Baseline self.assertIsInstance(pa, C) # Test - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_proxy_super(self): # Testing super() for a proxy object... class Proxy(object): @@ -4434,8 +4408,7 @@ def f(self): p = Proxy(obj) self.assertEqual(C.__dict__["f"](p), "B.f->C.f") - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_carloverre(self): # Testing prohibition of Carlo Verre's hack... try: @@ -4468,8 +4441,7 @@ class C(B, A): except TypeError: self.fail("setattr through direct base types should be legal") - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_carloverre_multi_inherit_invalid(self): class A(type): def __setattr__(cls, key, value): @@ -4507,8 +4479,8 @@ class Oops(object): o = Oops() o.whatever = Provoker(o) del o - - @unittest.skip("TODO: RUSTPYTHON, rustpython segmentation fault") + + @unittest.skip('TODO: RUSTPYTHON; rustpython segmentation fault') @support.requires_resource('cpu') def test_wrapper_segfault(self): # SF 927248: deeply nested wrappers could cause stack overflow @@ -4583,8 +4555,7 @@ def assertNotOrderable(self, a, b): with self.assertRaises(TypeError): a >= b - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_method_wrapper(self): # Testing method-wrapper objects... # did not support any reflection before 2.5 @@ -4644,18 +4615,16 @@ def test_special_unbound_method_types(self): def test_not_implemented(self): # Testing NotImplemented... # all binary methods should be able to return a NotImplemented - import operator def specialmethod(self, other): return NotImplemented def check(expr, x, y): - try: - exec(expr, {'x': x, 'y': y, 'operator': operator}) - except TypeError: - pass - else: - self.fail("no TypeError from %r" % (expr,)) + with ( + self.subTest(expr=expr, x=x, y=y), + self.assertRaises(TypeError), + ): + exec(expr, {'x': x, 'y': y}) N1 = sys.maxsize + 1 # might trigger OverflowErrors instead of # TypeErrors @@ -4676,12 +4645,23 @@ def check(expr, x, y): ('__and__', 'x & y', 'x &= y'), ('__or__', 'x | y', 'x |= y'), ('__xor__', 'x ^ y', 'x ^= y')]: - rname = '__r' + name[2:] + # Defines 'left' magic method: A = type('A', (), {name: specialmethod}) a = A() check(expr, a, a) check(expr, a, N1) check(expr, a, N2) + # Defines 'right' magic method: + rname = '__r' + name[2:] + B = type('B', (), {rname: specialmethod}) + b = B() + check(expr, b, b) + check(expr, a, b) + check(expr, b, a) + check(expr, b, N1) + check(expr, b, N2) + check(expr, N1, b) + check(expr, N2, b) if iexpr: check(iexpr, a, a) check(iexpr, a, N1) @@ -4788,6 +4768,21 @@ class X(object): with self.assertRaises(AttributeError): del X.__abstractmethods__ + @unittest.skip('TODO: RUSTPYTHON; crash. "dict has non-string keys: [PyObject PyInt { value: 1 }]"') + def test_gh55664(self): + # gh-55664: issue a warning when the + # __dict__ of a class contains non-string keys + with self.assertWarnsRegex(RuntimeWarning, 'MyClass'): + MyClass = type('MyClass', (), {1: 2}) + + class meta(type): + def __new__(mcls, name, bases, ns): + ns[1] = 2 + return super().__new__(mcls, name, bases, ns) + + with self.assertWarnsRegex(RuntimeWarning, 'MyClass'): + MyClass = meta('MyClass', (), {}) + def test_proxy_call(self): class FakeStr: __class__ = str @@ -4811,24 +4806,24 @@ class Thing: thing = Thing() for i in range(20): with self.assertRaises(TypeError): - # PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS + # CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS list.sort(thing) for i in range(20): with self.assertRaises(TypeError): - # PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS + # CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS str.split(thing) for i in range(20): with self.assertRaises(TypeError): - # PRECALL_NO_KW_METHOD_DESCRIPTOR_NOARGS + # CALL_METHOD_DESCRIPTOR_NOARGS str.upper(thing) for i in range(20): with self.assertRaises(TypeError): - # PRECALL_NO_KW_METHOD_DESCRIPTOR_FAST + # CALL_METHOD_DESCRIPTOR_FAST str.strip(thing) from collections import deque for i in range(20): with self.assertRaises(TypeError): - # PRECALL_NO_KW_METHOD_DESCRIPTOR_O + # CALL_METHOD_DESCRIPTOR_O deque.append(thing, thing) def test_repr_as_str(self): @@ -4862,8 +4857,7 @@ class A(int): with self.assertRaises(TypeError): a + a - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_slot_shadows_class_variable(self): with self.assertRaises(ValueError) as cm: class X: @@ -4872,8 +4866,7 @@ class X: m = str(cm.exception) self.assertEqual("'foo' in __slots__ conflicts with class variable", m) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_set_doc(self): class X: "elephant" @@ -4889,8 +4882,7 @@ class X: self.assertIn("cannot delete '__doc__' attribute of immutable type 'X'", str(cm.exception)) self.assertEqual(X.__doc__, "banana") - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_qualname(self): descriptors = [str.lower, complex.real, float.real, int.__add__] types = ['method', 'member', 'getset', 'wrapper'] @@ -4923,8 +4915,7 @@ class Inside: self.assertEqual(Y.__qualname__, 'Y') self.assertEqual(Y.Inside.__qualname__, 'Y.Inside') - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_qualname_dict(self): ns = {'__qualname__': 'some.name'} tp = type('Foo', (), ns) @@ -4935,8 +4926,7 @@ def test_qualname_dict(self): ns = {'__qualname__': 1} self.assertRaises(TypeError, type, 'Foo', (), ns) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_cycle_through_dict(self): # See bug #1469629 class X(dict): @@ -4952,8 +4942,7 @@ def __init__(self): for o in gc.get_objects(): self.assertIsNot(type(o), X) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_object_new_and_init_with_parameters(self): # See issue #1683368 class OverrideNeither: @@ -4974,8 +4963,7 @@ class OverrideBoth(OverrideNew, OverrideInit): self.assertRaises(TypeError, case, 1, 2, 3) self.assertRaises(TypeError, case, 1, 2, foo=3) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_subclassing_does_not_duplicate_dict_descriptors(self): class Base: pass @@ -5055,6 +5043,7 @@ def __new__(cls): cls.lst = [2**i for i in range(10000)] X.descr + @support.suppress_immortalization() def test_remove_subclass(self): # bpo-46417: when the last subclass of a type is deleted, # remove_subclass() clears the internal dictionary of subclasses: @@ -5072,6 +5061,22 @@ class Child(Parent): gc.collect() self.assertEqual(Parent.__subclasses__(), []) + @unittest.expectedFailure # TODO: RUSTPYTHON + def test_instance_method_get_behavior(self): + # test case for gh-113157 + + class A: + def meth(self): + return self + + class B: + pass + + a = A() + b = B() + b.meth = a.meth.__get__(b, B) + self.assertEqual(b.meth(), a) + def test_attr_raise_through_property(self): # test case for gh-103272 class A: @@ -5106,8 +5111,7 @@ def meth(self): pass self.C = C - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __local__') def test_iter_keys(self): @@ -5116,9 +5120,12 @@ def test_iter_keys(self): self.assertNotIsInstance(it, list) keys = list(it) keys.sort() - self.assertEqual(keys, ['__dict__', '__doc__', '__module__', - '__weakref__', 'meth']) + self.assertEqual(keys, ['__dict__', '__doc__', '__firstlineno__', + '__module__', + '__static_attributes__', '__weakref__', + 'meth']) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 5 != 7 @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __local__') def test_iter_values(self): @@ -5126,10 +5133,9 @@ def test_iter_values(self): it = self.C.__dict__.values() self.assertNotIsInstance(it, list) values = list(it) - self.assertEqual(len(values), 5) + self.assertEqual(len(values), 7) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __local__') def test_iter_items(self): @@ -5138,8 +5144,10 @@ def test_iter_items(self): self.assertNotIsInstance(it, list) keys = [item[0] for item in it] keys.sort() - self.assertEqual(keys, ['__dict__', '__doc__', '__module__', - '__weakref__', 'meth']) + self.assertEqual(keys, ['__dict__', '__doc__', '__firstlineno__', + '__module__', + '__static_attributes__', '__weakref__', + 'meth']) def test_dict_type_with_metaclass(self): # Testing type of __dict__ when metaclass set... @@ -5189,7 +5197,7 @@ def __pow__(self, *args): class MiscTests(unittest.TestCase): - @unittest.skip("TODO: RUSTPYTHON, rustpython panicked at 'dict has non-string keys: [PyObject PyBaseObject]'") + @unittest.skip("TODO: RUSTPYTHON; rustpython panicked at 'dict has non-string keys: [PyObject PyBaseObject]'") def test_type_lookup_mro_reference(self): # Issue #14199: _PyType_Lookup() has to keep a strong reference to # the type MRO because it may be modified during the lookup, if @@ -5209,11 +5217,17 @@ class Base2(object): mykey = 'from Base2' mykey2 = 'from Base2' - X = type('X', (Base,), {MyKey(): 5}) + with self.assertWarnsRegex(RuntimeWarning, 'X'): + X = type('X', (Base,), {MyKey(): 5}) + + # Note that the access below uses getattr() rather than normally + # accessing the attribute. That is done to avoid the bytecode + # specializer activating on repeated runs of the test. + # mykey is read from Base - self.assertEqual(X.mykey, 'from Base') + self.assertEqual(getattr(X, 'mykey'), 'from Base') # mykey2 is read from Base2 because MyKey.__eq__ has set __bases__ - self.assertEqual(X.mykey2, 'from Base2') + self.assertEqual(getattr(X, 'mykey2'), 'from Base2') class PicklingTests(unittest.TestCase): @@ -5248,8 +5262,7 @@ def _check_reduce(self, proto, obj, args=(), kwargs={}, state=None, self.assertEqual(obj.__reduce_ex__(proto), reduce_value) self.assertEqual(obj.__reduce__(), reduce_value) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_reduce(self): protocols = range(pickle.HIGHEST_PROTOCOL + 1) args = (-101, "spam") @@ -5373,8 +5386,7 @@ class C16(list): for proto in protocols: self._check_reduce(proto, obj, listitems=list(obj)) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_special_method_lookup(self): protocols = range(pickle.HIGHEST_PROTOCOL + 1) class Picky: @@ -5507,8 +5519,7 @@ class E(C): y = pickle_copier.copy(x) self._assert_is_copy(x, y) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_reduce_copying(self): # Tests pickling and copying new-style classes and objects. global C1 @@ -5633,7 +5644,7 @@ def __repr__(self): objcopy2 = deepcopy(objcopy) self._assert_is_copy(obj, objcopy2) - @unittest.skip("TODO: RUSTPYTHON") + @unittest.skip('TODO: RUSTPYTHON') def test_issue24097(self): # Slot name is freed inside __getattr__ and is later used. class S(str): # Not interned @@ -5774,8 +5785,7 @@ class B(A): class C(B): pass - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_reent_set_bases_tp_base_cycle(self): """ type_set_bases must check for an inheritance cycle not only through @@ -5812,8 +5822,7 @@ class B2(A): with self.assertRaises(TypeError): B1.__bases__ += () - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_tp_subclasses_cycle_in_update_slots(self): """ type_set_bases must check for reentrancy upon finishing its job @@ -5850,8 +5859,7 @@ class C(A): self.assertEqual(B1.__bases__, (C,)) self.assertEqual(C.__subclasses__(), [B1]) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_tp_subclasses_cycle_error_return_path(self): """ The same as test_tp_subclasses_cycle_in_update_slots, but tests @@ -5920,8 +5928,7 @@ def mro(cls): class A(metaclass=M): pass - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test_disappearing_custom_mro(self): """ gh-92112: A custom mro() returning a result conflicting with From c2ea003bc160b187f8d6f3f197029d6df68e680f Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 29 Nov 2025 20:20:19 +0200 Subject: [PATCH 2/3] Update `test_super.py` from 3.13.9 --- Lib/test/test_super.py | 310 +++++++++++++++++++++++++++----- crates/vm/src/builtins/super.rs | 29 ++- 2 files changed, 291 insertions(+), 48 deletions(-) diff --git a/Lib/test/test_super.py b/Lib/test/test_super.py index 3fd5833d8b..8967dab8bd 100644 --- a/Lib/test/test_super.py +++ b/Lib/test/test_super.py @@ -1,6 +1,13 @@ """Unit tests for zero-argument super() & related machinery.""" +import textwrap +import threading import unittest +from unittest.mock import patch +from test.support import import_helper, threading_helper + + +ADAPTIVE_WARMUP_DELAY = 2 class A: @@ -84,41 +91,43 @@ def nested(): self.assertEqual(E().f(), 'AE') - # SyntaxError - # def test_various___class___pathologies(self): - # # See issue #12370 - # class X(A): - # def f(self): - # return super().f() - # __class__ = 413 - # x = X() - # self.assertEqual(x.f(), 'A') - # self.assertEqual(x.__class__, 413) - # class X: - # x = __class__ - # def f(): - # __class__ - # self.assertIs(X.x, type(self)) - # with self.assertRaises(NameError) as e: - # exec("""class X: - # __class__ - # def f(): - # __class__""", globals(), {}) - # self.assertIs(type(e.exception), NameError) # Not UnboundLocalError - # class X: - # global __class__ - # __class__ = 42 - # def f(): - # __class__ - # self.assertEqual(globals()["__class__"], 42) - # del globals()["__class__"] - # self.assertNotIn("__class__", X.__dict__) - # class X: - # nonlocal __class__ - # __class__ = 42 - # def f(): - # __class__ - # self.assertEqual(__class__, 42) + # TODO: RUSTPYTHON; SyntaxError: name '__class__' is assigned to before global declaration + ''' + def test_various___class___pathologies(self): + # See issue #12370 + class X(A): + def f(self): + return super().f() + __class__ = 413 + x = X() + self.assertEqual(x.f(), 'A') + self.assertEqual(x.__class__, 413) + class X: + x = __class__ + def f(): + __class__ + self.assertIs(X.x, type(self)) + with self.assertRaises(NameError) as e: + exec("""class X: + __class__ + def f(): + __class__""", globals(), {}) + self.assertIs(type(e.exception), NameError) # Not UnboundLocalError + class X: + global __class__ + __class__ = 42 + def f(): + __class__ + self.assertEqual(globals()["__class__"], 42) + del globals()["__class__"] + self.assertNotIn("__class__", X.__dict__) + class X: + nonlocal __class__ + __class__ = 42 + def f(): + __class__ + self.assertEqual(__class__, 42) + ''' def test___class___instancemethod(self): # See issue #14857 @@ -182,8 +191,7 @@ def f(): B = type("B", (), test_namespace) self.assertIs(B.f(), B) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test___class___mro(self): # See issue #23722 test_class = None @@ -201,8 +209,7 @@ def f(): self.assertIs(test_class, A) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.expectedFailure # TODO: RUSTPYTHON def test___classcell___expected_behaviour(self): # See issue #23722 class Meta(type): @@ -288,17 +295,28 @@ def f(self): def test_obscure_super_errors(self): def f(): super() - self.assertRaises(RuntimeError, f) + with self.assertRaisesRegex(RuntimeError, r"no arguments"): + f() + + class C: + def f(): + super() + with self.assertRaisesRegex(RuntimeError, r"no arguments"): + C.f() + def f(x): del x super() - self.assertRaises(RuntimeError, f, None) + with self.assertRaisesRegex(RuntimeError, r"arg\[0\] deleted"): + f(None) + class X: def f(x): nonlocal __class__ del __class__ super() - self.assertRaises(RuntimeError, X().f) + with self.assertRaisesRegex(RuntimeError, r"empty __class__ cell"): + X().f() def test_cell_as_self(self): class X: @@ -322,6 +340,214 @@ def test_super_init_leaks(self): for i in range(1000): super.__init__(sp, int, i) + def test_super_argcount(self): + with self.assertRaisesRegex(TypeError, "expected at most"): + super(int, int, int) + + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "argument 1 must be a type" does not match "Expected type 'type' but 'int' found." + def test_super_argtype(self): + with self.assertRaisesRegex(TypeError, "argument 1 must be a type"): + super(1, int) + + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'test.support.import_helper' has no attribute 'ready_to_import' + def test_shadowed_global(self): + source = textwrap.dedent( + """ + class super: + msg = "truly super" + + class C: + def method(self): + return super().msg + """, + ) + with import_helper.ready_to_import(name="shadowed_super", source=source): + import shadowed_super + self.assertEqual(shadowed_super.C().method(), "truly super") + import_helper.unload("shadowed_super") + + def test_shadowed_local(self): + class super: + msg = "quite super" + + class C: + def method(self): + return super().msg + + self.assertEqual(C().method(), "quite super") + + def test_shadowed_dynamic(self): + class MySuper: + msg = "super super" + + class C: + def method(self): + return super().msg + + with patch(f"{__name__}.super", MySuper) as m: + self.assertEqual(C().method(), "super super") + + def test_shadowed_dynamic_two_arg(self): + call_args = [] + class MySuper: + def __init__(self, *args): + call_args.append(args) + msg = "super super" + + class C: + def method(self): + return super(1, 2).msg + + with patch(f"{__name__}.super", MySuper) as m: + self.assertEqual(C().method(), "super super") + self.assertEqual(call_args, [(1, 2)]) + + def test_attribute_error(self): + class C: + def method(self): + return super().msg + + with self.assertRaisesRegex(AttributeError, "'super' object has no attribute 'msg'"): + C().method() + + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "argument 1 must be a type" does not match "Expected type 'type' but 'int' found." + def test_bad_first_arg(self): + class C: + def method(self): + return super(1, self).method() + + with self.assertRaisesRegex(TypeError, "argument 1 must be a type"): + C().method() + + def test_supercheck_fail(self): + class C: + def method(self, type_, obj): + return super(type_, obj).method() + + c = C() + err_msg = ( + r"super\(type, obj\): obj \({} {}\) is not " + r"an instance or subtype of type \({}\)." + ) + + cases = ( + (int, c, int.__name__, C.__name__, "instance of"), + # obj is instance of type + (C, list(), C.__name__, list.__name__, "instance of"), + # obj is type itself + (C, list, C.__name__, list.__name__, "type"), + ) + + for case in cases: + with self.subTest(case=case): + type_, obj, type_str, obj_str, instance_or_type = case + regex = err_msg.format(instance_or_type, obj_str, type_str) + + with self.assertRaisesRegex(TypeError, regex): + c.method(type_, obj) + + def test_super___class__(self): + class C: + def method(self): + return super().__class__ + + self.assertEqual(C().method(), super) + + @unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: type 'super' is not an acceptable base type + def test_super_subclass___class__(self): + class mysuper(super): + pass + + class C: + def method(self): + return mysuper(C, self).__class__ + + self.assertEqual(C().method(), mysuper) + + def test_unusual_getattro(self): + class MyType(type): + pass + + def test(name): + mytype = MyType(name, (MyType,), {}) + super(MyType, type(mytype)).__setattr__(mytype, "bar", 1) + self.assertEqual(mytype.bar, 1) + + 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) + + @threading_helper.requires_working_threading() + def test___class___modification_multithreaded(self): + """ Note: this test isn't actually testing anything on its own. + It requires a sys audithook to be set to crash on older Python. + This should be the case anyways as our test suite sets + an audit hook. + """ + + class Foo: + pass + + class Bar: + pass + + thing = Foo() + def work(): + foo = thing + for _ in range(200): + foo.__class__ = Bar + type(foo) + foo.__class__ = Foo + type(foo) + + + threads = [] + for _ in range(6): + thread = threading.Thread(target=work) + thread.start() + threads.append(thread) + + for thread in threads: + thread.join() + if __name__ == "__main__": unittest.main() diff --git a/crates/vm/src/builtins/super.rs b/crates/vm/src/builtins/super.rs index ce467ecefa..4cc19ca415 100644 --- a/crates/vm/src/builtins/super.rs +++ b/crates/vm/src/builtins/super.rs @@ -239,14 +239,16 @@ impl Representable for PySuper { } fn super_check(ty: PyTypeRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { - if let Ok(cls) = obj.clone().downcast::() - && cls.fast_issubclass(&ty) - { - return Ok(cls); - } + let is_type = match obj.clone().downcast::() { + Ok(cls) if cls.fast_issubclass(&ty) => return Ok(cls), + Ok(_) => true, + Err(_) => false, + }; + if obj.fast_isinstance(&ty) { return Ok(obj.class().to_owned()); } + let class_attr = obj.get_attr("__class__", vm)?; if let Ok(cls) = class_attr.downcast::() && !cls.is(&ty) @@ -254,7 +256,22 @@ fn super_check(ty: PyTypeRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { return Ok(cls); } - Err(vm.new_type_error("super(type, obj): obj must be an instance or subtype of type")) + + let (type_or_instance, obj_str) = if is_type { + // SAFETY: This is will always be successful as we already checked at the begining of + // the function. + let typ = unsafe { obj.downcast::().unwrap_unchecked() }; + ("type", typ.name().to_owned()) + } else { + ("instance of", obj.class().name().to_owned()) + }; + + Err(vm.new_type_error(format!( + "super(type, obj): obj ({} {}) is not an instance or subtype of type ({}).", + type_or_instance, + obj_str, + ty.name(), + ))) } pub fn init(context: &Context) { From 7c865233278d77ef2ad978fcc101b6bb09f3ebb5 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 29 Nov 2025 23:51:47 +0200 Subject: [PATCH 3/3] Reuse cls --- crates/vm/src/builtins/super.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/crates/vm/src/builtins/super.rs b/crates/vm/src/builtins/super.rs index 4cc19ca415..419055b79f 100644 --- a/crates/vm/src/builtins/super.rs +++ b/crates/vm/src/builtins/super.rs @@ -239,10 +239,10 @@ impl Representable for PySuper { } fn super_check(ty: PyTypeRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let is_type = match obj.clone().downcast::() { + let typ = match obj.clone().downcast::() { Ok(cls) if cls.fast_issubclass(&ty) => return Ok(cls), - Ok(_) => true, - Err(_) => false, + Ok(cls) => Some(cls), + Err(_) => None, }; if obj.fast_isinstance(&ty) { @@ -257,13 +257,9 @@ fn super_check(ty: PyTypeRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult return Ok(cls); } - let (type_or_instance, obj_str) = if is_type { - // SAFETY: This is will always be successful as we already checked at the begining of - // the function. - let typ = unsafe { obj.downcast::().unwrap_unchecked() }; - ("type", typ.name().to_owned()) - } else { - ("instance of", obj.class().name().to_owned()) + let (type_or_instance, obj_str) = match typ { + Some(t) => ("type", t.name().to_owned()), + None => ("instance of", obj.class().name().to_owned()), }; Err(vm.new_type_error(format!(