mirror of
https://github.com/davidhalter/parso.git
synced 2025-12-07 21:34:32 +08:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be9f5a401f | ||
|
|
7e4777b775 | ||
|
|
e99dbdd536 | ||
|
|
e22dc67aa1 | ||
|
|
baa3c90d85 | ||
|
|
23b1cdf73d | ||
|
|
a73af5c709 | ||
|
|
9328cffce3 | ||
|
|
f670e6e7dc | ||
|
|
338a576027 | ||
|
|
9ddffca4da | ||
|
|
06db036e23 | ||
|
|
c792ae546c | ||
|
|
1c01dafc2b | ||
|
|
1ca6b1f3e8 | ||
|
|
5a9349ae58 | ||
|
|
279fd6903e | ||
|
|
e255b69cb8 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
|
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
|
||||||
experimental: [false]
|
experimental: [false]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|||||||
16
.readthedocs.yml
Normal file
16
.readthedocs.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
python:
|
||||||
|
install:
|
||||||
|
- method: pip
|
||||||
|
path: .
|
||||||
|
extra_requirements:
|
||||||
|
- docs
|
||||||
|
|
||||||
|
submodules:
|
||||||
|
include: all
|
||||||
|
|
||||||
|
build:
|
||||||
|
os: ubuntu-22.04
|
||||||
|
tools:
|
||||||
|
python: "3.11"
|
||||||
@@ -6,6 +6,11 @@ Changelog
|
|||||||
Unreleased
|
Unreleased
|
||||||
++++++++++
|
++++++++++
|
||||||
|
|
||||||
|
0.8.5 (2025-08-23)
|
||||||
|
++++++++++++++++++
|
||||||
|
|
||||||
|
- Add a fallback grammar for Python 3.14+
|
||||||
|
|
||||||
0.8.4 (2024-04-05)
|
0.8.4 (2024-04-05)
|
||||||
++++++++++++++++++
|
++++++++++++++++++
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ Resources
|
|||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
pip install parso
|
pip install parso
|
||||||
|
|
||||||
Future
|
Future
|
||||||
@@ -88,8 +90,7 @@ Acknowledgements
|
|||||||
|
|
||||||
- Guido van Rossum (@gvanrossum) for creating the parser generator pgen2
|
- Guido van Rossum (@gvanrossum) for creating the parser generator pgen2
|
||||||
(originally used in lib2to3).
|
(originally used in lib2to3).
|
||||||
- `Salome Schneider <https://www.crepes-schnaegg.ch/cr%C3%AApes-schn%C3%A4gg/kunst-f%C3%BCrs-cr%C3%AApes-mobil/>`_
|
- Salome Schneider for the extremely awesome parso logo.
|
||||||
for the extremely awesome parso logo.
|
|
||||||
|
|
||||||
|
|
||||||
.. _jedi: https://github.com/davidhalter/jedi
|
.. _jedi: https://github.com/davidhalter/jedi
|
||||||
|
|||||||
9
SECURITY.md
Normal file
9
SECURITY.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
If security issues arise, we will try to fix those as soon as possible.
|
||||||
|
|
||||||
|
Due to Parso's nature, Security Issues will probably be extremely rare, but we will of course treat them seriously.
|
||||||
|
|
||||||
|
## Reporting Security Problems
|
||||||
|
|
||||||
|
If you need to report a security vulnerability, please send an email to davidhalter88@gmail.com. Typically, I will respond in the next few business days.
|
||||||
@@ -13,7 +13,7 @@ from parso.utils import parse_version_string
|
|||||||
|
|
||||||
collect_ignore = ["setup.py"]
|
collect_ignore = ["setup.py"]
|
||||||
|
|
||||||
_SUPPORTED_VERSIONS = '3.6', '3.7', '3.8', '3.9', '3.10'
|
_SUPPORTED_VERSIONS = '3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='session')
|
@pytest.fixture(scope='session')
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ From git
|
|||||||
--------
|
--------
|
||||||
If you want to install the current development version (master branch)::
|
If you want to install the current development version (master branch)::
|
||||||
|
|
||||||
sudo pip install -e git://github.com/davidhalter/parso.git#egg=parso
|
sudo pip install -e git+https://github.com/davidhalter/parso.git#egg=parso
|
||||||
|
|
||||||
|
|
||||||
Manual installation from a downloaded package (not recommended)
|
Manual installation from a downloaded package (not recommended)
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ from parso.grammar import Grammar, load_grammar
|
|||||||
from parso.utils import split_lines, python_bytes_to_unicode
|
from parso.utils import split_lines, python_bytes_to_unicode
|
||||||
|
|
||||||
|
|
||||||
__version__ = '0.8.4'
|
__version__ = '0.8.5'
|
||||||
|
|
||||||
|
|
||||||
def parse(code=None, **kwargs):
|
def parse(code=None, **kwargs):
|
||||||
|
|||||||
@@ -239,7 +239,16 @@ def load_grammar(*, version: str = None, path: str = None):
|
|||||||
:param str version: A python version string, e.g. ``version='3.8'``.
|
:param str version: A python version string, e.g. ``version='3.8'``.
|
||||||
:param str path: A path to a grammar file
|
:param str path: A path to a grammar file
|
||||||
"""
|
"""
|
||||||
version_info = parse_version_string(version)
|
# NOTE: this (3, 14) should be updated to the latest version parso supports.
|
||||||
|
# (if this doesn't happen, users will get older syntaxes and spurious warnings)
|
||||||
|
passed_version_info = parse_version_string(version)
|
||||||
|
version_info = min(passed_version_info, PythonVersionInfo(3, 14))
|
||||||
|
|
||||||
|
# # NOTE: this is commented out until parso properly supports newer Python grammars.
|
||||||
|
# if passed_version_info != version_info:
|
||||||
|
# warnings.warn('parso does not support %s.%s yet.' % (
|
||||||
|
# passed_version_info.major, passed_version_info.minor
|
||||||
|
# ))
|
||||||
|
|
||||||
file = path or os.path.join(
|
file = path or os.path.join(
|
||||||
'python',
|
'python',
|
||||||
|
|||||||
169
parso/python/grammar314.txt
Normal file
169
parso/python/grammar314.txt
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
# Grammar for Python
|
||||||
|
|
||||||
|
# NOTE WELL: You should also follow all the steps listed at
|
||||||
|
# https://devguide.python.org/grammar/
|
||||||
|
|
||||||
|
# Start symbols for the grammar:
|
||||||
|
# single_input is a single interactive statement;
|
||||||
|
# file_input is a module or sequence of commands read from an input file;
|
||||||
|
# eval_input is the input for the eval() functions.
|
||||||
|
# NB: compound_stmt in single_input is followed by extra NEWLINE!
|
||||||
|
single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
|
||||||
|
file_input: stmt* ENDMARKER
|
||||||
|
eval_input: testlist NEWLINE* ENDMARKER
|
||||||
|
|
||||||
|
decorator: '@' namedexpr_test NEWLINE
|
||||||
|
decorators: decorator+
|
||||||
|
decorated: decorators (classdef | funcdef | async_funcdef)
|
||||||
|
|
||||||
|
async_funcdef: 'async' funcdef
|
||||||
|
funcdef: 'def' NAME parameters ['->' test] ':' suite
|
||||||
|
|
||||||
|
parameters: '(' [typedargslist] ')'
|
||||||
|
typedargslist: (
|
||||||
|
(tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [',' [ tfpdef ['=' test] (
|
||||||
|
',' tfpdef ['=' test])* ([',' [
|
||||||
|
'*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
|
||||||
|
| '**' tfpdef [',']]])
|
||||||
|
| '*' [tfpdef] (',' tfpdef ['=' test])* ([',' ['**' tfpdef [',']]])
|
||||||
|
| '**' tfpdef [',']]] )
|
||||||
|
| (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [
|
||||||
|
'*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
|
||||||
|
| '**' tfpdef [',']]]
|
||||||
|
| '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
|
||||||
|
| '**' tfpdef [','])
|
||||||
|
)
|
||||||
|
tfpdef: NAME [':' test]
|
||||||
|
varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
|
||||||
|
'*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
|
||||||
|
| '**' vfpdef [',']]]
|
||||||
|
| '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
|
||||||
|
| '**' vfpdef [',']) ]] | (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
|
||||||
|
'*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
|
||||||
|
| '**' vfpdef [',']]]
|
||||||
|
| '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
|
||||||
|
| '**' vfpdef [',']
|
||||||
|
)
|
||||||
|
vfpdef: NAME
|
||||||
|
|
||||||
|
stmt: simple_stmt | compound_stmt | NEWLINE
|
||||||
|
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
|
||||||
|
small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
|
||||||
|
import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
|
||||||
|
expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
|
||||||
|
('=' (yield_expr|testlist_star_expr))*)
|
||||||
|
annassign: ':' test ['=' (yield_expr|testlist_star_expr)]
|
||||||
|
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
|
||||||
|
augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
|
||||||
|
'<<=' | '>>=' | '**=' | '//=')
|
||||||
|
# For normal and annotated assignments, additional restrictions enforced by the interpreter
|
||||||
|
del_stmt: 'del' exprlist
|
||||||
|
pass_stmt: 'pass'
|
||||||
|
flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
|
||||||
|
break_stmt: 'break'
|
||||||
|
continue_stmt: 'continue'
|
||||||
|
return_stmt: 'return' [testlist_star_expr]
|
||||||
|
yield_stmt: yield_expr
|
||||||
|
raise_stmt: 'raise' [test ['from' test]]
|
||||||
|
import_stmt: import_name | import_from
|
||||||
|
import_name: 'import' dotted_as_names
|
||||||
|
# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
|
||||||
|
import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+)
|
||||||
|
'import' ('*' | '(' import_as_names ')' | import_as_names))
|
||||||
|
import_as_name: NAME ['as' NAME]
|
||||||
|
dotted_as_name: dotted_name ['as' NAME]
|
||||||
|
import_as_names: import_as_name (',' import_as_name)* [',']
|
||||||
|
dotted_as_names: dotted_as_name (',' dotted_as_name)*
|
||||||
|
dotted_name: NAME ('.' NAME)*
|
||||||
|
global_stmt: 'global' NAME (',' NAME)*
|
||||||
|
nonlocal_stmt: 'nonlocal' NAME (',' NAME)*
|
||||||
|
assert_stmt: 'assert' test [',' test]
|
||||||
|
|
||||||
|
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt
|
||||||
|
async_stmt: 'async' (funcdef | with_stmt | for_stmt)
|
||||||
|
if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite]
|
||||||
|
while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite]
|
||||||
|
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
|
||||||
|
try_stmt: ('try' ':' suite
|
||||||
|
((except_clause ':' suite)+
|
||||||
|
['else' ':' suite]
|
||||||
|
['finally' ':' suite] |
|
||||||
|
'finally' ':' suite))
|
||||||
|
with_stmt: 'with' with_item (',' with_item)* ':' suite
|
||||||
|
with_item: test ['as' expr]
|
||||||
|
# NB compile.c makes sure that the default except clause is last
|
||||||
|
except_clause: 'except' [test ['as' NAME]]
|
||||||
|
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
|
||||||
|
|
||||||
|
namedexpr_test: test [':=' test]
|
||||||
|
test: or_test ['if' or_test 'else' test] | lambdef
|
||||||
|
lambdef: 'lambda' [varargslist] ':' test
|
||||||
|
or_test: and_test ('or' and_test)*
|
||||||
|
and_test: not_test ('and' not_test)*
|
||||||
|
not_test: 'not' not_test | comparison
|
||||||
|
comparison: expr (comp_op expr)*
|
||||||
|
# <> isn't actually a valid comparison operator in Python. It's here for the
|
||||||
|
# sake of a __future__ import described in PEP 401 (which really works :-)
|
||||||
|
comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
|
||||||
|
star_expr: '*' expr
|
||||||
|
expr: xor_expr ('|' xor_expr)*
|
||||||
|
xor_expr: and_expr ('^' and_expr)*
|
||||||
|
and_expr: shift_expr ('&' shift_expr)*
|
||||||
|
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
|
||||||
|
arith_expr: term (('+'|'-') term)*
|
||||||
|
term: factor (('*'|'@'|'/'|'%'|'//') factor)*
|
||||||
|
factor: ('+'|'-'|'~') factor | power
|
||||||
|
power: atom_expr ['**' factor]
|
||||||
|
atom_expr: ['await'] atom trailer*
|
||||||
|
atom: ('(' [yield_expr|testlist_comp] ')' |
|
||||||
|
'[' [testlist_comp] ']' |
|
||||||
|
'{' [dictorsetmaker] '}' |
|
||||||
|
NAME | NUMBER | strings | '...' | 'None' | 'True' | 'False')
|
||||||
|
testlist_comp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] )
|
||||||
|
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
|
||||||
|
subscriptlist: subscript (',' subscript)* [',']
|
||||||
|
subscript: test [':=' test] | [test] ':' [test] [sliceop]
|
||||||
|
sliceop: ':' [test]
|
||||||
|
exprlist: (expr|star_expr) (',' (expr|star_expr))* [',']
|
||||||
|
testlist: test (',' test)* [',']
|
||||||
|
dictorsetmaker: ( ((test ':' test | '**' expr)
|
||||||
|
(comp_for | (',' (test ':' test | '**' expr))* [','])) |
|
||||||
|
((test [':=' test] | star_expr)
|
||||||
|
(comp_for | (',' (test [':=' test] | star_expr))* [','])) )
|
||||||
|
|
||||||
|
classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
|
||||||
|
|
||||||
|
arglist: argument (',' argument)* [',']
|
||||||
|
|
||||||
|
# The reason that keywords are test nodes instead of NAME is that using NAME
|
||||||
|
# results in an ambiguity. ast.c makes sure it's a NAME.
|
||||||
|
# "test '=' test" is really "keyword '=' test", but we have no such token.
|
||||||
|
# These need to be in a single rule to avoid grammar that is ambiguous
|
||||||
|
# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr,
|
||||||
|
# we explicitly match '*' here, too, to give it proper precedence.
|
||||||
|
# Illegal combinations and orderings are blocked in ast.c:
|
||||||
|
# multiple (test comp_for) arguments are blocked; keyword unpackings
|
||||||
|
# that precede iterable unpackings are blocked; etc.
|
||||||
|
argument: ( test [comp_for] |
|
||||||
|
test ':=' test |
|
||||||
|
test '=' test |
|
||||||
|
'**' test |
|
||||||
|
'*' test )
|
||||||
|
|
||||||
|
comp_iter: comp_for | comp_if
|
||||||
|
sync_comp_for: 'for' exprlist 'in' or_test [comp_iter]
|
||||||
|
comp_for: ['async'] sync_comp_for
|
||||||
|
comp_if: 'if' or_test [comp_iter]
|
||||||
|
|
||||||
|
# not used in grammar, but may appear in "node" passed from Parser to Compiler
|
||||||
|
encoding_decl: NAME
|
||||||
|
|
||||||
|
yield_expr: 'yield' [yield_arg]
|
||||||
|
yield_arg: 'from' test | testlist_star_expr
|
||||||
|
|
||||||
|
strings: (STRING | fstring)+
|
||||||
|
fstring: FSTRING_START fstring_content* FSTRING_END
|
||||||
|
fstring_content: FSTRING_STRING | fstring_expr
|
||||||
|
fstring_conversion: '!' NAME
|
||||||
|
fstring_expr: '{' (testlist_comp | yield_expr) ['='] [ fstring_conversion ] [ fstring_format_spec ] '}'
|
||||||
|
fstring_format_spec: ':' fstring_content*
|
||||||
@@ -10,3 +10,6 @@ norecursedirs = .* docs scripts normalizer_issue_files build
|
|||||||
# fine as long as we are using `clean_jedi_cache` as a session scoped
|
# fine as long as we are using `clean_jedi_cache` as a session scoped
|
||||||
# fixture.
|
# fixture.
|
||||||
usefixtures = clean_parso_cache
|
usefixtures = clean_parso_cache
|
||||||
|
|
||||||
|
# Disallow warnings
|
||||||
|
filterwarnings = error
|
||||||
|
|||||||
5
setup.py
5
setup.py
@@ -40,6 +40,11 @@ setup(
|
|||||||
'Programming Language :: Python :: 3.7',
|
'Programming Language :: Python :: 3.7',
|
||||||
'Programming Language :: Python :: 3.8',
|
'Programming Language :: Python :: 3.8',
|
||||||
'Programming Language :: Python :: 3.9',
|
'Programming Language :: Python :: 3.9',
|
||||||
|
'Programming Language :: Python :: 3.10',
|
||||||
|
'Programming Language :: Python :: 3.11',
|
||||||
|
'Programming Language :: Python :: 3.12',
|
||||||
|
'Programming Language :: Python :: 3.13',
|
||||||
|
'Programming Language :: Python :: 3.14',
|
||||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||||
'Topic :: Text Editors :: Integrated Development Environments (IDE)',
|
'Topic :: Text Editors :: Integrated Development Environments (IDE)',
|
||||||
'Topic :: Utilities',
|
'Topic :: Utilities',
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ FAILING_EXAMPLES = [
|
|||||||
'from foo import a,',
|
'from foo import a,',
|
||||||
'from __future__ import whatever',
|
'from __future__ import whatever',
|
||||||
'from __future__ import braces',
|
'from __future__ import braces',
|
||||||
'from .__future__ import whatever',
|
|
||||||
'def f(x=3, y): pass',
|
'def f(x=3, y): pass',
|
||||||
'lambda x=3, y: x',
|
'lambda x=3, y: x',
|
||||||
'__debug__ = 1',
|
'__debug__ = 1',
|
||||||
@@ -216,7 +215,6 @@ FAILING_EXAMPLES = [
|
|||||||
'f"{\'\\\'}"',
|
'f"{\'\\\'}"',
|
||||||
'f"{#}"',
|
'f"{#}"',
|
||||||
"f'{1!b}'",
|
"f'{1!b}'",
|
||||||
"f'{1:{5:{3}}}'",
|
|
||||||
"f'{'",
|
"f'{'",
|
||||||
"f'{'",
|
"f'{'",
|
||||||
"f'}'",
|
"f'}'",
|
||||||
@@ -227,8 +225,6 @@ FAILING_EXAMPLES = [
|
|||||||
"f'{1;1}'",
|
"f'{1;1}'",
|
||||||
"f'{a;}'",
|
"f'{a;}'",
|
||||||
"f'{b\"\" \"\"}'",
|
"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',
|
||||||
'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 += [
|
FAILING_EXAMPLES += [
|
||||||
"f'{1=!b}'",
|
"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',
|
||||||
|
]
|
||||||
|
|||||||
@@ -4,15 +4,20 @@ from parso import utils
|
|||||||
|
|
||||||
|
|
||||||
def test_load_inexisting_grammar():
|
def test_load_inexisting_grammar():
|
||||||
# This version shouldn't be out for a while, but if we ever do, wow!
|
# We support future grammars assuming future compatibility,
|
||||||
with pytest.raises(NotImplementedError):
|
# but we don't know how to parse old grammars.
|
||||||
load_grammar(version='15.8')
|
|
||||||
# The same is true for very old grammars (even though this is probably not
|
|
||||||
# going to be an issue.
|
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
load_grammar(version='1.5')
|
load_grammar(version='1.5')
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_grammar_uses_older_syntax():
|
||||||
|
load_grammar(version='4.0')
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_grammar_doesnt_warn(each_version):
|
||||||
|
load_grammar(version=each_version)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(('string', 'result'), [
|
@pytest.mark.parametrize(('string', 'result'), [
|
||||||
('2', (2, 7)), ('3', (3, 6)), ('1.1', (1, 1)), ('1.1.1', (1, 1)), ('300.1.31', (300, 1))
|
('2', (2, 7)), ('3', (3, 6)), ('1.1', (1, 1)), ('1.1.1', (1, 1)), ('300.1.31', (300, 1))
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -118,25 +118,57 @@ def _get_actual_exception(code):
|
|||||||
assert False, "The piece of code should raise an exception."
|
assert False, "The piece of code should raise an exception."
|
||||||
|
|
||||||
# SyntaxError
|
# 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",
|
return [wanted, "SyntaxError: can't assign to keyword",
|
||||||
'SyntaxError: cannot assign to __debug__'], line_nr
|
'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':
|
elif wanted == 'SyntaxError: f-string expression part cannot include a backslash':
|
||||||
return [
|
return [
|
||||||
wanted,
|
wanted,
|
||||||
"SyntaxError: EOL while scanning string literal",
|
"SyntaxError: EOL while scanning string literal",
|
||||||
"SyntaxError: unexpected character after line continuation character",
|
"SyntaxError: unexpected character after line continuation character",
|
||||||
], line_nr
|
], 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:
|
elif "Maybe you meant '==' instead of '='?" in wanted:
|
||||||
wanted = wanted.removesuffix(" here. Maybe you meant '==' instead of '='?")
|
wanted = wanted.removesuffix(" here. Maybe you meant '==' instead of '='?")
|
||||||
elif re.match(
|
elif re.match(
|
||||||
@@ -148,18 +180,28 @@ def _get_actual_exception(code):
|
|||||||
wanted,
|
wanted,
|
||||||
):
|
):
|
||||||
wanted = 'SyntaxError: EOF while scanning triple-quoted string literal'
|
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(
|
elif re.match(
|
||||||
r"IndentationError: expected an indented block after '[^']*' statement on line \d",
|
r"IndentationError: expected an indented block after '[^']*' statement on line \d",
|
||||||
wanted,
|
wanted,
|
||||||
):
|
):
|
||||||
wanted = 'IndentationError: expected an indented block'
|
wanted = 'IndentationError: expected an indented block'
|
||||||
elif wanted == 'SyntaxError: unterminated string literal':
|
# The following two errors are produced for both some f-strings and
|
||||||
wanted = 'SyntaxError: EOL while scanning string literal'
|
# some non-f-strings in Python 3.13:
|
||||||
return [wanted], line_nr
|
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():
|
def test_default_except_error_postition():
|
||||||
|
|||||||
Reference in New Issue
Block a user