mirror of
https://github.com/davidhalter/parso.git
synced 2026-05-16 21:45:20 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bfe3058441 | |||
| a36a4216a4 | |||
| 5b63e63911 |
@@ -6,6 +6,11 @@ Changelog
|
|||||||
Unreleased
|
Unreleased
|
||||||
++++++++++
|
++++++++++
|
||||||
|
|
||||||
|
0.8.7 (2026-05-02)
|
||||||
|
++++++++++++++++++
|
||||||
|
|
||||||
|
- Add PEP 695 type parameter syntax
|
||||||
|
|
||||||
0.8.6 (2026-02-09)
|
0.8.6 (2026-02-09)
|
||||||
++++++++++++++++++
|
++++++++++++++++++
|
||||||
|
|
||||||
|
|||||||
+12
@@ -145,3 +145,15 @@ def works_ge_py38(each_version):
|
|||||||
def works_ge_py39(each_version):
|
def works_ge_py39(each_version):
|
||||||
version_info = parse_version_string(each_version)
|
version_info = parse_version_string(each_version)
|
||||||
return Checker(each_version, version_info >= (3, 9))
|
return Checker(each_version, version_info >= (3, 9))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def works_ge_py312(each_version):
|
||||||
|
version_info = parse_version_string(each_version)
|
||||||
|
return Checker(each_version, version_info >= (3, 12))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def works_ge_py313(each_version):
|
||||||
|
version_info = parse_version_string(each_version)
|
||||||
|
return Checker(each_version, version_info >= (3, 13))
|
||||||
|
|||||||
+1
-1
@@ -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.6'
|
__version__ = '0.8.7'
|
||||||
|
|
||||||
|
|
||||||
def parse(code=None, **kwargs):
|
def parse(code=None, **kwargs):
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ decorators: decorator+
|
|||||||
decorated: decorators (classdef | funcdef | async_funcdef)
|
decorated: decorators (classdef | funcdef | async_funcdef)
|
||||||
|
|
||||||
async_funcdef: 'async' funcdef
|
async_funcdef: 'async' funcdef
|
||||||
funcdef: 'def' NAME parameters ['->' test] ':' suite
|
funcdef: 'def' NAME [type_params] parameters ['->' test] ':' suite
|
||||||
|
|
||||||
|
type_params: '[' type_param (',' type_param)* [','] ']'
|
||||||
|
type_param: NAME [type_param_bound] | '*' NAME | '**' NAME
|
||||||
|
type_param_bound: ':' test
|
||||||
|
|
||||||
parameters: '(' [typedargslist] ')'
|
parameters: '(' [typedargslist] ')'
|
||||||
typedargslist: (
|
typedargslist: (
|
||||||
@@ -131,7 +135,7 @@ dictorsetmaker: ( ((test ':' test | '**' expr)
|
|||||||
((test [':=' test] | star_expr)
|
((test [':=' test] | star_expr)
|
||||||
(comp_for | (',' (test [':=' test] | star_expr))* [','])) )
|
(comp_for | (',' (test [':=' test] | star_expr))* [','])) )
|
||||||
|
|
||||||
classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
|
classdef: 'class' NAME [type_params] ['(' [arglist] ')'] ':' suite
|
||||||
|
|
||||||
arglist: argument (',' argument)* [',']
|
arglist: argument (',' argument)* [',']
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,12 @@ decorators: decorator+
|
|||||||
decorated: decorators (classdef | funcdef | async_funcdef)
|
decorated: decorators (classdef | funcdef | async_funcdef)
|
||||||
|
|
||||||
async_funcdef: 'async' funcdef
|
async_funcdef: 'async' funcdef
|
||||||
funcdef: 'def' NAME parameters ['->' test] ':' suite
|
funcdef: 'def' NAME [type_params] parameters ['->' test] ':' suite
|
||||||
|
|
||||||
|
type_params: '[' type_param (',' type_param)* [','] ']'
|
||||||
|
type_param: NAME [type_param_bound] [type_param_default] | '*' NAME [type_param_default] | '**' NAME [type_param_default]
|
||||||
|
type_param_bound: ':' test
|
||||||
|
type_param_default: '=' test
|
||||||
|
|
||||||
parameters: '(' [typedargslist] ')'
|
parameters: '(' [typedargslist] ')'
|
||||||
typedargslist: (
|
typedargslist: (
|
||||||
@@ -131,7 +136,7 @@ dictorsetmaker: ( ((test ':' test | '**' expr)
|
|||||||
((test [':=' test] | star_expr)
|
((test [':=' test] | star_expr)
|
||||||
(comp_for | (',' (test [':=' test] | star_expr))* [','])) )
|
(comp_for | (',' (test [':=' test] | star_expr))* [','])) )
|
||||||
|
|
||||||
classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
|
classdef: 'class' NAME [type_params] ['(' [arglist] ')'] ':' suite
|
||||||
|
|
||||||
arglist: argument (',' argument)* [',']
|
arglist: argument (',' argument)* [',']
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,12 @@ decorators: decorator+
|
|||||||
decorated: decorators (classdef | funcdef | async_funcdef)
|
decorated: decorators (classdef | funcdef | async_funcdef)
|
||||||
|
|
||||||
async_funcdef: 'async' funcdef
|
async_funcdef: 'async' funcdef
|
||||||
funcdef: 'def' NAME parameters ['->' test] ':' suite
|
funcdef: 'def' NAME [type_params] parameters ['->' test] ':' suite
|
||||||
|
|
||||||
|
type_params: '[' type_param (',' type_param)* [','] ']'
|
||||||
|
type_param: NAME [type_param_bound] [type_param_default] | '*' NAME [type_param_default] | '**' NAME [type_param_default]
|
||||||
|
type_param_bound: ':' test
|
||||||
|
type_param_default: '=' test
|
||||||
|
|
||||||
parameters: '(' [typedargslist] ')'
|
parameters: '(' [typedargslist] ')'
|
||||||
typedargslist: (
|
typedargslist: (
|
||||||
@@ -131,7 +136,7 @@ dictorsetmaker: ( ((test ':' test | '**' expr)
|
|||||||
((test [':=' test] | star_expr)
|
((test [':=' test] | star_expr)
|
||||||
(comp_for | (',' (test [':=' test] | star_expr))* [','])) )
|
(comp_for | (',' (test [':=' test] | star_expr))* [','])) )
|
||||||
|
|
||||||
classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
|
classdef: 'class' NAME [type_params] ['(' [arglist] ')'] ':' suite
|
||||||
|
|
||||||
arglist: argument (',' argument)* [',']
|
arglist: argument (',' argument)* [',']
|
||||||
|
|
||||||
|
|||||||
+22
-19
@@ -479,13 +479,13 @@ class Class(ClassOrFunc):
|
|||||||
Returns the `arglist` node that defines the super classes. It returns
|
Returns the `arglist` node that defines the super classes. It returns
|
||||||
None if there are no arguments.
|
None if there are no arguments.
|
||||||
"""
|
"""
|
||||||
if self.children[2] != '(': # Has no parentheses
|
for i, child in enumerate(self.children):
|
||||||
return None
|
if child == '(':
|
||||||
else:
|
next_child = self.children[i + 1]
|
||||||
if self.children[3] == ')': # Empty parentheses
|
if next_child == ')':
|
||||||
return None
|
return None
|
||||||
else:
|
return next_child
|
||||||
return self.children[3]
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _create_params(parent, argslist_list):
|
def _create_params(parent, argslist_list):
|
||||||
@@ -552,15 +552,21 @@ class Function(ClassOrFunc):
|
|||||||
|
|
||||||
def __init__(self, children):
|
def __init__(self, children):
|
||||||
super().__init__(children)
|
super().__init__(children)
|
||||||
parameters = self.children[2] # After `def foo`
|
parameters = self._find_parameters()
|
||||||
parameters_children = parameters.children[1:-1]
|
parameters_children = parameters.children[1:-1]
|
||||||
# If input parameters list already has Param objects, keep it as is;
|
|
||||||
# otherwise, convert it to a list of Param objects.
|
|
||||||
if not any(isinstance(child, Param) for child in parameters_children):
|
if not any(isinstance(child, Param) for child in parameters_children):
|
||||||
parameters.children[1:-1] = _create_params(parameters, parameters_children)
|
parameters.children[1:-1] = _create_params(
|
||||||
|
parameters, parameters_children
|
||||||
|
)
|
||||||
|
|
||||||
|
def _find_parameters(self):
|
||||||
|
for child in self.children:
|
||||||
|
if child.type == 'parameters':
|
||||||
|
return child
|
||||||
|
raise Exception("A function should always have parameters")
|
||||||
|
|
||||||
def _get_param_nodes(self):
|
def _get_param_nodes(self):
|
||||||
return self.children[2].children
|
return self._find_parameters().children
|
||||||
|
|
||||||
def get_params(self):
|
def get_params(self):
|
||||||
"""
|
"""
|
||||||
@@ -633,13 +639,10 @@ class Function(ClassOrFunc):
|
|||||||
"""
|
"""
|
||||||
Returns the test node after `->` or `None` if there is no annotation.
|
Returns the test node after `->` or `None` if there is no annotation.
|
||||||
"""
|
"""
|
||||||
try:
|
for i, child in enumerate(self.children):
|
||||||
if self.children[3] == "->":
|
if child == '->':
|
||||||
return self.children[4]
|
return self.children[i + 1]
|
||||||
assert self.children[3] == ":"
|
return None
|
||||||
return None
|
|
||||||
except IndexError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class Lambda(Function):
|
class Lambda(Function):
|
||||||
|
|||||||
@@ -206,3 +206,64 @@ def test_positional_only_arguments(works_ge_py38, param_code):
|
|||||||
)
|
)
|
||||||
def test_decorator_expression(works_ge_py39, expression):
|
def test_decorator_expression(works_ge_py39, expression):
|
||||||
works_ge_py39.parse("@%s\ndef x(): pass" % expression)
|
works_ge_py39.parse("@%s\ndef x(): pass" % expression)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'code', [
|
||||||
|
'class Foo[T]: pass',
|
||||||
|
'class Foo[T: str]: pass',
|
||||||
|
'class Foo[T, U]: pass',
|
||||||
|
'class Foo[T: str, U: int]: pass',
|
||||||
|
'class Foo[T](Base): pass',
|
||||||
|
'class Foo[T: str](Base, Mixin): pass',
|
||||||
|
'class Foo[*Ts]: pass',
|
||||||
|
'class Foo[**P]: pass',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_pep695_generic_class(works_ge_py312, code):
|
||||||
|
works_ge_py312.parse(code)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'code', [
|
||||||
|
'def foo[T](x: T) -> T: pass',
|
||||||
|
'def foo[T: int](x: T) -> T: pass',
|
||||||
|
'def foo[T, U](x: T, y: U): pass',
|
||||||
|
'def foo[*Ts](*args): pass',
|
||||||
|
'def foo[**P](*args): pass',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_pep695_generic_function(works_ge_py312, code):
|
||||||
|
works_ge_py312.parse(code)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pep695_class_get_super_arglist(works_ge_py312):
|
||||||
|
module = works_ge_py312.parse('class Foo[T](Bar, Baz): pass')
|
||||||
|
if module is None:
|
||||||
|
return
|
||||||
|
classdef = module.children[0]
|
||||||
|
arglist = classdef.get_super_arglist()
|
||||||
|
assert arglist is not None
|
||||||
|
assert 'Bar' in arglist.get_code()
|
||||||
|
assert 'Baz' in arglist.get_code()
|
||||||
|
|
||||||
|
|
||||||
|
def test_pep695_class_no_bases(works_ge_py312):
|
||||||
|
module = works_ge_py312.parse('class Foo[T]: pass')
|
||||||
|
if module is None:
|
||||||
|
return
|
||||||
|
classdef = module.children[0]
|
||||||
|
assert classdef.get_super_arglist() is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'code', [
|
||||||
|
'class Foo[T = int]: pass',
|
||||||
|
'class Foo[T: str = "default"]: pass',
|
||||||
|
'class Foo[*Ts = tuple[int, ...]]: pass',
|
||||||
|
'class Foo[**P = None]: pass',
|
||||||
|
'def foo[T = int](x: T) -> T: pass',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_pep696_type_param_defaults(works_ge_py313, code):
|
||||||
|
works_ge_py313.parse(code)
|
||||||
|
|||||||
Reference in New Issue
Block a user