@@ -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 ():
@@ -409,27 +421,6 @@ def test_loads_reference_loop_dict(self):
409421 self .assertIs (a [None ], a )
410422
411423 def test_loads_abnormal_reference_loops (self ):
412- # Indirect self-references of tuples.
413- data = b'\xa8 \x01 \x00 \x00 \x00 [\x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' # ([<R>],)
414- a = marshal .loads (data )
415- self .assertIsInstance (a , tuple )
416- self .assertIsInstance (a [0 ], list )
417- self .assertIs (a [0 ][0 ], a )
418-
419- data = b'\xa8 \x01 \x00 \x00 \x00 {Nr\x00 \x00 \x00 \x00 0' # ({None: <R>},)
420- a = marshal .loads (data )
421- self .assertIsInstance (a , tuple )
422- self .assertIsInstance (a [0 ], dict )
423- self .assertIs (a [0 ][None ], a )
424-
425- # Direct self-reference which cannot be created in Python.
426- # This creates a reference loop which cannot be collected.
427- if False :
428- data = b'\xa8 \x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' # (<R>,)
429- a = marshal .loads (data )
430- self .assertIsInstance (a , tuple )
431- self .assertIs (a [0 ], a )
432-
433424 # Direct self-references which cannot be created in Python
434425 # because of unhashability.
435426 data = b'\xfb r\x00 \x00 \x00 \x00 N0' # {<R>: None}
@@ -439,6 +430,8 @@ def test_loads_abnormal_reference_loops(self):
439430
440431 for data in [
441432 # Indirect self-references of immutable objects.
433+ b'\xa8 \x01 \x00 \x00 \x00 [\x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' , # ([<R>],)
434+ b'\xa8 \x01 \x00 \x00 \x00 {Nr\x00 \x00 \x00 \x00 0' , # ({None: <R>},)
442435 b'\xba [\x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 NN' , # slice([<R>], None)
443436 b'\xba N[\x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 N' , # slice(None, [<R>])
444437 b'\xba NN[\x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' , # slice(None, None, [<R>])
@@ -449,12 +442,18 @@ def test_loads_abnormal_reference_loops(self):
449442 b'\xfd N{Nr\x00 \x00 \x00 \x00 00' , # frozendict({None: {None: <R>})
450443
451444 # Direct self-references which cannot be created in Python.
445+ b'\xa8 \x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' , # (<R>,)
452446 b'\xbe \x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' , # frozenset({<R>})
453447 b'\xfd Nr\x00 \x00 \x00 \x00 0' , # frozendict({None: <R>})
454448 b'\xfd r\x00 \x00 \x00 \x00 N0' , # frozendict({<R>: None})
455449 b'\xba r\x00 \x00 \x00 \x00 NN' , # slice(<R>, None)
456450 b'\xba Nr\x00 \x00 \x00 \x00 N' , # slice(None, <R>)
457451 b'\xba NNr\x00 \x00 \x00 \x00 ' , # slice(None, None, <R>)
452+
453+ # Indirect self-references which cannot be created in Python
454+ # because of unhashability.
455+ b'\xa8 \x01 \x00 \x00 \x00 {r\x00 \x00 \x00 \x00 N0' , # ({<R>: None},)
456+ b'\xa8 \x01 \x00 \x00 \x00 <\x01 \x00 \x00 \x00 r\x00 \x00 \x00 \x00 ' , # ({<R>},)
458457 ]:
459458 with self .subTest (data = data ):
460459 self .assertRaises (ValueError , marshal .loads , data )
0 commit comments