11"""Unit tests for zero-argument super() & related machinery."""
22
3+ import textwrap
4+ import threading
35import unittest
6+ from unittest .mock import patch
7+ from test .support import import_helper , threading_helper
8+
9+
10+ ADAPTIVE_WARMUP_DELAY = 2
411
512
613class A :
@@ -84,41 +91,43 @@ def nested():
8491
8592 self .assertEqual (E ().f (), 'AE' )
8693
87- # SyntaxError
88- # def test_various___class___pathologies(self):
89- # # See issue #12370
90- # class X(A):
91- # def f(self):
92- # return super().f()
93- # __class__ = 413
94- # x = X()
95- # self.assertEqual(x.f(), 'A')
96- # self.assertEqual(x.__class__, 413)
97- # class X:
98- # x = __class__
99- # def f():
100- # __class__
101- # self.assertIs(X.x, type(self))
102- # with self.assertRaises(NameError) as e:
103- # exec("""class X:
104- # __class__
105- # def f():
106- # __class__""", globals(), {})
107- # self.assertIs(type(e.exception), NameError) # Not UnboundLocalError
108- # class X:
109- # global __class__
110- # __class__ = 42
111- # def f():
112- # __class__
113- # self.assertEqual(globals()["__class__"], 42)
114- # del globals()["__class__"]
115- # self.assertNotIn("__class__", X.__dict__)
116- # class X:
117- # nonlocal __class__
118- # __class__ = 42
119- # def f():
120- # __class__
121- # self.assertEqual(__class__, 42)
94+ # TODO: RUSTPYTHON; SyntaxError: name '__class__' is assigned to before global declaration
95+ '''
96+ def test_various___class___pathologies(self):
97+ # See issue #12370
98+ class X(A):
99+ def f(self):
100+ return super().f()
101+ __class__ = 413
102+ x = X()
103+ self.assertEqual(x.f(), 'A')
104+ self.assertEqual(x.__class__, 413)
105+ class X:
106+ x = __class__
107+ def f():
108+ __class__
109+ self.assertIs(X.x, type(self))
110+ with self.assertRaises(NameError) as e:
111+ exec("""class X:
112+ __class__
113+ def f():
114+ __class__""", globals(), {})
115+ self.assertIs(type(e.exception), NameError) # Not UnboundLocalError
116+ class X:
117+ global __class__
118+ __class__ = 42
119+ def f():
120+ __class__
121+ self.assertEqual(globals()["__class__"], 42)
122+ del globals()["__class__"]
123+ self.assertNotIn("__class__", X.__dict__)
124+ class X:
125+ nonlocal __class__
126+ __class__ = 42
127+ def f():
128+ __class__
129+ self.assertEqual(__class__, 42)
130+ '''
122131
123132 def test___class___instancemethod (self ):
124133 # See issue #14857
@@ -182,8 +191,7 @@ def f():
182191 B = type ("B" , (), test_namespace )
183192 self .assertIs (B .f (), B )
184193
185- # TODO: RUSTPYTHON
186- @unittest .expectedFailure
194+ @unittest .expectedFailure # TODO: RUSTPYTHON
187195 def test___class___mro (self ):
188196 # See issue #23722
189197 test_class = None
@@ -201,8 +209,7 @@ def f():
201209
202210 self .assertIs (test_class , A )
203211
204- # TODO: RUSTPYTHON
205- @unittest .expectedFailure
212+ @unittest .expectedFailure # TODO: RUSTPYTHON
206213 def test___classcell___expected_behaviour (self ):
207214 # See issue #23722
208215 class Meta (type ):
@@ -288,17 +295,28 @@ def f(self):
288295 def test_obscure_super_errors (self ):
289296 def f ():
290297 super ()
291- self .assertRaises (RuntimeError , f )
298+ with self .assertRaisesRegex (RuntimeError , r"no arguments" ):
299+ f ()
300+
301+ class C :
302+ def f ():
303+ super ()
304+ with self .assertRaisesRegex (RuntimeError , r"no arguments" ):
305+ C .f ()
306+
292307 def f (x ):
293308 del x
294309 super ()
295- self .assertRaises (RuntimeError , f , None )
310+ with self .assertRaisesRegex (RuntimeError , r"arg\[0\] deleted" ):
311+ f (None )
312+
296313 class X :
297314 def f (x ):
298315 nonlocal __class__
299316 del __class__
300317 super ()
301- self .assertRaises (RuntimeError , X ().f )
318+ with self .assertRaisesRegex (RuntimeError , r"empty __class__ cell" ):
319+ X ().f ()
302320
303321 def test_cell_as_self (self ):
304322 class X :
@@ -322,6 +340,214 @@ def test_super_init_leaks(self):
322340 for i in range (1000 ):
323341 super .__init__ (sp , int , i )
324342
343+ def test_super_argcount (self ):
344+ with self .assertRaisesRegex (TypeError , "expected at most" ):
345+ super (int , int , int )
346+
347+ @unittest .expectedFailure # TODO: RUSTPYTHON; AssertionError: "argument 1 must be a type" does not match "Expected type 'type' but 'int' found."
348+ def test_super_argtype (self ):
349+ with self .assertRaisesRegex (TypeError , "argument 1 must be a type" ):
350+ super (1 , int )
351+
352+ @unittest .expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'test.support.import_helper' has no attribute 'ready_to_import'
353+ def test_shadowed_global (self ):
354+ source = textwrap .dedent (
355+ """
356+ class super:
357+ msg = "truly super"
358+
359+ class C:
360+ def method(self):
361+ return super().msg
362+ """ ,
363+ )
364+ with import_helper .ready_to_import (name = "shadowed_super" , source = source ):
365+ import shadowed_super
366+ self .assertEqual (shadowed_super .C ().method (), "truly super" )
367+ import_helper .unload ("shadowed_super" )
368+
369+ def test_shadowed_local (self ):
370+ class super :
371+ msg = "quite super"
372+
373+ class C :
374+ def method (self ):
375+ return super ().msg
376+
377+ self .assertEqual (C ().method (), "quite super" )
378+
379+ def test_shadowed_dynamic (self ):
380+ class MySuper :
381+ msg = "super super"
382+
383+ class C :
384+ def method (self ):
385+ return super ().msg
386+
387+ with patch (f"{ __name__ } .super" , MySuper ) as m :
388+ self .assertEqual (C ().method (), "super super" )
389+
390+ def test_shadowed_dynamic_two_arg (self ):
391+ call_args = []
392+ class MySuper :
393+ def __init__ (self , * args ):
394+ call_args .append (args )
395+ msg = "super super"
396+
397+ class C :
398+ def method (self ):
399+ return super (1 , 2 ).msg
400+
401+ with patch (f"{ __name__ } .super" , MySuper ) as m :
402+ self .assertEqual (C ().method (), "super super" )
403+ self .assertEqual (call_args , [(1 , 2 )])
404+
405+ def test_attribute_error (self ):
406+ class C :
407+ def method (self ):
408+ return super ().msg
409+
410+ with self .assertRaisesRegex (AttributeError , "'super' object has no attribute 'msg'" ):
411+ C ().method ()
412+
413+ @unittest .expectedFailure # TODO: RUSTPYTHON; AssertionError: "argument 1 must be a type" does not match "Expected type 'type' but 'int' found."
414+ def test_bad_first_arg (self ):
415+ class C :
416+ def method (self ):
417+ return super (1 , self ).method ()
418+
419+ with self .assertRaisesRegex (TypeError , "argument 1 must be a type" ):
420+ C ().method ()
421+
422+ def test_supercheck_fail (self ):
423+ class C :
424+ def method (self , type_ , obj ):
425+ return super (type_ , obj ).method ()
426+
427+ c = C ()
428+ err_msg = (
429+ r"super\(type, obj\): obj \({} {}\) is not "
430+ r"an instance or subtype of type \({}\)."
431+ )
432+
433+ cases = (
434+ (int , c , int .__name__ , C .__name__ , "instance of" ),
435+ # obj is instance of type
436+ (C , list (), C .__name__ , list .__name__ , "instance of" ),
437+ # obj is type itself
438+ (C , list , C .__name__ , list .__name__ , "type" ),
439+ )
440+
441+ for case in cases :
442+ with self .subTest (case = case ):
443+ type_ , obj , type_str , obj_str , instance_or_type = case
444+ regex = err_msg .format (instance_or_type , obj_str , type_str )
445+
446+ with self .assertRaisesRegex (TypeError , regex ):
447+ c .method (type_ , obj )
448+
449+ def test_super___class__ (self ):
450+ class C :
451+ def method (self ):
452+ return super ().__class__
453+
454+ self .assertEqual (C ().method (), super )
455+
456+ @unittest .expectedFailure # TODO: RUSTPYTHON; TypeError: type 'super' is not an acceptable base type
457+ def test_super_subclass___class__ (self ):
458+ class mysuper (super ):
459+ pass
460+
461+ class C :
462+ def method (self ):
463+ return mysuper (C , self ).__class__
464+
465+ self .assertEqual (C ().method (), mysuper )
466+
467+ def test_unusual_getattro (self ):
468+ class MyType (type ):
469+ pass
470+
471+ def test (name ):
472+ mytype = MyType (name , (MyType ,), {})
473+ super (MyType , type (mytype )).__setattr__ (mytype , "bar" , 1 )
474+ self .assertEqual (mytype .bar , 1 )
475+
476+ for _ in range (ADAPTIVE_WARMUP_DELAY ):
477+ test ("foo1" )
478+
479+ def test_reassigned_new (self ):
480+ class A :
481+ def __new__ (cls ):
482+ pass
483+
484+ def __init_subclass__ (cls ):
485+ if "__new__" not in cls .__dict__ :
486+ cls .__new__ = cls .__new__
487+
488+ class B (A ):
489+ pass
490+
491+ class C (B ):
492+ def __new__ (cls ):
493+ return super ().__new__ (cls )
494+
495+ for _ in range (ADAPTIVE_WARMUP_DELAY ):
496+ C ()
497+
498+ def test_mixed_staticmethod_hierarchy (self ):
499+ # This test is just a desugared version of `test_reassigned_new`
500+ class A :
501+ @staticmethod
502+ def some (cls , * args , ** kwargs ):
503+ self .assertFalse (args )
504+ self .assertFalse (kwargs )
505+
506+ class B (A ):
507+ def some (cls , * args , ** kwargs ):
508+ return super ().some (cls , * args , ** kwargs )
509+
510+ class C (B ):
511+ @staticmethod
512+ def some (cls ):
513+ return super ().some (cls )
514+
515+ for _ in range (ADAPTIVE_WARMUP_DELAY ):
516+ C .some (C )
517+
518+ @threading_helper .requires_working_threading ()
519+ def test___class___modification_multithreaded (self ):
520+ """ Note: this test isn't actually testing anything on its own.
521+ It requires a sys audithook to be set to crash on older Python.
522+ This should be the case anyways as our test suite sets
523+ an audit hook.
524+ """
525+
526+ class Foo :
527+ pass
528+
529+ class Bar :
530+ pass
531+
532+ thing = Foo ()
533+ def work ():
534+ foo = thing
535+ for _ in range (200 ):
536+ foo .__class__ = Bar
537+ type (foo )
538+ foo .__class__ = Foo
539+ type (foo )
540+
541+
542+ threads = []
543+ for _ in range (6 ):
544+ thread = threading .Thread (target = work )
545+ thread .start ()
546+ threads .append (thread )
547+
548+ for thread in threads :
549+ thread .join ()
550+
325551
326552if __name__ == "__main__" :
327553 unittest .main ()
0 commit comments