diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e51315e..8a4cf92 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,7 +29,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] experimental: [false] steps: - uses: actions/checkout@v2 diff --git a/test/failing_examples.py b/test/failing_examples.py index 09714d3..a555fde 100644 --- a/test/failing_examples.py +++ b/test/failing_examples.py @@ -29,7 +29,6 @@ FAILING_EXAMPLES = [ 'from foo import a,', 'from __future__ import whatever', 'from __future__ import braces', - 'from .__future__ import whatever', 'def f(x=3, y): pass', 'lambda x=3, y: x', '__debug__ = 1', @@ -216,7 +215,6 @@ FAILING_EXAMPLES = [ 'f"{\'\\\'}"', 'f"{#}"', "f'{1!b}'", - "f'{1:{5:{3}}}'", "f'{'", "f'{'", "f'}'", @@ -227,8 +225,6 @@ FAILING_EXAMPLES = [ "f'{1;1}'", "f'{a;}'", "f'{b\"\" \"\"}'", - # f-string expression part cannot include a backslash - r'''f"{'\n'}"''', 'async def foo():\n yield x\n return 1', 'async def foo():\n yield x\n return 1', @@ -413,3 +409,17 @@ if sys.version_info[:2] >= (3, 8): FAILING_EXAMPLES += [ "f'{1=!b}'", ] + +if sys.version_info[:2] < (3, 12): + FAILING_EXAMPLES += [ + # f-string expression part cannot include a backslash before 3.12 + r'''f"{'\n'}"''', + # this compiles successfully but fails when evaluated in 3.12 + "f'{1:{5:{3}}}'", + ] + +if sys.version_info[:2] < (3, 13): + # this compiles successfully but fails when evaluated in 3.13 + FAILING_EXAMPLES += [ + 'from .__future__ import whatever', + ] diff --git a/test/test_python_errors.py b/test/test_python_errors.py index 9686d14..0c89016 100644 --- a/test/test_python_errors.py +++ b/test/test_python_errors.py @@ -118,25 +118,57 @@ def _get_actual_exception(code): assert False, "The piece of code should raise an exception." # SyntaxError - if wanted == 'SyntaxError: assignment to keyword': + # Some errors have changed error message in later versions of Python, + # and we give a translation table here. We deal with special cases + # below. + translations = { + 'SyntaxError: f-string: unterminated string': + 'SyntaxError: EOL while scanning string literal', + "SyntaxError: f-string: expecting '}'": + 'SyntaxError: EOL while scanning string literal', + 'SyntaxError: f-string: empty expression not allowed': + 'SyntaxError: invalid syntax', + "SyntaxError: f-string expression part cannot include '#'": + 'SyntaxError: invalid syntax', + "SyntaxError: f-string: single '}' is not allowed": + 'SyntaxError: invalid syntax', + 'SyntaxError: cannot use starred expression here': + "SyntaxError: can't use starred expression here", + 'SyntaxError: f-string: cannot use starred expression here': + "SyntaxError: f-string: can't use starred expression here", + 'SyntaxError: unterminated string literal': + 'SyntaxError: EOL while scanning string literal', + 'SyntaxError: parameter without a default follows parameter with a default': + 'SyntaxError: non-default argument follows default argument', + "SyntaxError: 'yield from' outside function": + "SyntaxError: 'yield' outside function", + "SyntaxError: f-string: valid expression required before '}'": + 'SyntaxError: invalid syntax', + "SyntaxError: '{' was never closed": + 'SyntaxError: invalid syntax', + "SyntaxError: f-string: invalid conversion character 'b': expected 's', 'r', or 'a'": + "SyntaxError: f-string: invalid conversion character: expected 's', 'r', or 'a'", + "SyntaxError: (value error) Invalid format specifier ' 5' for object of type 'int'": + 'SyntaxError: f-string: expressions nested too deeply', + "SyntaxError: f-string: expecting a valid expression after '{'": + 'SyntaxError: f-string: invalid syntax', + "SyntaxError: f-string: expecting '=', or '!', or ':', or '}'": + 'SyntaxError: f-string: invalid syntax', + "SyntaxError: f-string: expecting '=', or '!', or ':', or '}'": + 'SyntaxError: f-string: invalid syntax', + } + + if wanted in translations: + wanted = translations[wanted] + elif wanted == 'SyntaxError: assignment to keyword': return [wanted, "SyntaxError: can't assign to keyword", 'SyntaxError: cannot assign to __debug__'], line_nr - elif wanted == 'SyntaxError: f-string: unterminated string': - wanted = 'SyntaxError: EOL while scanning string literal' elif wanted == 'SyntaxError: f-string expression part cannot include a backslash': return [ wanted, "SyntaxError: EOL while scanning string literal", "SyntaxError: unexpected character after line continuation character", ], line_nr - elif wanted == "SyntaxError: f-string: expecting '}'": - wanted = 'SyntaxError: EOL while scanning string literal' - elif wanted == 'SyntaxError: f-string: empty expression not allowed': - wanted = 'SyntaxError: invalid syntax' - elif wanted == "SyntaxError: f-string expression part cannot include '#'": - wanted = 'SyntaxError: invalid syntax' - elif wanted == "SyntaxError: f-string: single '}' is not allowed": - wanted = 'SyntaxError: invalid syntax' elif "Maybe you meant '==' instead of '='?" in wanted: wanted = wanted.removesuffix(" here. Maybe you meant '==' instead of '='?") elif re.match( @@ -148,18 +180,28 @@ def _get_actual_exception(code): wanted, ): wanted = 'SyntaxError: EOF while scanning triple-quoted string literal' - elif wanted == 'SyntaxError: cannot use starred expression here': - wanted = "SyntaxError: can't use starred expression here" - elif wanted == 'SyntaxError: f-string: cannot use starred expression here': - wanted = "SyntaxError: f-string: can't use starred expression here" elif re.match( r"IndentationError: expected an indented block after '[^']*' statement on line \d", wanted, ): wanted = 'IndentationError: expected an indented block' - elif wanted == 'SyntaxError: unterminated string literal': - wanted = 'SyntaxError: EOL while scanning string literal' - return [wanted], line_nr + # The following two errors are produced for both some f-strings and + # some non-f-strings in Python 3.13: + elif wanted == "SyntaxError: can't use starred expression here": + wanted = [ + "SyntaxError: can't use starred expression here", + "SyntaxError: f-string: can't use starred expression here" + ] + elif wanted == 'SyntaxError: cannot mix bytes and nonbytes literals': + wanted = [ + 'SyntaxError: cannot mix bytes and nonbytes literals', + 'SyntaxError: f-string: cannot mix bytes and nonbytes literals' + ] + + if isinstance(wanted, list): + return wanted, line_nr + else: + return [wanted], line_nr def test_default_except_error_postition():