@@ -154,14 +154,16 @@ def test_no_allow_code(self):
154154 data = {'a' : [({co , 0 },)]}
155155 dump = marshal .dumps (data , allow_code = True )
156156 self .assertEqual (marshal .loads (dump , allow_code = True ), data )
157- with self .assertRaises (ValueError ):
157+ with self .assertRaisesRegex (ValueError ,
158+ 'marshalling code objects is disallowed' ):
158159 marshal .dumps (data , allow_code = False )
159160 with self .assertRaises (ValueError ):
160161 marshal .loads (dump , allow_code = False )
161162
162163 marshal .dump (data , io .BytesIO (), allow_code = True )
163164 self .assertEqual (marshal .load (io .BytesIO (dump ), allow_code = True ), data )
164- with self .assertRaises (ValueError ):
165+ with self .assertRaisesRegex (ValueError ,
166+ 'marshalling code objects is disallowed' ):
165167 marshal .dump (data , io .BytesIO (), allow_code = False )
166168 with self .assertRaises (ValueError ):
167169 marshal .load (io .BytesIO (dump ), allow_code = False )
@@ -339,16 +341,29 @@ def test_reference_loop_dict(self):
339341 self .assertIsInstance (b , dict )
340342 self .assertIs (b [None ], b )
341343
344+ def check_reference_loop (self , a , typename , minversion ,
345+ oldmsg = 'object too deeply nested to marshal' ):
346+ # Only versions supporting references to the type detect the loop;
347+ # older versions fail for a different reason.
348+ for v in range (minversion ):
349+ with self .subTest (version = v ):
350+ with self .assertRaisesRegex (ValueError , oldmsg ):
351+ marshal .dumps (a , v )
352+ for v in range (minversion , marshal .version + 1 ):
353+ with self .subTest (version = v ):
354+ with self .assertRaisesRegex (
355+ ValueError ,
356+ f'cannot marshal recursion { typename } objects' ):
357+ marshal .dumps (a , v )
358+
342359 def test_reference_loop_tuple (self ):
343360 a = ([],)
344361 a [0 ].append (a )
345- for v in range (marshal .version + 1 ):
346- self .assertRaises (ValueError , marshal .dumps , a , v )
362+ self .check_reference_loop (a , 'tuple' , 3 )
347363
348364 a = ({},)
349365 a [0 ][None ] = a
350- for v in range (marshal .version + 1 ):
351- self .assertRaises (ValueError , marshal .dumps , a , v )
366+ self .check_reference_loop (a , 'tuple' , 3 )
352367
353368 def test_shared_reference_tuple (self ):
354369 # A tuple referenced more than once still round-trips with the
@@ -373,30 +388,28 @@ def f():
373388 # so we need to break the loop manually. See gh-148722.
374389 self .addCleanup (a .clear )
375390 a .append (code )
376- for v in range (marshal .version + 1 ):
377- self .assertRaises (ValueError , marshal .dumps , code , v )
391+ self .check_reference_loop (code , 'code' , 3 )
378392
379393 def test_reference_loop_slice (self ):
394+ oldmsg = 'marshalling slice objects requires version 5 or higher'
380395 a = slice ([], None )
381396 a .start .append (a )
382- for v in range (marshal .version + 1 ):
383- self .assertRaises (ValueError , marshal .dumps , a , v )
397+ self .check_reference_loop (a , 'slice' , 5 , oldmsg )
384398
385399 a = slice (None , [])
386400 a .stop .append (a )
387- for v in range (marshal .version + 1 ):
388- self .assertRaises (ValueError , marshal .dumps , a , v )
401+ self .check_reference_loop (a , 'slice' , 5 , oldmsg )
389402
390403 a = slice (None , None , [])
391404 a .step .append (a )
392- for v in range (marshal .version + 1 ):
393- self .assertRaises (ValueError , marshal .dumps , a , v )
405+ self .check_reference_loop (a , 'slice' , 5 , oldmsg )
394406
395407 def test_reference_loop_frozendict (self ):
396408 a = frozendict ({None : []})
397409 a [None ].append (a )
398- for v in range (marshal .version + 1 ):
399- self .assertRaises (ValueError , marshal .dumps , a , v )
410+ self .check_reference_loop (
411+ a , 'frozendict' , 6 ,
412+ 'marshalling frozendict objects requires version 6 or higher' )
400413
401414 def test_shared_reference_frozendict (self ):
402415 # A frozendict referenced more than once must round-trip with the
@@ -467,7 +480,9 @@ def test_exact_type_match(self):
467480 # Note: str subclasses are not tested because they get handled
468481 # by marshal's routines for objects supporting the buffer API.
469482 subtyp = type ('subtyp' , (typ ,), {})
470- self .assertRaises (ValueError , marshal .dumps , subtyp ())
483+ with self .assertRaisesRegex (ValueError ,
484+ r'cannot marshal \S*subtyp objects' ):
485+ marshal .dumps (subtyp ())
471486
472487 # Issue #1792 introduced a change in how marshal increases the size of its
473488 # internal buffer; this test ensures that the new code is exercised.
@@ -570,9 +585,25 @@ def test_unmarshallable(self):
570585 ('code' , code ))
571586 for name , arg in cases :
572587 with self .subTest (name , arg = arg ):
573- with self .assertRaisesRegex (ValueError , "unmarshallable object" ):
588+ with self .assertRaisesRegex (ValueError ,
589+ "cannot marshal type objects" ):
574590 marshal .dumps ((arg , memoryview (b'' )))
575591
592+ def test_error_in_set_item (self ):
593+ # Set items are sorted by their marshalled representation, and NaNs
594+ # are only distinguished by identity, so they are compared as
595+ # complex numbers.
596+ nan = float ('nan' )
597+ with self .assertRaisesRegex (TypeError , "'<' not supported" ):
598+ marshal .dumps ({complex (nan , 0 ), complex (nan , 0 )})
599+
600+ def test_error_in_buffer (self ):
601+ # The BufferError raised for a non-contiguous buffer is not replaced
602+ # with a generic error.
603+ step2 = slice (None , None , 2 )
604+ with self .assertRaises (BufferError ):
605+ marshal .dumps (memoryview (bytearray (b'abcdef' ))[step2 ])
606+
576607
577608LARGE_SIZE = 2 ** 31
578609pointer_size = 8 if sys .maxsize > 0xFFFFFFFF else 4
@@ -583,8 +614,14 @@ def write(self, s):
583614
584615@unittest .skipIf (LARGE_SIZE > sys .maxsize , "test cannot run on 32-bit systems" )
585616class LargeValuesTestCase (unittest .TestCase ):
586- def check_unmarshallable (self , data ):
587- self .assertRaises (ValueError , marshal .dump , data , NullWriter ())
617+ def check_unmarshallable (self , data , msg = 'object too large to marshal' ):
618+ with self .assertRaisesRegex (ValueError , msg ):
619+ marshal .dump (data , NullWriter ())
620+
621+ @support .bigmemtest (size = LARGE_SIZE , memuse = 4 , dry_run = False )
622+ def test_int (self , size ):
623+ # An int with more than SIZE32_MAX 15-bit digits.
624+ self .check_unmarshallable (1 << (15 * size ), 'int too large to marshal' )
588625
589626 @support .bigmemtest (size = LARGE_SIZE , memuse = 2 , dry_run = False )
590627 def test_bytes (self , size ):
@@ -717,7 +754,10 @@ def testFrozenDict(self):
717754 self .helper (dictobj )
718755
719756 for version in range (6 ):
720- with self .assertRaises (ValueError ):
757+ with self .assertRaisesRegex (
758+ ValueError ,
759+ 'marshalling frozendict objects requires '
760+ 'version 6 or higher' ):
721761 marshal .dumps (dictobj , version )
722762
723763 def testModule (self ):
@@ -786,7 +826,10 @@ def test_slice(self):
786826 self .helper (obj )
787827
788828 for version in range (5 ):
789- with self .assertRaises (ValueError ):
829+ with self .assertRaisesRegex (
830+ ValueError ,
831+ 'marshalling slice objects requires '
832+ 'version 5 or higher' ):
790833 marshal .dumps (obj , version )
791834
792835@support .cpython_only
@@ -818,7 +861,7 @@ def test_write_to_file_error(self):
818861
819862 def test_write_unmarshallable_to_file (self ):
820863 self .addCleanup (os_helper .unlink , os_helper .TESTFN )
821- with self .assertRaisesRegex (ValueError , 'unmarshallable object' ):
864+ with self .assertRaisesRegex (ValueError , 'cannot marshal object objects ' ):
822865 _testcapi .pymarshal_write_object_to_file (object (), os_helper .TESTFN ,
823866 marshal .version )
824867
0 commit comments