Skip to content

Commit

Permalink
pythongh-116126: Grammar changes for PEP 696
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra committed Feb 29, 2024
1 parent 45d8871 commit e7ca092
Show file tree
Hide file tree
Showing 15 changed files with 1,053 additions and 569 deletions.
11 changes: 6 additions & 5 deletions Grammar/python.gram
Original file line number Diff line number Diff line change
Expand Up @@ -644,21 +644,22 @@ type_params[asdl_type_param_seq*]: '[' t=type_param_seq ']' {
type_param_seq[asdl_type_param_seq*]: a[asdl_type_param_seq*]=','.type_param+ [','] { a }

type_param[type_param_ty] (memo):
| a=NAME b=[type_param_bound] { _PyAST_TypeVar(a->v.Name.id, b, EXTRA) }
| '*' a=NAME colon=':' e=expression {
| a=NAME b=[type_param_bound] c=[type_param_default] { _PyAST_TypeVar(a->v.Name.id, b, c, EXTRA) }
| '*' a=NAME [type_param_default] colon=':' e=expression {
RAISE_SYNTAX_ERROR_STARTING_FROM(colon, e->kind == Tuple_kind
? "cannot use constraints with TypeVarTuple"
: "cannot use bound with TypeVarTuple")
}
| '*' a=NAME { _PyAST_TypeVarTuple(a->v.Name.id, EXTRA) }
| '**' a=NAME colon=':' e=expression {
| '*' a=NAME b=[type_param_default] { _PyAST_TypeVarTuple(a->v.Name.id, b, EXTRA) }
| '**' a=NAME [type_param_default] colon=':' e=expression {
RAISE_SYNTAX_ERROR_STARTING_FROM(colon, e->kind == Tuple_kind
? "cannot use constraints with ParamSpec"
: "cannot use bound with ParamSpec")
}
| '**' a=NAME { _PyAST_ParamSpec(a->v.Name.id, EXTRA) }
| '**' a=NAME b=[type_param_default] { _PyAST_ParamSpec(a->v.Name.id, b, EXTRA) }

type_param_bound[expr_ty]: ':' e=expression { e }
type_param_default[expr_ty]: "=" e=expression { e }

# EXPRESSIONS
# -----------
Expand Down
20 changes: 12 additions & 8 deletions Include/internal/pycore_ast.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Include/internal/pycore_ast_state.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Include/internal/pycore_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@
#define INTRINSIC_TYPEVAR_WITH_BOUND 2
#define INTRINSIC_TYPEVAR_WITH_CONSTRAINTS 3
#define INTRINSIC_SET_FUNCTION_TYPE_PARAMS 4
#define INTRINSIC_SET_TYPEPARAM_DEFAULT 5

#define MAX_INTRINSIC_2 4
#define MAX_INTRINSIC_2 5

typedef PyObject *(*intrinsic_func1)(PyThreadState* tstate, PyObject *value);
typedef PyObject *(*intrinsic_func2)(PyThreadState* tstate, PyObject *value1, PyObject *value2);
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_typevarobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ extern PyObject *_Py_make_paramspec(PyThreadState *, PyObject *);
extern PyObject *_Py_make_typevartuple(PyThreadState *, PyObject *);
extern PyObject *_Py_make_typealias(PyThreadState *, PyObject *);
extern PyObject *_Py_subscript_generic(PyThreadState *, PyObject *);
extern PyObject *_Py_set_typeparam_default(PyThreadState *, PyObject *, PyObject *);
extern int _Py_initialize_generic(PyInterpreterState *);
extern void _Py_clear_generic_types(PyInterpreterState *);

Expand Down
60 changes: 60 additions & 0 deletions Lib/test/test_type_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,10 +535,12 @@ class Foo[T: Foo, U: (Foo, Foo)]:
self.assertEqual(type_params[0].__name__, "T")
self.assertIs(type_params[0].__bound__, Foo)
self.assertEqual(type_params[0].__constraints__, ())
self.assertIs(type_params[0].__default__, None)

self.assertEqual(type_params[1].__name__, "U")
self.assertIs(type_params[1].__bound__, None)
self.assertEqual(type_params[1].__constraints__, (Foo, Foo))
self.assertIs(type_params[1].__default__, None)

def test_evaluation_error(self):
class Foo[T: Undefined, U: (Undefined,)]:
Expand All @@ -549,6 +551,8 @@ class Foo[T: Undefined, U: (Undefined,)]:
type_params[0].__bound__
self.assertEqual(type_params[0].__constraints__, ())
self.assertIs(type_params[1].__bound__, None)
self.assertIs(type_params[0].__default__, None)
self.assertIs(type_params[1].__default__, None)
with self.assertRaises(NameError):
type_params[1].__constraints__

Expand Down Expand Up @@ -1102,3 +1106,59 @@ class Inner[U](T):
"""
with self.assertRaises(RuntimeError):
run_code(code)


class DefaultsTest(unittest.TestCase):
def test_defaults_on_func(self):
def func[T=int, *U=float, **V=None]():
pass

T, U, V = func.__type_params__
self.assertIs(T.__default__, int)
self.assertIs(U.__default__, float)
self.assertIs(V.__default__, type(None))

def test_defaults_on_class(self):
class C[T=int, *U=float, **V=None]:
pass

T, U, V = C.__type_params__
self.assertIs(T.__default__, int)
self.assertIs(U.__default__, float)
self.assertIs(V.__default__, type(None))

def test_defaults_on_type_alias(self):
type Alias[T = int, *U = float, **V = None] = int

T, U, V = Alias.__type_params__
self.assertIs(T.__default__, int)
self.assertIs(U.__default__, float)
self.assertIs(V.__default__, type(None))

def test_nondefault_after_default(self):
check_syntax_error(self, "def func[T=int, U](): pass", "non-default type parameter 'U' follows default type parameter")
check_syntax_error(self, "class C[T=int, U]: pass", "non-default type parameter 'U' follows default type parameter")
check_syntax_error(self, "type A[T=int, U] = int", "non-default type parameter 'U' follows default type parameter")

def test_lazy_evaluation(self):
type Alias[T = Undefined, *U = Undefined, **V = Undefined] = int

T, U, V = Alias.__type_params__

with self.assertRaises(NameError):
T.__default__
with self.assertRaises(NameError):
U.__default__
with self.assertRaises(NameError):
V.__default__

Undefined = "defined"
self.assertEqual(T.__default__, "defined")
self.assertEqual(U.__default__, "defined")
self.assertEqual(V.__default__, "defined")

# Now it is cached
Undefined = "redefined"
self.assertEqual(T.__default__, "defined")
self.assertEqual(U.__default__, "defined")
self.assertEqual(V.__default__, "defined")
Loading

0 comments on commit e7ca092

Please sign in to comment.