@@ -342,14 +342,26 @@ def test_reference_loop_dict(self):
342342 def test_reference_loop_tuple (self ):
343343 a = ([],)
344344 a [0 ].append (a )
345- for v in range (3 ):
345+ for v in range (marshal . version + 1 ):
346346 self .assertRaises (ValueError , marshal .dumps , a , v )
347+
348+ a = ({},)
349+ a [0 ][None ] = a
350+ for v in range (marshal .version + 1 ):
351+ self .assertRaises (ValueError , marshal .dumps , a , v )
352+
353+ def test_shared_reference_tuple (self ):
354+ # A tuple referenced more than once still round-trips with the
355+ # shared identity preserved.
356+ a = (1 , 2 )
347357 for v in range (3 , marshal .version + 1 ):
348- d = marshal .dumps (a , v )
349- b = marshal .loads (d )
350- self .assertIsInstance (b , tuple )
351- self .assertIsInstance (b [0 ], list )
352- self .assertIs (b [0 ][0 ], b )
358+ b = marshal .loads (marshal .dumps ([a , a ], v ))
359+ self .assertEqual (b [0 ], a )
360+ self .assertIs (b [0 ], b [1 ])
361+ big = tuple (range (300 )) # too large for TYPE_SMALL_TUPLE
362+ b = marshal .loads (marshal .dumps ([big , big ]))
363+ self .assertEqual (b [0 ], big )
364+ self .assertIs (b [0 ], b [1 ])
353365
354366 def test_reference_loop_code (self ):
355367 def f ():
@@ -399,27 +411,6 @@ def test_loads_reference_loop_dict(self):
399411 self .assertIs (a [None ], a )
400412
401413 def test_loads_abnormal_reference_loops (self ):
402- # Indirect self-references of tuples.
403- data = b'\xa8 \x01 \x00 \x00 \x00 [\x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' # ([<R>],)
404- a = marshal .loads (data )
405- self .assertIsInstance (a , tuple )
406- self .assertIsInstance (a [0 ], list )
407- self .assertIs (a [0 ][0 ], a )
408-
409- data = b'\xa8 \x01 \x00 \x00 \x00 {Nr\x00 \x00 \x00 \x00 0' # ({None: <R>},)
410- a = marshal .loads (data )
411- self .assertIsInstance (a , tuple )
412- self .assertIsInstance (a [0 ], dict )
413- self .assertIs (a [0 ][None ], a )
414-
415- # Direct self-reference which cannot be created in Python.
416- # This creates a reference loop which cannot be collected.
417- if False :
418- data = b'\xa8 \x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' # (<R>,)
419- a = marshal .loads (data )
420- self .assertIsInstance (a , tuple )
421- self .assertIs (a [0 ], a )
422-
423414 # Direct self-references which cannot be created in Python
424415 # because of unhashability.
425416 data = b'\xfb r\x00 \x00 \x00 \x00 N0' # {<R>: None}
@@ -429,6 +420,8 @@ def test_loads_abnormal_reference_loops(self):
429420
430421 for data in [
431422 # Indirect self-references of immutable objects.
423+ b'\xa8 \x01 \x00 \x00 \x00 [\x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' , # ([<R>],)
424+ b'\xa8 \x01 \x00 \x00 \x00 {Nr\x00 \x00 \x00 \x00 0' , # ({None: <R>},)
432425 b'\xba [\x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 NN' , # slice([<R>], None)
433426 b'\xba N[\x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 N' , # slice(None, [<R>])
434427 b'\xba NN[\x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' , # slice(None, None, [<R>])
@@ -439,12 +432,18 @@ def test_loads_abnormal_reference_loops(self):
439432 b'\xfd N{Nr\x00 \x00 \x00 \x00 00' , # frozendict({None: {None: <R>})
440433
441434 # Direct self-references which cannot be created in Python.
435+ b'\xa8 \x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' , # (<R>,)
442436 b'\xbe \x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' , # frozenset({<R>})
443437 b'\xfd Nr\x00 \x00 \x00 \x00 0' , # frozendict({None: <R>})
444438 b'\xfd r\x00 \x00 \x00 \x00 N0' , # frozendict({<R>: None})
445439 b'\xba r\x00 \x00 \x00 \x00 NN' , # slice(<R>, None)
446440 b'\xba Nr\x00 \x00 \x00 \x00 N' , # slice(None, <R>)
447441 b'\xba NNr\x00 \x00 \x00 \x00 ' , # slice(None, None, <R>)
442+
443+ # Indirect self-references which cannot be created in Python
444+ # because of unhashability.
445+ b'\xa8 \x01 \x00 \x00 \x00 {r\x00 \x00 \x00 \x00 N0' , # ({<R>: None},)
446+ b'\xa8 \x01 \x00 \x00 \x00 <\x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' , # ({<R>},)
448447 ]:
449448 with self .subTest (data = data ):
450449 self .assertRaises (ValueError , marshal .loads , data )
0 commit comments