@@ -1790,6 +1790,7 @@ def f():
17901790 ]
17911791 self .assertEqual (result_lines , expected )
17921792
1793+
17931794class TestKeywordTypoSuggestions (unittest .TestCase ):
17941795 TYPO_CASES = [
17951796 ("with block ad something:\n pass" , "and" ),
@@ -5414,6 +5415,92 @@ def expected(t, m, fn, l, f, E, e, z, n):
54145415 ]
54155416 self .assertEqual (actual , expected (** colors ))
54165417
5418+ def test_colorized_traceback_unicode (self ):
5419+ try :
5420+ 啊哈 = 1 ; 啊哈 / 0 ####
5421+ except Exception as e :
5422+ exc = traceback .TracebackException .from_exception (e )
5423+
5424+ actual = "" .join (exc .format (colorize = True )).splitlines ()
5425+ def expected (t , m , fn , l , f , E , e , z , n ):
5426+ return [
5427+ f" 啊哈=1; { e } 啊哈{ z } { E } /{ z } { e } 0{ z } ####" ,
5428+ f" { e } ~~~~{ z } { E } ^{ z } { e } ~{ z } " ,
5429+ ]
5430+ self .assertEqual (actual [2 :4 ], expected (** colors ))
5431+
5432+ try :
5433+ ééééé / 0
5434+ except Exception as e :
5435+ exc = traceback .TracebackException .from_exception (e )
5436+
5437+ actual = "" .join (exc .format (colorize = True )).splitlines ()
5438+ def expected (t , m , fn , l , f , E , e , z , n ):
5439+ return [
5440+ f" { E } ééééé{ z } /0" ,
5441+ f" { E } ^^^^^{ z } " ,
5442+ ]
5443+ self .assertEqual (actual [2 :4 ], expected (** colors ))
5444+
5445+ def test_colorized_syntax_error_ascii_display_width (self ):
5446+ """Caret alignment for ASCII edge cases handled by _wlen.
5447+
5448+ The old ASCII fast track in _display_width returned the raw character
5449+ offset for ASCII strings, which is wrong for CTRL-Z (display width 2)
5450+ and ANSI escape sequences (display width 0).
5451+ """
5452+ E = colors ["E" ]
5453+ z = colors ["z" ]
5454+ t = colors ["t" ]
5455+ m = colors ["m" ]
5456+ fn = colors ["fn" ]
5457+ l = colors ["l" ]
5458+
5459+ def _make_syntax_error (text , offset , end_offset ):
5460+ err = SyntaxError ("invalid syntax" )
5461+ err .filename = "<string>"
5462+ err .lineno = 1
5463+ err .end_lineno = 1
5464+ err .text = text
5465+ err .offset = offset
5466+ err .end_offset = end_offset
5467+ return err
5468+
5469+ # CTRL-Z (\x1a) is ASCII but displayed as ^Z (2 columns).
5470+ # Verify caret aligns when CTRL-Z precedes the error.
5471+ err = _make_syntax_error ("a\x1a $\n " , offset = 3 , end_offset = 4 )
5472+ exc = traceback .TracebackException .from_exception (err )
5473+ actual = "" .join (exc .format (colorize = True ))
5474+ # 'a' (1 col) + '\x1a' (2 cols) = 3 cols before '$'
5475+ self .assertIn (
5476+ f' File { fn } "<string>"{ z } , line { l } 1{ z } \n '
5477+ f' a\x1a { E } ${ z } \n '
5478+ f' { " " * 3 } { E } ^{ z } \n '
5479+ f'{ t } SyntaxError{ z } : { m } invalid syntax{ z } \n ' ,
5480+ actual ,
5481+ )
5482+
5483+ # CTRL-Z in the highlighted (error) region counts as 2 columns.
5484+ err = _make_syntax_error ("$\x1a \n " , offset = 1 , end_offset = 3 )
5485+ exc = traceback .TracebackException .from_exception (err )
5486+ actual = "" .join (exc .format (colorize = True ))
5487+ # '$' (1 col) + '\x1a' (2 cols) = 3 columns of carets
5488+ self .assertIn (
5489+ f' { E } $\x1a { z } \n '
5490+ f' { E } { "^" * 3 } { z } \n ' ,
5491+ actual ,
5492+ )
5493+
5494+ # ANSI escape sequences are ASCII but take 0 display columns.
5495+ err = _make_syntax_error ("a\x1b [1mb$\n " , offset = 7 , end_offset = 8 )
5496+ exc = traceback .TracebackException .from_exception (err )
5497+ actual = "" .join (exc .format (colorize = True ))
5498+ # 'a' (1 col) + '\x1b[1m' (0 cols) + 'b' (1 col) = 2 before '$'
5499+ self .assertIn (
5500+ f' a\x1b [1mb{ E } ${ z } \n '
5501+ f' { " " * 2 } { E } ^{ z } \n ' ,
5502+ actual ,
5503+ )
54175504
54185505class TestLazyImportSuggestions (unittest .TestCase ):
54195506 """Test that lazy imports are not reified when computing AttributeError suggestions."""
0 commit comments