@@ -561,25 +561,28 @@ def _check_contents(self, tp, obj, contents):
561561class BytesMemoryviewTest (unittest .TestCase ,
562562 BaseMemoryviewTests , BaseBytesMemoryTests ):
563563
564- def test_comparison_blocks_release_during_buffer_callback (self ):
564+ def test_comparison_release_during_buffer_callback (self ):
565+ # Releasing the view from inside the other operand's __buffer__
566+ # succeeds, but the buffer stays pinned until the comparison ends:
567+ # the exporter cannot be resized under the running comparison.
565568 data = bytearray (b'abc' )
566569 view = memoryview (data )
567570 assert_raises = self .assertRaises
568571
569572 class Other :
570573 def __buffer__ (self , flags ):
571- with assert_raises (BufferError ):
572- view .release ()
574+ view .release ()
573575 with assert_raises (BufferError ):
574576 data .clear ()
575577 return memoryview (data )
576578
577- self . assertTrue ( view == Other () )
578- self .assertEqual ( view . tobytes (), b'abc' )
579- view .release ()
579+ view == Other ()
580+ with self .assertRaisesRegex ( ValueError , "released" ):
581+ view .tobytes ()
580582 data .clear ()
581583
582584 def test_comparison_releases_buffer_after_access (self ):
585+ # __release_buffer__ releasing the very view being compared.
583586 data = bytearray (b'abc' )
584587 view = memoryview (data )
585588
@@ -596,6 +599,7 @@ def __release_buffer__(self, buffer):
596599 data .clear ()
597600
598601 def test_assignment_releases_buffer_after_access (self ):
602+ # __release_buffer__ releasing the very view being assigned to.
599603 data = bytearray (b'abc' )
600604 view = memoryview (data )
601605
@@ -755,6 +759,8 @@ def test_gc_cleared_view(self):
755759 with self .assertRaisesRegex (ValueError , "released" ):
756760 view .tobytes ()
757761
762+ # Clearing the view while an operation is in progress must not
763+ # free the buffer under that operation, nor crash when it ends.
758764 data = bytearray (b"abc" )
759765 view = memoryview (data )
760766
@@ -764,7 +770,9 @@ def __buffer__(self, flags):
764770 return memoryview (data )
765771
766772 self .assertTrue (view == Other ())
767- self .assertEqual (view .tobytes (), b"abc" )
773+ with self .assertRaisesRegex (ValueError , "released" ):
774+ view .tobytes ()
775+ data .clear ()
768776
769777 def test_cast_order (self ):
770778 # gh-78959: cast(order=...) selects the result memory layout.
@@ -886,11 +894,15 @@ def test_pickle(self):
886894 with self .assertRaises (TypeError ):
887895 pickle .dumps (m , proto )
888896
889- def test_release_during_operation (self ):
890- # gh-92888: A callback cannot release a view while an operation uses it.
897+ def test_use_released_memory (self ):
898+ # gh-92888: Previously it was possible to use a memoryview even after
899+ # backing buffer is freed in certain cases. This tests that those
900+ # cases raise an exception.
891901 size = 128
892902 def release ():
893903 m .release ()
904+ nonlocal ba
905+ ba = bytearray (size )
894906 class MyIndex :
895907 def __index__ (self ):
896908 release ()
@@ -904,90 +916,85 @@ def __bool__(self):
904916 release ()
905917 return True
906918
919+ ba = None
907920 m = memoryview (bytearray (b'\xff ' * size ))
908- with self .assertRaises (BufferError ):
921+ with self .assertRaises (ValueError ):
909922 m [MyIndex ()]
910923
924+ ba = None
911925 m = memoryview (bytearray (b'\xff ' * size ))
912- with self .assertRaises (BufferError ):
913- m [:MyIndex ()]
926+ self .assertEqual (list (m [:MyIndex ()]), [255 ] * 4 )
914927
928+ ba = None
915929 m = memoryview (bytearray (b'\xff ' * size ))
916- with self .assertRaises (BufferError ):
917- m [MyIndex ():8 ]
930+ self .assertEqual (list (m [MyIndex ():8 ]), [255 ] * 4 )
918931
932+ ba = None
919933 m = memoryview (bytearray (b'\xff ' * size )).cast ('B' , (64 , 2 ))
920- with self .assertRaises ( BufferError ):
934+ with self .assertRaisesRegex ( ValueError , "operation forbidden" ):
921935 m [MyIndex (), 0 ]
922936
937+ ba = None
923938 m = memoryview (bytearray (b'\xff ' * size )).cast ('B' , (2 , 64 ))
924- with self .assertRaises ( BufferError ):
939+ with self .assertRaisesRegex ( ValueError , "operation forbidden" ):
925940 m [0 , MyIndex ()]
926941
942+ ba = None
927943 m = memoryview (bytearray (b'\xff ' * size ))
928- with self .assertRaises ( BufferError ):
944+ with self .assertRaisesRegex ( ValueError , "operation forbidden" ):
929945 m [MyIndex ()] = 42
930- self .assertEqual (m . tobytes () , b'\xff ' * size )
946+ self .assertEqual (ba [: 8 ] , b'\0 ' * 8 )
931947
948+ ba = None
932949 m = memoryview (bytearray (b'\xff ' * size ))
933- with self .assertRaises ( BufferError ):
950+ with self .assertRaisesRegex ( ValueError , "operation forbidden" ):
934951 m [:MyIndex ()] = b'spam'
935- self .assertEqual (m . tobytes () , b'\xff ' * size )
952+ self .assertEqual (ba [: 8 ] , b'\0 ' * 8 )
936953
954+ ba = None
937955 m = memoryview (bytearray (b'\xff ' * size ))
938- with self .assertRaises ( BufferError ):
956+ with self .assertRaisesRegex ( ValueError , "operation forbidden" ):
939957 m [MyIndex ():8 ] = b'spam'
940- self .assertEqual (m . tobytes () , b'\xff ' * size )
958+ self .assertEqual (ba [: 8 ] , b'\0 ' * 8 )
941959
960+ ba = None
942961 m = memoryview (bytearray (b'\xff ' * size )).cast ('B' , (64 , 2 ))
943- with self .assertRaises ( BufferError ):
962+ with self .assertRaisesRegex ( ValueError , "operation forbidden" ):
944963 m [MyIndex (), 0 ] = 42
945- self .assertEqual (m .tobytes (), b'\xff ' * size )
964+ self .assertEqual (ba [8 :16 ], b'\0 ' * 8 )
965+ ba = None
946966 m = memoryview (bytearray (b'\xff ' * size )).cast ('B' , (2 , 64 ))
947- with self .assertRaises ( BufferError ):
967+ with self .assertRaisesRegex ( ValueError , "operation forbidden" ):
948968 m [0 , MyIndex ()] = 42
949- self .assertEqual (m . tobytes () , b'\xff ' * size )
969+ self .assertEqual (ba [: 8 ] , b'\0 ' * 8 )
950970
971+ ba = None
951972 m = memoryview (bytearray (b'\xff ' * size ))
952- with self .assertRaises ( BufferError ):
973+ with self .assertRaisesRegex ( ValueError , "operation forbidden" ):
953974 m [0 ] = MyIndex ()
954- self .assertEqual (m . tobytes () , b'\xff ' * size )
975+ self .assertEqual (ba [: 8 ] , b'\0 ' * 8 )
955976
956977 for fmt in 'bhilqnBHILQN' :
957978 with self .subTest (fmt = fmt ):
979+ ba = None
958980 m = memoryview (bytearray (b'\xff ' * size )).cast (fmt )
959- with self .assertRaises ( BufferError ):
981+ with self .assertRaisesRegex ( ValueError , "operation forbidden" ):
960982 m [0 ] = MyIndex ()
961- self .assertEqual (m . tobytes () , b'\xff ' * size )
983+ self .assertEqual (ba [: 8 ] , b'\0 ' * 8 )
962984
963985 for fmt in 'fd' :
964986 with self .subTest (fmt = fmt ):
987+ ba = None
965988 m = memoryview (bytearray (b'\xff ' * size )).cast (fmt )
966- with self .assertRaises ( BufferError ):
989+ with self .assertRaisesRegex ( ValueError , "operation forbidden" ):
967990 m [0 ] = MyFloat ()
968- self .assertEqual (m . tobytes () , b'\xff ' * size )
991+ self .assertEqual (ba [: 8 ] , b'\0 ' * 8 )
969992
993+ ba = None
970994 m = memoryview (bytearray (b'\xff ' * size )).cast ('?' )
971- with self .assertRaises ( BufferError ):
995+ with self .assertRaisesRegex ( ValueError , "operation forbidden" ):
972996 m [0 ] = MyBool ()
973- self .assertEqual (m .tobytes (), b'\xff ' * size )
974-
975- data = bytearray (b'abc' )
976- m = memoryview (data ).cast ('c' )
977- assert_raises = self .assertRaises
978-
979- class CatchingIndex :
980- def __index__ (self ):
981- with assert_raises (BufferError ):
982- release ()
983- with assert_raises (BufferError ):
984- data .clear ()
985- return 0
986-
987- m [CatchingIndex ()] = b'x'
988- self .assertEqual (data , b'xbc' )
989- m .release ()
990- data .clear ()
997+ self .assertEqual (ba [:8 ], b'\0 ' * 8 )
991998
992999 def test_buffer_reference_loop (self ):
9931000 m = memoryview (b'abc' ).__buffer__ (0 )
0 commit comments