@@ -415,6 +415,156 @@ def test_err_formatunraisable(self):
415415 # CRASHES formatunraisable(NULL, NULL)
416416
417417
418+ class TestUnicodeTranslateError (UnicodeTranslateError ):
419+ # UnicodeTranslateError takes 4 arguments instead of 5,
420+ # so we just make a UnicodeTranslateError class that is
421+ # compatible with the UnicodeError.__init__.
422+ def __init__ (self , encoding , * args , ** kwargs ):
423+ super ().__init__ (* args , ** kwargs )
424+
425+
426+ class TestUnicodeError (unittest .TestCase ):
427+
428+ def _check_no_crash (self , exc ):
429+ # ensure that the __str__() method does not crash
430+ _ = str (exc )
431+
432+ def test_unicode_encode_error_get_start (self ):
433+ get_start = _testcapi .unicode_encode_get_start
434+ self ._test_unicode_error_get_start ('x' , UnicodeEncodeError , get_start )
435+
436+ def test_unicode_decode_error_get_start (self ):
437+ get_start = _testcapi .unicode_decode_get_start
438+ self ._test_unicode_error_get_start (b'x' , UnicodeDecodeError , get_start )
439+
440+ def test_unicode_translate_error_get_start (self ):
441+ get_start = _testcapi .unicode_translate_get_start
442+ self ._test_unicode_error_get_start ('x' , TestUnicodeTranslateError , get_start )
443+
444+ def _test_unicode_error_get_start (self , literal , exc_type , get_start ):
445+ for obj_len , start , c_start in [
446+ # normal cases
447+ (5 , 0 , 0 ),
448+ (5 , 1 , 1 ),
449+ (5 , 2 , 2 ),
450+ # out of range start is clamped to max(0, obj_len - 1)
451+ (0 , 0 , 0 ),
452+ (0 , 1 , 0 ),
453+ (0 , 10 , 0 ),
454+ (5 , 5 , 4 ),
455+ (5 , 10 , 4 ),
456+ # negative values are allowed but clipped in the getter
457+ (0 , - 1 , 0 ),
458+ (1 , - 1 , 0 ),
459+ (2 , - 1 , 0 ),
460+ (2 , - 2 , 0 ),
461+ ]:
462+ obj = literal * obj_len
463+ with self .subTest (obj , exc_type = exc_type , start = start ):
464+ exc = exc_type ('utf-8' , obj , start , obj_len , 'reason' )
465+ self .assertEqual (get_start (exc ), c_start )
466+ self ._check_no_crash (exc )
467+
468+ def test_unicode_encode_error_set_start (self ):
469+ set_start = _testcapi .unicode_encode_set_start
470+ self ._test_unicode_error_set_start ('x' , UnicodeEncodeError , set_start )
471+
472+ def test_unicode_decode_error_set_start (self ):
473+ set_start = _testcapi .unicode_decode_set_start
474+ self ._test_unicode_error_set_start (b'x' , UnicodeDecodeError , set_start )
475+
476+ def test_unicode_translate_error_set_start (self ):
477+ set_start = _testcapi .unicode_translate_set_start
478+ self ._test_unicode_error_set_start ('x' , TestUnicodeTranslateError , set_start )
479+
480+ def _test_unicode_error_set_start (self , literal , exc_type , set_start ):
481+ obj_len = 5
482+ obj = literal * obj_len
483+ for new_start in range (- 2 * obj_len , 2 * obj_len ):
484+ with self .subTest ('C-API' , obj = obj , exc_type = exc_type , new_start = new_start ):
485+ exc = exc_type ('utf-8' , obj , 0 , obj_len , 'reason' )
486+ # arbitrary value is allowed in the C API setter
487+ set_start (exc , new_start )
488+ self .assertEqual (exc .start , new_start )
489+ self ._check_no_crash (exc )
490+
491+ with self .subTest ('Py-API' , obj = obj , exc_type = exc_type , new_start = new_start ):
492+ exc = exc_type ('utf-8' , obj , 0 , obj_len , 'reason' )
493+ # arbitrary value is allowed in the attribute setter
494+ exc .start = new_start
495+ self .assertEqual (exc .start , new_start )
496+ self ._check_no_crash (exc )
497+
498+ def test_unicode_encode_error_get_end (self ):
499+ get_end = _testcapi .unicode_encode_get_end
500+ self ._test_unicode_error_get_end ('x' , UnicodeEncodeError , get_end )
501+
502+ def test_unicode_decode_error_get_end (self ):
503+ get_end = _testcapi .unicode_decode_get_end
504+ self ._test_unicode_error_get_end (b'x' , UnicodeDecodeError , get_end )
505+
506+ def test_unicode_translate_error_get_end (self ):
507+ get_end = _testcapi .unicode_translate_get_end
508+ self ._test_unicode_error_get_end ('x' , TestUnicodeTranslateError , get_end )
509+
510+ def _test_unicode_error_get_end (self , literal , exc_type , get_end ):
511+ for obj_len , end , c_end in [
512+ # normal cases
513+ (5 , 0 , 1 ),
514+ (5 , 1 , 1 ),
515+ (5 , 2 , 2 ),
516+ # out-of-range clipped in [MIN(1, OBJLEN), MAX(MIN(1, OBJLEN), OBJLEN)]
517+ (0 , 0 , 0 ),
518+ (0 , 1 , 0 ),
519+ (0 , 10 , 0 ),
520+ (1 , 1 , 1 ),
521+ (1 , 2 , 1 ),
522+ (5 , 5 , 5 ),
523+ (5 , 5 , 5 ),
524+ (5 , 10 , 5 ),
525+ # negative values are allowed but clipped in the getter
526+ (0 , - 1 , 0 ),
527+ (1 , - 1 , 1 ),
528+ (2 , - 1 , 1 ),
529+ (2 , - 2 , 1 ),
530+ ]:
531+ obj = literal * obj_len
532+ with self .subTest (obj , exc_type = exc_type , end = end ):
533+ exc = exc_type ('utf-8' , obj , 0 , end , 'reason' )
534+ self .assertEqual (get_end (exc ), c_end )
535+ self ._check_no_crash (exc )
536+
537+ def test_unicode_encode_error_set_end (self ):
538+ set_end = _testcapi .unicode_encode_set_end
539+ self ._test_unicode_error_set_end ('x' , UnicodeEncodeError , set_end )
540+
541+ def test_unicode_decode_error_set_end (self ):
542+ set_end = _testcapi .unicode_decode_set_end
543+ self ._test_unicode_error_set_end (b'x' , UnicodeDecodeError , set_end )
544+
545+ def test_unicode_translate_error_set_end (self ):
546+ set_end = _testcapi .unicode_translate_set_end
547+ self ._test_unicode_error_set_end ('x' , TestUnicodeTranslateError , set_end )
548+
549+ def _test_unicode_error_set_end (self , literal , exc_type , set_end ):
550+ obj_len = 5
551+ obj = literal * obj_len
552+ for new_end in range (- 2 * obj_len , 2 * obj_len ):
553+ with self .subTest ('C-API' , obj = obj , exc_type = exc_type , new_end = new_end ):
554+ exc = exc_type ('utf-8' , obj , 0 , obj_len , 'reason' )
555+ # arbitrary value is allowed in the C API setter
556+ set_end (exc , new_end )
557+ self .assertEqual (exc .end , new_end )
558+ self ._check_no_crash (exc )
559+
560+ with self .subTest ('Py-API' , obj = obj , exc_type = exc_type , new_end = new_end ):
561+ exc = exc_type ('utf-8' , obj , 0 , obj_len , 'reason' )
562+ # arbitrary value is allowed in the attribute setter
563+ exc .end = new_end
564+ self .assertEqual (exc .end , new_end )
565+ self ._check_no_crash (exc )
566+
567+
418568class Test_PyUnstable_Exc_PrepReraiseStar (ExceptionIsLikeMixin , unittest .TestCase ):
419569
420570 def setUp (self ):
0 commit comments