mirror of
https://github.com/davidhalter/parso.git
synced 2026-04-21 07:08:11 +08:00
Merge pull request #237 from darki73/feat/pep695-type-params
PEP 695 type parameter syntax for Python 3.12+
This commit is contained in:
12
conftest.py
12
conftest.py
@@ -145,3 +145,15 @@ def works_ge_py38(each_version):
|
||||
def works_ge_py39(each_version):
|
||||
version_info = parse_version_string(each_version)
|
||||
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))
|
||||
|
||||
@@ -17,7 +17,11 @@ decorators: decorator+
|
||||
decorated: decorators (classdef | 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] ')'
|
||||
typedargslist: (
|
||||
@@ -131,7 +135,7 @@ dictorsetmaker: ( ((test ':' test | '**' expr)
|
||||
((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)* [',']
|
||||
|
||||
|
||||
@@ -17,7 +17,12 @@ decorators: decorator+
|
||||
decorated: decorators (classdef | 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] ')'
|
||||
typedargslist: (
|
||||
@@ -131,7 +136,7 @@ dictorsetmaker: ( ((test ':' test | '**' expr)
|
||||
((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)* [',']
|
||||
|
||||
|
||||
@@ -17,7 +17,12 @@ decorators: decorator+
|
||||
decorated: decorators (classdef | 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] ')'
|
||||
typedargslist: (
|
||||
@@ -131,7 +136,7 @@ dictorsetmaker: ( ((test ':' test | '**' expr)
|
||||
((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)* [',']
|
||||
|
||||
|
||||
@@ -479,13 +479,13 @@ class Class(ClassOrFunc):
|
||||
Returns the `arglist` node that defines the super classes. It returns
|
||||
None if there are no arguments.
|
||||
"""
|
||||
if self.children[2] != '(': # Has no parentheses
|
||||
return None
|
||||
else:
|
||||
if self.children[3] == ')': # Empty parentheses
|
||||
return None
|
||||
else:
|
||||
return self.children[3]
|
||||
for i, child in enumerate(self.children):
|
||||
if child == '(':
|
||||
next_child = self.children[i + 1]
|
||||
if next_child == ')':
|
||||
return None
|
||||
return next_child
|
||||
return None
|
||||
|
||||
|
||||
def _create_params(parent, argslist_list):
|
||||
@@ -552,15 +552,21 @@ class Function(ClassOrFunc):
|
||||
|
||||
def __init__(self, children):
|
||||
super().__init__(children)
|
||||
parameters = self.children[2] # After `def foo`
|
||||
parameters = self._find_parameters()
|
||||
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):
|
||||
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):
|
||||
return self.children[2].children
|
||||
return self._find_parameters().children
|
||||
|
||||
def get_params(self):
|
||||
"""
|
||||
@@ -633,13 +639,10 @@ class Function(ClassOrFunc):
|
||||
"""
|
||||
Returns the test node after `->` or `None` if there is no annotation.
|
||||
"""
|
||||
try:
|
||||
if self.children[3] == "->":
|
||||
return self.children[4]
|
||||
assert self.children[3] == ":"
|
||||
return None
|
||||
except IndexError:
|
||||
return None
|
||||
for i, child in enumerate(self.children):
|
||||
if child == '->':
|
||||
return self.children[i + 1]
|
||||
return None
|
||||
|
||||
|
||||
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):
|
||||
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