From 6279686282a2471d563804b21fa3e5ea0a06251a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:14:18 +0200 Subject: [PATCH 01/22] fix ``SyntaxError`` for invalid type parameters expressions --- Include/internal/pycore_symtable.h | 22 +++++++ Lib/test/test_syntax.py | 100 +++++++++++++++++++++++++++++ Python/symtable.c | 52 ++++++++++++--- 3 files changed, 165 insertions(+), 9 deletions(-) diff --git a/Include/internal/pycore_symtable.h b/Include/internal/pycore_symtable.h index ac6c499c08264e..0d86a4c9c8e87a 100644 --- a/Include/internal/pycore_symtable.h +++ b/Include/internal/pycore_symtable.h @@ -29,6 +29,27 @@ typedef enum _comprehension_type { SetComprehension = 3, GeneratorExpression = 4 } _Py_comprehension_ty; +/* Additional flags that are set temporarily on a symtable object. + * + * Those flags are only used to add some information on the current context. + */ +typedef enum _extra_flags { + // Mutually exclusive flags indicating the kind of type variable + // being processed. + InTypeVar = 1, + InTypeVarTuple = 2, + InParamSpec = (1 << 2), + + // Mutually exclusive flags indicating which component of + // a type parameters block (PEP 695) is being processed. + InTypeParamBound = (1 << 3), + InTypeParamConstraint = (1 << 4), + InTypeParamDefault = (1 << 5), +} _Py_extra_fls; + +#define CLEAR_TYPE_PARAM_KIND_EXTRA_FLAGS ~(InTypeVar | InTypeVarTuple | InParamSpec) +#define CLEAR_TYPE_PARAM_EXTRA_FLAGS ~(InTypeParamBound | InTypeParamConstraint | InTypeParamDefault) + /* source location information */ typedef struct { int lineno; @@ -83,6 +104,7 @@ typedef struct _symtable_entry { PyObject *ste_directives;/* locations of global and nonlocal statements */ PyObject *ste_mangled_names; /* set of names for which mangling should be applied */ _Py_block_ty ste_type; + _Py_extra_fls ste_extra_flags; int ste_nested; /* true if block is nested */ unsigned ste_free : 1; /* true if block has free variables */ unsigned ste_child_free : 1; /* true if a child block has free vars, diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index b978838ea7003f..74ef4f347885fc 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2068,16 +2068,91 @@ def f(x: *b) ... SyntaxError: Type parameter list cannot be empty + >>> def f[T: (x:=3)](): pass + Traceback (most recent call last): + ... + SyntaxError: named expression cannot be used within a TypeVar bound + + >>> def f[T: ((x:= 3), int)](): pass + Traceback (most recent call last): + ... + SyntaxError: named expression cannot be used within a TypeVar constraint + + >>> def f[T=((x:=3))](): pass + Traceback (most recent call last): + ... + SyntaxError: named expression cannot be used within a TypeVar default + + >>> async def f[T: (x:=3)](): pass + Traceback (most recent call last): + ... + SyntaxError: named expression cannot be used within a TypeVar bound + + >>> async def f[T: ((x:= 3), int)](): pass + Traceback (most recent call last): + ... + SyntaxError: named expression cannot be used within a TypeVar constraint + + >>> async def f[T=((x:=3))](): pass + Traceback (most recent call last): + ... + SyntaxError: named expression cannot be used within a TypeVar default + >>> type A[T: (x:=3)] = int Traceback (most recent call last): ... SyntaxError: named expression cannot be used within a TypeVar bound + >>> type A[T: ((x:= 3), int)] = int + Traceback (most recent call last): + ... + SyntaxError: named expression cannot be used within a TypeVar constraint + + >>> type A[T=((x:=3))] = int + Traceback (most recent call last): + ... + SyntaxError: named expression cannot be used within a TypeVar default + + >>> def f[T: (yield)](): pass + Traceback (most recent call last): + ... + SyntaxError: yield expression cannot be used within a TypeVar bound + + >>> def f[T: (int, (yield))](): pass + Traceback (most recent call last): + ... + SyntaxError: yield expression cannot be used within a TypeVar constraint + + >>> def f[T=(yield)](): pass + Traceback (most recent call last): + ... + SyntaxError: yield expression cannot be used within a TypeVar default + + >>> def f[*Ts=(yield)](): pass + Traceback (most recent call last): + ... + SyntaxError: yield expression cannot be used within a TypeVarTuple default + + >>> def f[**Ps=[(yield), int]](): pass + Traceback (most recent call last): + ... + SyntaxError: yield expression cannot be used within a ParamSpec default + >>> type A[T: (yield 3)] = int Traceback (most recent call last): ... SyntaxError: yield expression cannot be used within a TypeVar bound + >>> type A[T: (int, (yield 3))] = int + Traceback (most recent call last): + ... + SyntaxError: yield expression cannot be used within a TypeVar constraint + + >>> type A[T=(yield 3)] = int + Traceback (most recent call last): + ... + SyntaxError: yield expression cannot be used within a TypeVar default + >>> type A[T: (await 3)] = int Traceback (most recent call last): ... @@ -2088,6 +2163,31 @@ def f(x: *b) ... SyntaxError: yield expression cannot be used within a TypeVar bound + >>> class A[T: (yield 3)]: pass + Traceback (most recent call last): + ... + SyntaxError: yield expression cannot be used within a TypeVar bound + + >>> class A[T: (int, (yield 3))]: pass + Traceback (most recent call last): + ... + SyntaxError: yield expression cannot be used within a TypeVar constraint + + >>> class A[T=(yield)]: pass + Traceback (most recent call last): + ... + SyntaxError: yield expression cannot be used within a TypeVar default + + >>> class A[*Ts=(yield)]: pass + Traceback (most recent call last): + ... + SyntaxError: yield expression cannot be used within a TypeVarTuple default + + >>> class A[**Ps=[(yield), int]]: pass + Traceback (most recent call last): + ... + SyntaxError: yield expression cannot be used within a ParamSpec default + >>> type A = (x := 3) Traceback (most recent call last): ... diff --git a/Python/symtable.c b/Python/symtable.c index d8240cdd11f7ea..6b0691e3d26bef 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -58,8 +58,8 @@ #define ANNOTATION_NOT_ALLOWED \ "%s cannot be used within an annotation" -#define TYPEVAR_BOUND_NOT_ALLOWED \ -"%s cannot be used within a TypeVar bound" +#define EXPR_NOT_ALLOWED_IN_TYPE_PARAM_BLOCK \ +"%s cannot be used within %s%s" #define TYPEALIAS_NOT_ALLOWED \ "%s cannot be used within a type alias" @@ -106,6 +106,7 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block, ste->ste_mangled_names = NULL; ste->ste_type = block; + ste->ste_extra_flags = 0; ste->ste_nested = 0; ste->ste_free = 0; ste->ste_varargs = 0; @@ -1339,6 +1340,8 @@ symtable_enter_block(struct symtable *st, identifier name, _Py_block_ty block, */ if (prev) { ste->ste_comp_iter_expr = prev->ste_comp_iter_expr; + /* inherit already set flags */ + ste->ste_extra_flags = prev->ste_extra_flags; } /* No need to inherit ste_mangled_names in classes, where all names * are mangled. */ @@ -2291,12 +2294,18 @@ symtable_visit_expr(struct symtable *st, expr_ty e) } static int -symtable_visit_type_param_bound_or_default(struct symtable *st, expr_ty e, identifier name, void *key) +symtable_visit_type_param_bound_or_default(struct symtable *st, expr_ty e, identifier name, void *key, int is_default) { if (e) { int is_in_class = st->st_cur->ste_can_see_class_scope; if (!symtable_enter_block(st, name, TypeVarBoundBlock, key, LOCATION(e))) return 0; + + st->st_cur->ste_extra_flags &= CLEAR_TYPE_PARAM_EXTRA_FLAGS; + st->st_cur->ste_extra_flags |= (is_default == 1 ? InTypeParamDefault : ( + e->kind == Tuple_kind ? InTypeParamConstraint : InTypeParamBound + )); + st->st_cur->ste_can_see_class_scope = is_in_class; if (is_in_class && !symtable_add_def(st, &_Py_ID(__classdict__), USE, LOCATION(e))) { VISIT_QUIT(st, 0); @@ -2321,6 +2330,9 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) if (!symtable_add_def(st, tp->v.TypeVar.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) VISIT_QUIT(st, 0); + st->st_cur->ste_extra_flags &= CLEAR_TYPE_PARAM_KIND_EXTRA_FLAGS; + st->st_cur->ste_extra_flags |= InTypeVar; + // We must use a different key for the bound and default. The obvious choice would be to // use the .bound and .default_value pointers, but that fails when the expression immediately // inside the bound or default is a comprehension: we would reuse the same key for @@ -2328,11 +2340,11 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) // The only requirement for the key is that it is unique and it matches the logic in // compile.c where the scope is retrieved. if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.bound, tp->v.TypeVar.name, - (void *)tp)) { + (void *)tp, 0)) { VISIT_QUIT(st, 0); } if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.default_value, tp->v.TypeVar.name, - (void *)((uintptr_t)tp + 1))) { + (void *)((uintptr_t)tp + 1), 1)) { VISIT_QUIT(st, 0); } break; @@ -2340,8 +2352,12 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) if (!symtable_add_def(st, tp->v.TypeVarTuple.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) { VISIT_QUIT(st, 0); } + + st->st_cur->ste_extra_flags &= CLEAR_TYPE_PARAM_KIND_EXTRA_FLAGS; + st->st_cur->ste_extra_flags |= InTypeVarTuple; + if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVarTuple.default_value, tp->v.TypeVarTuple.name, - (void *)tp)) { + (void *)tp, 1)) { VISIT_QUIT(st, 0); } break; @@ -2349,8 +2365,12 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) if (!symtable_add_def(st, tp->v.ParamSpec.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) { VISIT_QUIT(st, 0); } + + st->st_cur->ste_extra_flags &= CLEAR_TYPE_PARAM_KIND_EXTRA_FLAGS; + st->st_cur->ste_extra_flags |= InParamSpec; + if (!symtable_visit_type_param_bound_or_default(st, tp->v.ParamSpec.default_value, tp->v.ParamSpec.name, - (void *)tp)) { + (void *)tp, 1)) { VISIT_QUIT(st, 0); } break; @@ -2732,8 +2752,22 @@ symtable_raise_if_annotation_block(struct symtable *st, const char *name, expr_t enum _block_type type = st->st_cur->ste_type; if (type == AnnotationBlock) PyErr_Format(PyExc_SyntaxError, ANNOTATION_NOT_ALLOWED, name); - else if (type == TypeVarBoundBlock) - PyErr_Format(PyExc_SyntaxError, TYPEVAR_BOUND_NOT_ALLOWED, name); + else if (type == TypeVarBoundBlock) { + enum _extra_flags flags = st->st_cur->ste_extra_flags; + PyErr_Format( + PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_PARAM_BLOCK, name, ( + (flags & InTypeVar) ? "a TypeVar" : + (flags & InTypeVarTuple) ? "a TypeVarTuple" : + (flags & InParamSpec) ? "a ParamSpec" : + "a TypeVar, TypeVarTuple or a ParamSpec" + ), ( + (flags & InTypeParamBound) ? " bound" : + (flags & InTypeParamConstraint) ? " constraint" : + (flags & InTypeParamDefault) ? " default" : + "" + ) + ); + } else if (type == TypeAliasBlock) PyErr_Format(PyExc_SyntaxError, TYPEALIAS_NOT_ALLOWED, name); else if (type == TypeParamBlock) From 7075bc6cd7ff0dd8c186ba45d4d759fbe56ad2ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:49:23 +0200 Subject: [PATCH 02/22] blurb --- .../next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst diff --git a/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst b/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst new file mode 100644 index 00000000000000..76f402b72168bc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst @@ -0,0 +1 @@ +Improve :exc:`SyntaxError` messages for :pep:`695` and :pep:`696`. From 74d330ed03c1ba937f7eb97a7d22d9a143b38c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 3 Jun 2024 16:38:19 +0200 Subject: [PATCH 03/22] improve doc & naming --- Include/internal/pycore_symtable.h | 17 ++++++++++------- Python/symtable.c | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Include/internal/pycore_symtable.h b/Include/internal/pycore_symtable.h index 0d86a4c9c8e87a..2c68cb6ae5ee83 100644 --- a/Include/internal/pycore_symtable.h +++ b/Include/internal/pycore_symtable.h @@ -29,26 +29,29 @@ typedef enum _comprehension_type { SetComprehension = 3, GeneratorExpression = 4 } _Py_comprehension_ty; -/* Additional flags that are set temporarily on a symtable object. +/* Additional flags that are set temporarily on a ``_symtable_entry`` object. * * Those flags are only used to add some information on the current context. */ -typedef enum _extra_flags { +typedef enum _extra_flags_e { // Mutually exclusive flags indicating the kind of type variable - // being processed. + // being processed. Use this flag to avoid passing or checking + // twice the kind of a ``type_param_ty`` object. InTypeVar = 1, InTypeVarTuple = 2, InParamSpec = (1 << 2), // Mutually exclusive flags indicating which component of - // a type parameters block (PEP 695) is being processed. + // a type parameters block is being processed. Those flags + // are used in conjunction with 'InTypeVar', 'InTypeVarTuple' + // or 'InParamSpec' but not all combinations are supported. InTypeParamBound = (1 << 3), InTypeParamConstraint = (1 << 4), InTypeParamDefault = (1 << 5), -} _Py_extra_fls; +} _extra_flags_t; #define CLEAR_TYPE_PARAM_KIND_EXTRA_FLAGS ~(InTypeVar | InTypeVarTuple | InParamSpec) -#define CLEAR_TYPE_PARAM_EXTRA_FLAGS ~(InTypeParamBound | InTypeParamConstraint | InTypeParamDefault) +#define CLEAR_TYPE_PARAM_ATTR_EXTRA_FLAGS ~(InTypeParamBound | InTypeParamConstraint | InTypeParamDefault) /* source location information */ typedef struct { @@ -104,7 +107,7 @@ typedef struct _symtable_entry { PyObject *ste_directives;/* locations of global and nonlocal statements */ PyObject *ste_mangled_names; /* set of names for which mangling should be applied */ _Py_block_ty ste_type; - _Py_extra_fls ste_extra_flags; + _extra_flags_t ste_extra_flags; /* extra temporary flags */ int ste_nested; /* true if block is nested */ unsigned ste_free : 1; /* true if block has free variables */ unsigned ste_child_free : 1; /* true if a child block has free vars, diff --git a/Python/symtable.c b/Python/symtable.c index 6b0691e3d26bef..549b73f816bf49 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -2301,7 +2301,7 @@ symtable_visit_type_param_bound_or_default(struct symtable *st, expr_ty e, ident if (!symtable_enter_block(st, name, TypeVarBoundBlock, key, LOCATION(e))) return 0; - st->st_cur->ste_extra_flags &= CLEAR_TYPE_PARAM_EXTRA_FLAGS; + st->st_cur->ste_extra_flags &= CLEAR_TYPE_PARAM_ATTR_EXTRA_FLAGS; st->st_cur->ste_extra_flags |= (is_default == 1 ? InTypeParamDefault : ( e->kind == Tuple_kind ? InTypeParamConstraint : InTypeParamBound )); @@ -2753,7 +2753,7 @@ symtable_raise_if_annotation_block(struct symtable *st, const char *name, expr_t if (type == AnnotationBlock) PyErr_Format(PyExc_SyntaxError, ANNOTATION_NOT_ALLOWED, name); else if (type == TypeVarBoundBlock) { - enum _extra_flags flags = st->st_cur->ste_extra_flags; + _extra_flags_t flags = st->st_cur->ste_extra_flags; PyErr_Format( PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_PARAM_BLOCK, name, ( (flags & InTypeVar) ? "a TypeVar" : From 1ff9183d839fa8016d92db0483f7184e59689e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 3 Jun 2024 16:51:17 +0200 Subject: [PATCH 04/22] visual improvements of some test --- Lib/test/test_syntax.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 74ef4f347885fc..ef96ed271ce55f 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2078,7 +2078,7 @@ def f(x: *b) ... SyntaxError: named expression cannot be used within a TypeVar constraint - >>> def f[T=((x:=3))](): pass + >>> def f[T = ((x:=3))](): pass Traceback (most recent call last): ... SyntaxError: named expression cannot be used within a TypeVar default @@ -2093,7 +2093,7 @@ def f(x: *b) ... SyntaxError: named expression cannot be used within a TypeVar constraint - >>> async def f[T=((x:=3))](): pass + >>> async def f[T = ((x:=3))](): pass Traceback (most recent call last): ... SyntaxError: named expression cannot be used within a TypeVar default @@ -2108,7 +2108,7 @@ def f(x: *b) ... SyntaxError: named expression cannot be used within a TypeVar constraint - >>> type A[T=((x:=3))] = int + >>> type A[T = ((x:=3))] = int Traceback (most recent call last): ... SyntaxError: named expression cannot be used within a TypeVar default @@ -2123,17 +2123,17 @@ def f(x: *b) ... SyntaxError: yield expression cannot be used within a TypeVar constraint - >>> def f[T=(yield)](): pass + >>> def f[T = (yield)](): pass Traceback (most recent call last): ... SyntaxError: yield expression cannot be used within a TypeVar default - >>> def f[*Ts=(yield)](): pass + >>> def f[*Ts = (yield)](): pass Traceback (most recent call last): ... SyntaxError: yield expression cannot be used within a TypeVarTuple default - >>> def f[**Ps=[(yield), int]](): pass + >>> def f[**P = [(yield), int]](): pass Traceback (most recent call last): ... SyntaxError: yield expression cannot be used within a ParamSpec default @@ -2148,7 +2148,7 @@ def f(x: *b) ... SyntaxError: yield expression cannot be used within a TypeVar constraint - >>> type A[T=(yield 3)] = int + >>> type A[T = (yield 3)] = int Traceback (most recent call last): ... SyntaxError: yield expression cannot be used within a TypeVar default @@ -2173,17 +2173,17 @@ def f(x: *b) ... SyntaxError: yield expression cannot be used within a TypeVar constraint - >>> class A[T=(yield)]: pass + >>> class A[T = (yield)]: pass Traceback (most recent call last): ... SyntaxError: yield expression cannot be used within a TypeVar default - >>> class A[*Ts=(yield)]: pass + >>> class A[*Ts = (yield)]: pass Traceback (most recent call last): ... SyntaxError: yield expression cannot be used within a TypeVarTuple default - >>> class A[**Ps=[(yield), int]]: pass + >>> class A[**P = [(yield), int]]: pass Traceback (most recent call last): ... SyntaxError: yield expression cannot be used within a ParamSpec default From fa7f02fbe83cbe530300624430694330408430cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 3 Jun 2024 18:12:52 +0200 Subject: [PATCH 05/22] simplify the flow --- Include/internal/pycore_symtable.h | 26 +------------- Python/symtable.c | 57 +++++++++++------------------- 2 files changed, 21 insertions(+), 62 deletions(-) diff --git a/Include/internal/pycore_symtable.h b/Include/internal/pycore_symtable.h index 2c68cb6ae5ee83..879234fd91fc8c 100644 --- a/Include/internal/pycore_symtable.h +++ b/Include/internal/pycore_symtable.h @@ -29,30 +29,6 @@ typedef enum _comprehension_type { SetComprehension = 3, GeneratorExpression = 4 } _Py_comprehension_ty; -/* Additional flags that are set temporarily on a ``_symtable_entry`` object. - * - * Those flags are only used to add some information on the current context. - */ -typedef enum _extra_flags_e { - // Mutually exclusive flags indicating the kind of type variable - // being processed. Use this flag to avoid passing or checking - // twice the kind of a ``type_param_ty`` object. - InTypeVar = 1, - InTypeVarTuple = 2, - InParamSpec = (1 << 2), - - // Mutually exclusive flags indicating which component of - // a type parameters block is being processed. Those flags - // are used in conjunction with 'InTypeVar', 'InTypeVarTuple' - // or 'InParamSpec' but not all combinations are supported. - InTypeParamBound = (1 << 3), - InTypeParamConstraint = (1 << 4), - InTypeParamDefault = (1 << 5), -} _extra_flags_t; - -#define CLEAR_TYPE_PARAM_KIND_EXTRA_FLAGS ~(InTypeVar | InTypeVarTuple | InParamSpec) -#define CLEAR_TYPE_PARAM_ATTR_EXTRA_FLAGS ~(InTypeParamBound | InTypeParamConstraint | InTypeParamDefault) - /* source location information */ typedef struct { int lineno; @@ -107,7 +83,7 @@ typedef struct _symtable_entry { PyObject *ste_directives;/* locations of global and nonlocal statements */ PyObject *ste_mangled_names; /* set of names for which mangling should be applied */ _Py_block_ty ste_type; - _extra_flags_t ste_extra_flags; /* extra temporary flags */ + const char *ste_description; /* describe what is being done (used in error messages) */ int ste_nested; /* true if block is nested */ unsigned ste_free : 1; /* true if block has free variables */ unsigned ste_child_free : 1; /* true if a child block has free vars, diff --git a/Python/symtable.c b/Python/symtable.c index baef4292d136a4..4611e51d47d9a4 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -59,7 +59,7 @@ "%s cannot be used within an annotation" #define EXPR_NOT_ALLOWED_IN_TYPE_PARAM_BLOCK \ -"%s cannot be used within %s%s" +"%s cannot be used within %s" #define TYPEALIAS_NOT_ALLOWED \ "%s cannot be used within a type alias" @@ -106,7 +106,7 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block, ste->ste_mangled_names = NULL; ste->ste_type = block; - ste->ste_extra_flags = 0; + ste->ste_description = NULL; ste->ste_nested = 0; ste->ste_free = 0; ste->ste_varargs = 0; @@ -1341,8 +1341,6 @@ symtable_enter_block(struct symtable *st, identifier name, _Py_block_ty block, */ if (prev) { ste->ste_comp_iter_expr = prev->ste_comp_iter_expr; - /* inherit already set flags */ - ste->ste_extra_flags = prev->ste_extra_flags; } /* No need to inherit ste_mangled_names in classes, where all names * are mangled. */ @@ -2301,18 +2299,23 @@ symtable_visit_expr(struct symtable *st, expr_ty e) } static int -symtable_visit_type_param_bound_or_default(struct symtable *st, expr_ty e, identifier name, void *key, int is_default) +symtable_visit_type_param_bound_or_default( + struct symtable *st, expr_ty e, identifier name, void *key, + const char *kind_name, int is_default) { if (e) { int is_in_class = st->st_cur->ste_can_see_class_scope; if (!symtable_enter_block(st, name, TypeVarBoundBlock, key, LOCATION(e))) return 0; - st->st_cur->ste_extra_flags &= CLEAR_TYPE_PARAM_ATTR_EXTRA_FLAGS; - st->st_cur->ste_extra_flags |= (is_default == 1 ? InTypeParamDefault : ( - e->kind == Tuple_kind ? InTypeParamConstraint : InTypeParamBound - )); - + PyObject *desc; + desc = PyUnicode_FromFormat("%s %s", kind_name, (is_default == 1 ? "default" : ( + e->kind == Tuple_kind ? "constraint" : "bound" + ))); + if (desc == NULL) { + return 0; + } + st->st_cur->ste_description = PyUnicode_AsUTF8(desc); st->st_cur->ste_can_see_class_scope = is_in_class; if (is_in_class && !symtable_add_def(st, &_Py_ID(__classdict__), USE, LOCATION(e))) { VISIT_QUIT(st, 0); @@ -2337,9 +2340,6 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) if (!symtable_add_def(st, tp->v.TypeVar.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) VISIT_QUIT(st, 0); - st->st_cur->ste_extra_flags &= CLEAR_TYPE_PARAM_KIND_EXTRA_FLAGS; - st->st_cur->ste_extra_flags |= InTypeVar; - // We must use a different key for the bound and default. The obvious choice would be to // use the .bound and .default_value pointers, but that fails when the expression immediately // inside the bound or default is a comprehension: we would reuse the same key for @@ -2347,11 +2347,11 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) // The only requirement for the key is that it is unique and it matches the logic in // compile.c where the scope is retrieved. if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.bound, tp->v.TypeVar.name, - (void *)tp, 0)) { + (void *)tp, "a TypeVar", 0)) { VISIT_QUIT(st, 0); } if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.default_value, tp->v.TypeVar.name, - (void *)((uintptr_t)tp + 1), 1)) { + (void *)((uintptr_t)tp + 1), "a TypeVar", 1)) { VISIT_QUIT(st, 0); } break; @@ -2360,11 +2360,8 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) VISIT_QUIT(st, 0); } - st->st_cur->ste_extra_flags &= CLEAR_TYPE_PARAM_KIND_EXTRA_FLAGS; - st->st_cur->ste_extra_flags |= InTypeVarTuple; - if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVarTuple.default_value, tp->v.TypeVarTuple.name, - (void *)tp, 1)) { + (void *)tp, "a TypeVarTuple", 1)) { VISIT_QUIT(st, 0); } break; @@ -2373,11 +2370,8 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) VISIT_QUIT(st, 0); } - st->st_cur->ste_extra_flags &= CLEAR_TYPE_PARAM_KIND_EXTRA_FLAGS; - st->st_cur->ste_extra_flags |= InParamSpec; - if (!symtable_visit_type_param_bound_or_default(st, tp->v.ParamSpec.default_value, tp->v.ParamSpec.name, - (void *)tp, 1)) { + (void *)tp, "a ParamSpec", 1)) { VISIT_QUIT(st, 0); } break; @@ -2760,20 +2754,9 @@ symtable_raise_if_annotation_block(struct symtable *st, const char *name, expr_t if (type == AnnotationBlock) PyErr_Format(PyExc_SyntaxError, ANNOTATION_NOT_ALLOWED, name); else if (type == TypeVarBoundBlock) { - _extra_flags_t flags = st->st_cur->ste_extra_flags; - PyErr_Format( - PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_PARAM_BLOCK, name, ( - (flags & InTypeVar) ? "a TypeVar" : - (flags & InTypeVarTuple) ? "a TypeVarTuple" : - (flags & InParamSpec) ? "a ParamSpec" : - "a TypeVar, TypeVarTuple or a ParamSpec" - ), ( - (flags & InTypeParamBound) ? " bound" : - (flags & InTypeParamConstraint) ? " constraint" : - (flags & InTypeParamDefault) ? " default" : - "" - ) - ); + const char *description = st->st_cur->ste_description; + assert(description != NULL); + PyErr_Format(PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_PARAM_BLOCK, name, description); } else if (type == TypeAliasBlock) PyErr_Format(PyExc_SyntaxError, TYPEALIAS_NOT_ALLOWED, name); From 7482db6b64a926cff5223ef73f7262347a7176d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:00:13 +0200 Subject: [PATCH 06/22] address review - improve comments - rename 'TypeVarBoundBlock' -> 'TypeVariableBlock' - rename 'TypeParamBlock' -> 'TypeParametersBlock' - rename 'ste_description' -> 'ste_context_info' - only set 'ste_context_info' just before visiting the actual expression --- Include/internal/pycore_symtable.h | 30 +++++++++--- Modules/symtablemodule.c | 4 +- Python/symtable.c | 79 ++++++++++++++++++------------ 3 files changed, 73 insertions(+), 40 deletions(-) diff --git a/Include/internal/pycore_symtable.h b/Include/internal/pycore_symtable.h index 879234fd91fc8c..c00c7cec1df8c7 100644 --- a/Include/internal/pycore_symtable.h +++ b/Include/internal/pycore_symtable.h @@ -15,11 +15,21 @@ typedef enum _block_type { // Used for annotations if 'from __future__ import annotations' is active. // Annotation blocks cannot bind names and are not evaluated. AnnotationBlock, - // Used for generics and type aliases. These work mostly like functions - // (see PEP 695 for details). The three different blocks function identically; - // they are different enum entries only so that error messages can be more - // precise. - TypeVarBoundBlock, TypeAliasBlock, TypeParamBlock + + // The following blocks are used for generics and type aliases. These work + // mostly like functions (see PEP 695 for details). The three different + // blocks function identically; they are different enum entries only so + // that error messages can be more precise. + + // The block to enter when processing a "type" (PEP 695) construction, + // e.g., "type MyGeneric[T] = list[T]". + TypeAliasBlock, + // The block to enter when processing a "generic" (PEP 695) object, + // e.g., "def foo[T](): pass" or "class A[T]: pass". + TypeParametersBlock, + // The block to enter when processing a single "type variable" in the + // formal sense, i.e., a TypeVar, a TypeVarTuple or a ParamSpec object. + TypeVariableBlock, } _Py_block_ty; typedef enum _comprehension_type { @@ -82,8 +92,16 @@ typedef struct _symtable_entry { PyObject *ste_children; /* list of child blocks */ PyObject *ste_directives;/* locations of global and nonlocal statements */ PyObject *ste_mangled_names; /* set of names for which mangling should be applied */ + _Py_block_ty ste_type; - const char *ste_description; /* describe what is being done (used in error messages) */ + // Optional string set by symtable.c and used when reporting errors. + // The content of that string is a description of the current "context". + // + // For instance, if we are processing the default value of the type + // variable "T" in "def foo[T = int](): pass", `ste_scope_info` is + // set to "a TypeVar default". + const char *ste_scope_info; + int ste_nested; /* true if block is nested */ unsigned ste_free : 1; /* true if block has free variables */ unsigned ste_child_free : 1; /* true if a child block has free vars, diff --git a/Modules/symtablemodule.c b/Modules/symtablemodule.c index 63c4dd4225298d..ea76ef73eb8c87 100644 --- a/Modules/symtablemodule.c +++ b/Modules/symtablemodule.c @@ -90,11 +90,11 @@ symtable_init_constants(PyObject *m) return -1; if (PyModule_AddIntConstant(m, "TYPE_ANNOTATION", AnnotationBlock) < 0) return -1; - if (PyModule_AddIntConstant(m, "TYPE_TYPE_VAR_BOUND", TypeVarBoundBlock) < 0) + if (PyModule_AddIntConstant(m, "TYPE_TYPE_VAR_BOUND", TypeVariableBlock) < 0) return -1; if (PyModule_AddIntConstant(m, "TYPE_TYPE_ALIAS", TypeAliasBlock) < 0) return -1; - if (PyModule_AddIntConstant(m, "TYPE_TYPE_PARAM", TypeParamBlock) < 0) + if (PyModule_AddIntConstant(m, "TYPE_TYPE_PARAM", TypeParametersBlock) < 0) return -1; if (PyModule_AddIntMacro(m, LOCAL) < 0) return -1; diff --git a/Python/symtable.c b/Python/symtable.c index 4611e51d47d9a4..b7adeb8eea4662 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -58,13 +58,13 @@ #define ANNOTATION_NOT_ALLOWED \ "%s cannot be used within an annotation" -#define EXPR_NOT_ALLOWED_IN_TYPE_PARAM_BLOCK \ +#define EXPR_NOT_ALLOWED_IN_TYPE_VARIABLE \ "%s cannot be used within %s" -#define TYPEALIAS_NOT_ALLOWED \ +#define EXPR_NOT_ALLOWED_IN_TYPE_ALIAS \ "%s cannot be used within a type alias" -#define TYPEPARAM_NOT_ALLOWED \ +#define EXPR_NOT_ALLOWED_IN_TYPE_PARAMETERS \ "%s cannot be used within the definition of a generic" #define DUPLICATE_TYPE_PARAM \ @@ -106,7 +106,8 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block, ste->ste_mangled_names = NULL; ste->ste_type = block; - ste->ste_description = NULL; + ste->ste_scope_info = NULL; + ste->ste_nested = 0; ste->ste_free = 0; ste->ste_varargs = 0; @@ -266,9 +267,9 @@ static void _dump_symtable(PySTEntryObject* ste, PyObject* prefix) case ClassBlock: blocktype = "ClassBlock"; break; case ModuleBlock: blocktype = "ModuleBlock"; break; case AnnotationBlock: blocktype = "AnnotationBlock"; break; - case TypeVarBoundBlock: blocktype = "TypeVarBoundBlock"; break; + case TypeVariableBlock: blocktype = "TypeVariableBlock"; break; case TypeAliasBlock: blocktype = "TypeAliasBlock"; break; - case TypeParamBlock: blocktype = "TypeParamBlock"; break; + case TypeParametersBlock: blocktype = "TypeParametersBlock"; break; } const char *comptype = ""; switch (ste->ste_comprehension) { @@ -526,9 +527,9 @@ int _PyST_IsFunctionLike(PySTEntryObject *ste) { return ste->ste_type == FunctionBlock - || ste->ste_type == TypeVarBoundBlock + || ste->ste_type == TypeVariableBlock || ste->ste_type == TypeAliasBlock - || ste->ste_type == TypeParamBlock; + || ste->ste_type == TypeParametersBlock; } static int @@ -1499,7 +1500,7 @@ symtable_enter_type_param_block(struct symtable *st, identifier name, int end_lineno, int end_col_offset) { _Py_block_ty current_type = st->st_cur->ste_type; - if(!symtable_enter_block(st, name, TypeParamBlock, ast, lineno, + if(!symtable_enter_block(st, name, TypeParametersBlock, ast, lineno, col_offset, end_lineno, end_col_offset)) { return 0; } @@ -2080,20 +2081,20 @@ symtable_extend_namedexpr_scope(struct symtable *st, expr_ty e) } /* Disallow usage in ClassBlock and type scopes */ if (ste->ste_type == ClassBlock || - ste->ste_type == TypeParamBlock || + ste->ste_type == TypeParametersBlock || ste->ste_type == TypeAliasBlock || - ste->ste_type == TypeVarBoundBlock) { + ste->ste_type == TypeVariableBlock) { switch (ste->ste_type) { case ClassBlock: PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_CLASS); break; - case TypeParamBlock: + case TypeParametersBlock: PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_TYPEPARAM); break; case TypeAliasBlock: PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_TYPEALIAS); break; - case TypeVarBoundBlock: + case TypeVariableBlock: PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_TYPEVAR_BOUND); break; default: @@ -2301,25 +2302,33 @@ symtable_visit_expr(struct symtable *st, expr_ty e) static int symtable_visit_type_param_bound_or_default( struct symtable *st, expr_ty e, identifier name, void *key, - const char *kind_name, int is_default) + const char *tp_kind_name, int is_default) { if (e) { int is_in_class = st->st_cur->ste_can_see_class_scope; - if (!symtable_enter_block(st, name, TypeVarBoundBlock, key, LOCATION(e))) + if (!symtable_enter_block(st, name, TypeVariableBlock, key, LOCATION(e))) return 0; - PyObject *desc; - desc = PyUnicode_FromFormat("%s %s", kind_name, (is_default == 1 ? "default" : ( - e->kind == Tuple_kind ? "constraint" : "bound" - ))); - if (desc == NULL) { - return 0; - } - st->st_cur->ste_description = PyUnicode_AsUTF8(desc); st->st_cur->ste_can_see_class_scope = is_in_class; if (is_in_class && !symtable_add_def(st, &_Py_ID(__classdict__), USE, LOCATION(e))) { VISIT_QUIT(st, 0); } + + PyObject *scope_info; + scope_info = PyUnicode_FromFormat("%s %s", tp_kind_name, + (is_default == 1) ? "default" : + (e->kind == Tuple_kind) ? "constraint" : + "bound" + ); + if (scope_info == NULL) + return 0; + + const char *ste_scope_info; + ste_scope_info = PyUnicode_AsUTF8(scope_info); + if (ste_scope_info == NULL) + return 0; + st->st_cur->ste_scope_info = ste_scope_info; + VISIT(st, expr, e); if (!symtable_exit_block(st)) return 0; @@ -2753,15 +2762,21 @@ symtable_raise_if_annotation_block(struct symtable *st, const char *name, expr_t enum _block_type type = st->st_cur->ste_type; if (type == AnnotationBlock) PyErr_Format(PyExc_SyntaxError, ANNOTATION_NOT_ALLOWED, name); - else if (type == TypeVarBoundBlock) { - const char *description = st->st_cur->ste_description; - assert(description != NULL); - PyErr_Format(PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_PARAM_BLOCK, name, description); - } - else if (type == TypeAliasBlock) - PyErr_Format(PyExc_SyntaxError, TYPEALIAS_NOT_ALLOWED, name); - else if (type == TypeParamBlock) - PyErr_Format(PyExc_SyntaxError, TYPEPARAM_NOT_ALLOWED, name); + else if (type == TypeVariableBlock) { + const char *info = st->st_cur->ste_scope_info; + assert(info != NULL); // e.g., info == "a ParamSpec default" + PyErr_Format(PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_VARIABLE, name, info); + } + else if (type == TypeAliasBlock) { + // for now, we do not have any extra information + assert(st->st_cur->ste_scope_info == NULL); + PyErr_Format(PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_ALIAS, name); + } + else if (type == TypeParametersBlock) { + // for now, we do not have any extra information + assert(st->st_cur->ste_scope_info == NULL); + PyErr_Format(PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_PARAMETERS, name); + } else return 1; From 2edf665e3a6b80a5f6c94da6765640608183c6d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:11:25 +0200 Subject: [PATCH 07/22] address review - rename '_symtable.TYPE_TYPE_VAR_BOUND' to '_symtable.TYPE_TYPE_VARIABLE' - rename '_symtable.TYPE_TYPE_PARAM' to '_symtable.TYPE_TYPE_PARAMETERS' - add string enumeration for symbol table type --- Lib/symtable.py | 33 ++++++++++++++++++++++----------- Lib/test/test_symtable.py | 12 +++++++----- Modules/symtablemodule.c | 4 ++-- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/Lib/symtable.py b/Lib/symtable.py index ba2f0dafcd0063..a6df78f458a381 100644 --- a/Lib/symtable.py +++ b/Lib/symtable.py @@ -10,6 +10,7 @@ ) import weakref +from enum import StrEnum __all__ = ["symtable", "SymbolTable", "Class", "Function", "Symbol"] @@ -43,6 +44,16 @@ def __call__(self, table, filename): _newSymbolTable = SymbolTableFactory() +class SymbolTableType(StrEnum): + MODULE = "module" + FUNCTION = "function" + CLASS = "class" + ANNOTATION = "annotation" + TYPE_VARIABLE = "type variable" + TYPE_ALIAS = "type alias" + TYPE_PARAMETERS = "type parameters" + + class SymbolTable: def __init__(self, raw_table, filename): @@ -66,23 +77,23 @@ def __repr__(self): def get_type(self): """Return the type of the symbol table. - The values returned are 'class', 'module', 'function', - 'annotation', 'TypeVar bound', 'type alias', and 'type parameter'. + The values returned are one of the values in + the ``SymbolTableType`` enumeration. """ if self._table.type == _symtable.TYPE_MODULE: - return "module" + return SymbolTableType.MODULE.value if self._table.type == _symtable.TYPE_FUNCTION: - return "function" + return SymbolTableType.FUNCTION.value if self._table.type == _symtable.TYPE_CLASS: - return "class" + return SymbolTableType.CLASS.value if self._table.type == _symtable.TYPE_ANNOTATION: - return "annotation" - if self._table.type == _symtable.TYPE_TYPE_VAR_BOUND: - return "TypeVar bound" + return SymbolTableType.ANNOTATION.value + if self._table.type == _symtable.TYPE_TYPE_VARIABLE: + return SymbolTableType.TYPE_VARIABLE.value if self._table.type == _symtable.TYPE_TYPE_ALIAS: - return "type alias" - if self._table.type == _symtable.TYPE_TYPE_PARAM: - return "type parameter" + return SymbolTableType.TYPE_ALIAS.value + if self._table.type == _symtable.TYPE_TYPE_PARAMETERS: + return SymbolTableType.TYPE_PARAMETERS.value assert False, f"unexpected type: {self._table.type}" def get_id(self): diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index ef2a228b15ed4e..9bd659ae0710ca 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -49,7 +49,7 @@ def namespace_test(): pass def generic_spam[T](a): pass -class GenericMine[T: int]: +class GenericMine[T: int, U: (int, str) = int]: pass """ @@ -78,6 +78,7 @@ class SymtableTest(unittest.TestCase): GenericMine = find_block(top, "GenericMine") GenericMine_inner = find_block(GenericMine, "GenericMine") T = find_block(GenericMine, "T") + U = find_block(GenericMine, "U") def test_type(self): self.assertEqual(self.top.get_type(), "module") @@ -87,13 +88,14 @@ def test_type(self): self.assertEqual(self.internal.get_type(), "function") self.assertEqual(self.foo.get_type(), "function") self.assertEqual(self.Alias.get_type(), "type alias") - self.assertEqual(self.GenericAlias.get_type(), "type parameter") + self.assertEqual(self.GenericAlias.get_type(), "type parameters") self.assertEqual(self.GenericAlias_inner.get_type(), "type alias") - self.assertEqual(self.generic_spam.get_type(), "type parameter") + self.assertEqual(self.generic_spam.get_type(), "type parameters") self.assertEqual(self.generic_spam_inner.get_type(), "function") - self.assertEqual(self.GenericMine.get_type(), "type parameter") + self.assertEqual(self.GenericMine.get_type(), "type parameters") self.assertEqual(self.GenericMine_inner.get_type(), "class") - self.assertEqual(self.T.get_type(), "TypeVar bound") + self.assertEqual(self.T.get_type(), "type variable") + self.assertEqual(self.U.get_type(), "type variable") def test_id(self): self.assertGreater(self.top.get_id(), 0) diff --git a/Modules/symtablemodule.c b/Modules/symtablemodule.c index ea76ef73eb8c87..f6b51a0295058a 100644 --- a/Modules/symtablemodule.c +++ b/Modules/symtablemodule.c @@ -90,11 +90,11 @@ symtable_init_constants(PyObject *m) return -1; if (PyModule_AddIntConstant(m, "TYPE_ANNOTATION", AnnotationBlock) < 0) return -1; - if (PyModule_AddIntConstant(m, "TYPE_TYPE_VAR_BOUND", TypeVariableBlock) < 0) + if (PyModule_AddIntConstant(m, "TYPE_TYPE_VARIABLE", TypeVariableBlock) < 0) return -1; if (PyModule_AddIntConstant(m, "TYPE_TYPE_ALIAS", TypeAliasBlock) < 0) return -1; - if (PyModule_AddIntConstant(m, "TYPE_TYPE_PARAM", TypeParametersBlock) < 0) + if (PyModule_AddIntConstant(m, "TYPE_TYPE_PARAMETERS", TypeParametersBlock) < 0) return -1; if (PyModule_AddIntMacro(m, LOCAL) < 0) return -1; From 845c7a657cf9e3c9cb78519a0d022f7f9b607c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:12:44 +0200 Subject: [PATCH 08/22] use the same declaration order across files --- Lib/symtable.py | 6 +++--- Modules/symtablemodule.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/symtable.py b/Lib/symtable.py index a6df78f458a381..ae2813ce799a10 100644 --- a/Lib/symtable.py +++ b/Lib/symtable.py @@ -49,9 +49,9 @@ class SymbolTableType(StrEnum): FUNCTION = "function" CLASS = "class" ANNOTATION = "annotation" - TYPE_VARIABLE = "type variable" TYPE_ALIAS = "type alias" TYPE_PARAMETERS = "type parameters" + TYPE_VARIABLE = "type variable" class SymbolTable: @@ -88,12 +88,12 @@ def get_type(self): return SymbolTableType.CLASS.value if self._table.type == _symtable.TYPE_ANNOTATION: return SymbolTableType.ANNOTATION.value - if self._table.type == _symtable.TYPE_TYPE_VARIABLE: - return SymbolTableType.TYPE_VARIABLE.value if self._table.type == _symtable.TYPE_TYPE_ALIAS: return SymbolTableType.TYPE_ALIAS.value if self._table.type == _symtable.TYPE_TYPE_PARAMETERS: return SymbolTableType.TYPE_PARAMETERS.value + if self._table.type == _symtable.TYPE_TYPE_VARIABLE: + return SymbolTableType.TYPE_VARIABLE.value assert False, f"unexpected type: {self._table.type}" def get_id(self): diff --git a/Modules/symtablemodule.c b/Modules/symtablemodule.c index f6b51a0295058a..d2dea281b47727 100644 --- a/Modules/symtablemodule.c +++ b/Modules/symtablemodule.c @@ -90,12 +90,12 @@ symtable_init_constants(PyObject *m) return -1; if (PyModule_AddIntConstant(m, "TYPE_ANNOTATION", AnnotationBlock) < 0) return -1; - if (PyModule_AddIntConstant(m, "TYPE_TYPE_VARIABLE", TypeVariableBlock) < 0) - return -1; if (PyModule_AddIntConstant(m, "TYPE_TYPE_ALIAS", TypeAliasBlock) < 0) return -1; if (PyModule_AddIntConstant(m, "TYPE_TYPE_PARAMETERS", TypeParametersBlock) < 0) return -1; + if (PyModule_AddIntConstant(m, "TYPE_TYPE_VARIABLE", TypeVariableBlock) < 0) + return -1; if (PyModule_AddIntMacro(m, LOCAL) < 0) return -1; if (PyModule_AddIntMacro(m, GLOBAL_EXPLICIT) < 0) return -1; From bbfdcb73bc80d46367a78a0db20c288771d74dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:35:16 +0200 Subject: [PATCH 09/22] Use enumeration members instead of `.value`. --- Lib/symtable.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Lib/symtable.py b/Lib/symtable.py index ae2813ce799a10..ecd60ef3dcc96a 100644 --- a/Lib/symtable.py +++ b/Lib/symtable.py @@ -81,19 +81,19 @@ def get_type(self): the ``SymbolTableType`` enumeration. """ if self._table.type == _symtable.TYPE_MODULE: - return SymbolTableType.MODULE.value + return SymbolTableType.MODULE if self._table.type == _symtable.TYPE_FUNCTION: - return SymbolTableType.FUNCTION.value + return SymbolTableType.FUNCTION if self._table.type == _symtable.TYPE_CLASS: - return SymbolTableType.CLASS.value + return SymbolTableType.CLASS if self._table.type == _symtable.TYPE_ANNOTATION: - return SymbolTableType.ANNOTATION.value + return SymbolTableType.ANNOTATION if self._table.type == _symtable.TYPE_TYPE_ALIAS: - return SymbolTableType.TYPE_ALIAS.value + return SymbolTableType.TYPE_ALIAS if self._table.type == _symtable.TYPE_TYPE_PARAMETERS: - return SymbolTableType.TYPE_PARAMETERS.value + return SymbolTableType.TYPE_PARAMETERS if self._table.type == _symtable.TYPE_TYPE_VARIABLE: - return SymbolTableType.TYPE_VARIABLE.value + return SymbolTableType.TYPE_VARIABLE assert False, f"unexpected type: {self._table.type}" def get_id(self): From f1208c70b98f96fbe7b4d62342dcbf75bc69a94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:58:01 +0200 Subject: [PATCH 10/22] export `SymbolTableType` enumeration --- Lib/symtable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/symtable.py b/Lib/symtable.py index ecd60ef3dcc96a..abce66d31eb9e5 100644 --- a/Lib/symtable.py +++ b/Lib/symtable.py @@ -12,7 +12,7 @@ import weakref from enum import StrEnum -__all__ = ["symtable", "SymbolTable", "Class", "Function", "Symbol"] +__all__ = ["symtable", "SymbolTableType", "SymbolTable", "Class", "Function", "Symbol"] def symtable(code, filename, compile_type): """ Return the toplevel *SymbolTable* for the source code. From e533e4dbe073657daf427dad83de3d4cb4e565dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:58:07 +0200 Subject: [PATCH 11/22] update NEWS --- .../2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst b/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst index 76f402b72168bc..4140b2a413ffde 100644 --- a/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst +++ b/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst @@ -1 +1,7 @@ -Improve :exc:`SyntaxError` messages for :pep:`695` and :pep:`696`. +- Improve :exc:`SyntaxError` messages for invalid expressions in a type + parameters bound, a type parameter constraint tuple or a default type + parameter. + Patch by Bénédikt Tran. +- Add the :class:`symtable.SymbolTableType` enumeration to represent the + possible outputs of the :class:`symtable.SymbolTable.get_type` method. + Patch by Bénédikt Tran. \ No newline at end of file From 4cf0bf88f7be35418728d245677fbb8fec169263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:58:55 +0200 Subject: [PATCH 12/22] simplify `ste_scope_info` creation --- Python/symtable.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/Python/symtable.c b/Python/symtable.c index b7adeb8eea4662..ba89ffe1cfcf7c 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -2301,8 +2301,8 @@ symtable_visit_expr(struct symtable *st, expr_ty e) static int symtable_visit_type_param_bound_or_default( - struct symtable *st, expr_ty e, identifier name, void *key, - const char *tp_kind_name, int is_default) + struct symtable *st, expr_ty e, identifier name, + void *key, const char *ste_scope_info) { if (e) { int is_in_class = st->st_cur->ste_can_see_class_scope; @@ -2314,24 +2314,13 @@ symtable_visit_type_param_bound_or_default( VISIT_QUIT(st, 0); } - PyObject *scope_info; - scope_info = PyUnicode_FromFormat("%s %s", tp_kind_name, - (is_default == 1) ? "default" : - (e->kind == Tuple_kind) ? "constraint" : - "bound" - ); - if (scope_info == NULL) - return 0; - - const char *ste_scope_info; - ste_scope_info = PyUnicode_AsUTF8(scope_info); - if (ste_scope_info == NULL) - return 0; + assert(ste_scope_info != NULL); st->st_cur->ste_scope_info = ste_scope_info; - VISIT(st, expr, e); - if (!symtable_exit_block(st)) + + if (!symtable_exit_block(st)) { return 0; + } } return 1; } @@ -2349,6 +2338,12 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) if (!symtable_add_def(st, tp->v.TypeVar.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) VISIT_QUIT(st, 0); + const char *ste_scope_info = NULL; + const expr_ty bound = tp->v.TypeVar.bound; + if (bound != NULL) { + ste_scope_info = bound->kind == Tuple_kind ? "a TypeVar constraint" : "a TypeVar bound"; + } + // We must use a different key for the bound and default. The obvious choice would be to // use the .bound and .default_value pointers, but that fails when the expression immediately // inside the bound or default is a comprehension: we would reuse the same key for @@ -2356,11 +2351,13 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) // The only requirement for the key is that it is unique and it matches the logic in // compile.c where the scope is retrieved. if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.bound, tp->v.TypeVar.name, - (void *)tp, "a TypeVar", 0)) { + (void *)tp, ste_scope_info)) { VISIT_QUIT(st, 0); } + + ste_scope_info = "a TypeVar default"; if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.default_value, tp->v.TypeVar.name, - (void *)((uintptr_t)tp + 1), "a TypeVar", 1)) { + (void *)((uintptr_t)tp + 1), ste_scope_info)) { VISIT_QUIT(st, 0); } break; @@ -2370,7 +2367,7 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) } if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVarTuple.default_value, tp->v.TypeVarTuple.name, - (void *)tp, "a TypeVarTuple", 1)) { + (void *)tp, "a TypeVarTuple default")) { VISIT_QUIT(st, 0); } break; @@ -2380,7 +2377,7 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) } if (!symtable_visit_type_param_bound_or_default(st, tp->v.ParamSpec.default_value, tp->v.ParamSpec.name, - (void *)tp, "a ParamSpec", 1)) { + (void *)tp, "a ParamSpec default")) { VISIT_QUIT(st, 0); } break; From 740c8f28d870edaadf285ff32a8b0c37a73f6e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:59:36 +0200 Subject: [PATCH 13/22] improve documentation for `TypeVariableBlock` --- Include/internal/pycore_symtable.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_symtable.h b/Include/internal/pycore_symtable.h index c00c7cec1df8c7..d5ed143f6fc733 100644 --- a/Include/internal/pycore_symtable.h +++ b/Include/internal/pycore_symtable.h @@ -27,8 +27,10 @@ typedef enum _block_type { // The block to enter when processing a "generic" (PEP 695) object, // e.g., "def foo[T](): pass" or "class A[T]: pass". TypeParametersBlock, - // The block to enter when processing a single "type variable" in the - // formal sense, i.e., a TypeVar, a TypeVarTuple or a ParamSpec object. + // The block to enter when processing a formal "type variable" bound, + // constraint tuple or default type of a TypeVar, a TypeVarTuple or + // a ParamSpec object (the latter two do not support a bound or a + // constraint tuple). TypeVariableBlock, } _Py_block_ty; From 3e329df071bb59d72d920dc5fcd701f998cccb28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 5 Jun 2024 10:19:33 +0200 Subject: [PATCH 14/22] update documentation --- Doc/library/symtable.rst | 57 +++++++++++++++++++++++++++--- Include/internal/pycore_symtable.h | 8 ++--- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/Doc/library/symtable.rst b/Doc/library/symtable.rst index e17a33f7feb1ab..40a6c8fdd53ee6 100644 --- a/Doc/library/symtable.rst +++ b/Doc/library/symtable.rst @@ -31,21 +31,70 @@ Generating Symbol Tables Examining Symbol Tables ----------------------- +.. class:: SymbolTableType + + An enumeration indicating the type of a :class:`SymbolTable` object. + + .. attribute:: MODULE + :value: "module" + + Used for the symbol table of a module. + + .. attribute:: FUNCTION + :value: "function" + + Used for the symbol table of a function. + + .. attribute:: CLASS + :value: "class" + + Used for the symbol table of a class. + + The following members refer to different flavors of + :ref:`annotation scopes `. + + .. attribute:: ANNOTATION + :value: "annotation" + + Used for annotations if ``from __future__ import annotations`` is active. + + .. attribute:: TYPE_ALIAS + :value: "type alias" + + Used for the symbol table of :keyword:`type` constructions. + + .. attribute:: TYPE_PARAMETERS + :value: "type parameters" + + Used for the symbol table of :ref:`generic functions ` + or :ref:`generic classes `. + + .. attribute:: TYPE_VARIABLE + :value: "type variable" + + Used for the symbol table of the bound, the constraint tuple or the + default value of a single type variable in the formal sense, i.e., + a TypeVar, a TypeVarTuple or a ParamSpec object (the latter two do + not support a bound or a constraint tuple). + + .. versionadded:: 3.14 + .. class:: SymbolTable A namespace table for a block. The constructor is not public. .. method:: get_type() - Return the type of the symbol table. Possible values are ``'class'``, - ``'module'``, ``'function'``, ``'annotation'``, ``'TypeVar bound'``, - ``'type alias'``, and ``'type parameter'``. The latter four refer to - different flavors of :ref:`annotation scopes `. + Return the type of the symbol table. Possible values are members + of the :class:`SymbolTableType` enumeration. .. versionchanged:: 3.12 Added ``'annotation'``, ``'TypeVar bound'``, ``'type alias'``, and ``'type parameter'`` as possible return values. + .. versionchanged:: 3.14 + Return values are members of the :class:`SymbolTableType` enumeration. + .. method:: get_id() Return the table's identifier. diff --git a/Include/internal/pycore_symtable.h b/Include/internal/pycore_symtable.h index d5ed143f6fc733..90252bf8365443 100644 --- a/Include/internal/pycore_symtable.h +++ b/Include/internal/pycore_symtable.h @@ -27,10 +27,10 @@ typedef enum _block_type { // The block to enter when processing a "generic" (PEP 695) object, // e.g., "def foo[T](): pass" or "class A[T]: pass". TypeParametersBlock, - // The block to enter when processing a formal "type variable" bound, - // constraint tuple or default type of a TypeVar, a TypeVarTuple or - // a ParamSpec object (the latter two do not support a bound or a - // constraint tuple). + // The block to enter when processing the bound, the constraint tuple + // or the default value of a single "type variable" in the formal sense, + // i.e., a TypeVar, a TypeVarTuple or a ParamSpec object (the latter two + // do not support a bound or a constraint tuple). TypeVariableBlock, } _Py_block_ty; From 18bc1f35fbf12796c524cd1e1d6b4ba4bb711157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 5 Jun 2024 10:20:56 +0200 Subject: [PATCH 15/22] fixup --- .../next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst b/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst index 4140b2a413ffde..19c4fc09e95bda 100644 --- a/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst +++ b/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst @@ -4,4 +4,4 @@ Patch by Bénédikt Tran. - Add the :class:`symtable.SymbolTableType` enumeration to represent the possible outputs of the :class:`symtable.SymbolTable.get_type` method. - Patch by Bénédikt Tran. \ No newline at end of file + Patch by Bénédikt Tran. From 2ca6fd99f26121b55fcee13c6ee897fad864d1e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 5 Jun 2024 11:39:27 +0200 Subject: [PATCH 16/22] blurb --- .../2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst | 11 ++++------- .../2024-06-05-11-39-21.gh-issue-119933.ooJXQV.rst | 3 +++ 2 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-06-05-11-39-21.gh-issue-119933.ooJXQV.rst diff --git a/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst b/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst index 19c4fc09e95bda..3efa595f76b087 100644 --- a/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst +++ b/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst @@ -1,7 +1,4 @@ -- Improve :exc:`SyntaxError` messages for invalid expressions in a type - parameters bound, a type parameter constraint tuple or a default type - parameter. - Patch by Bénédikt Tran. -- Add the :class:`symtable.SymbolTableType` enumeration to represent the - possible outputs of the :class:`symtable.SymbolTable.get_type` method. - Patch by Bénédikt Tran. +Improve :exc:`SyntaxError` messages for invalid expressions in a type +parameters bound, a type parameter constraint tuple or a default type +parameter. +Patch by Bénédikt Tran. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2024-06-05-11-39-21.gh-issue-119933.ooJXQV.rst b/Misc/NEWS.d/next/Library/2024-06-05-11-39-21.gh-issue-119933.ooJXQV.rst new file mode 100644 index 00000000000000..475da88914bde3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-05-11-39-21.gh-issue-119933.ooJXQV.rst @@ -0,0 +1,3 @@ +Add the :class:`symtable.SymbolTableType` enumeration to represent the +possible outputs of the :class:`symtable.SymbolTable.get_type` method. Patch +by Bénédikt Tran. From 278d1fc9de5f33e2b13752e1aedf510b9992465c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 5 Jun 2024 11:41:48 +0200 Subject: [PATCH 17/22] fixup --- .../next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst b/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst index 3efa595f76b087..513a0200dcc48a 100644 --- a/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst +++ b/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst @@ -1,4 +1,4 @@ Improve :exc:`SyntaxError` messages for invalid expressions in a type parameters bound, a type parameter constraint tuple or a default type parameter. -Patch by Bénédikt Tran. \ No newline at end of file +Patch by Bénédikt Tran. From 82a37b6946bfa2d923e9244a1b38af62ec33ca13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 13 Jun 2024 09:11:01 +0200 Subject: [PATCH 18/22] Move SyntaxError's NEWS into "Core and Builtins" --- .../2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/{Library => Core and Builtins}/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst (100%) diff --git a/Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst b/Misc/NEWS.d/next/Core and Builtins/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst similarity index 100% rename from Misc/NEWS.d/next/Library/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst rename to Misc/NEWS.d/next/Core and Builtins/2024-06-03-13-48-44.gh-issue-119933.Kc0HG5.rst From cf45f400b04fc9aa7941dd522e3ec98fdcb3aa09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 13 Jun 2024 09:20:31 +0200 Subject: [PATCH 19/22] improve documentation wording --- Doc/library/symtable.rst | 6 +++++- Lib/symtable.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Doc/library/symtable.rst b/Doc/library/symtable.rst index 22a91cceebc17c..167529317265d5 100644 --- a/Doc/library/symtable.rst +++ b/Doc/library/symtable.rst @@ -92,9 +92,13 @@ Examining Symbol Tables Added ``'annotation'``, ``'TypeVar bound'``, ``'type alias'``, and ``'type parameter'`` as possible return values. - .. versionchanged:: 3.14 + .. versionchanged:: 3.13 Return values are members of the :class:`SymbolTableType` enumeration. + The exact values of the returned string may change in the future, + and thus, it is recommended to use :class:`SymbolTableType` members + instead of hard-coded strings. + .. method:: get_id() Return the table's identifier. diff --git a/Lib/symtable.py b/Lib/symtable.py index ccc1e8206757e9..9d591bf2cd6f88 100644 --- a/Lib/symtable.py +++ b/Lib/symtable.py @@ -80,7 +80,7 @@ def __repr__(self): def get_type(self): """Return the type of the symbol table. - The values returned are one of the values in + The value returned is one of the values in the ``SymbolTableType`` enumeration. """ if self._table.type == _symtable.TYPE_MODULE: From e96f6b40a459b1861ca10d7989dd322149644974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 17 Jun 2024 09:25:34 +0200 Subject: [PATCH 20/22] Update Python/symtable.c Co-authored-by: Jelle Zijlstra --- Python/symtable.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Python/symtable.c b/Python/symtable.c index 6254b1c202c285..b595ba1756231f 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -2399,7 +2399,6 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) VISIT_QUIT(st, 0); } - ste_scope_info = "a TypeVar default"; if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.default_value, tp->v.TypeVar.name, (void *)((uintptr_t)tp + 1), ste_scope_info)) { VISIT_QUIT(st, 0); From 705a76f7c226d4fba552a2ddf4afdc387401ba7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 17 Jun 2024 09:25:49 +0200 Subject: [PATCH 21/22] Update Doc/library/symtable.rst Co-authored-by: Jelle Zijlstra --- Doc/library/symtable.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/symtable.rst b/Doc/library/symtable.rst index 167529317265d5..a33263796ee53d 100644 --- a/Doc/library/symtable.rst +++ b/Doc/library/symtable.rst @@ -77,7 +77,7 @@ Examining Symbol Tables a TypeVar, a TypeVarTuple or a ParamSpec object (the latter two do not support a bound or a constraint tuple). - .. versionadded:: 3.14 + .. versionadded:: 3.13 .. class:: SymbolTable From abad81bd66c2a0ebf9213ba9538b31dfea4c6873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 17 Jun 2024 09:28:54 +0200 Subject: [PATCH 22/22] address review --- Python/symtable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/symtable.c b/Python/symtable.c index b595ba1756231f..0e3a0383d9693d 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -2400,7 +2400,7 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) } if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.default_value, tp->v.TypeVar.name, - (void *)((uintptr_t)tp + 1), ste_scope_info)) { + (void *)((uintptr_t)tp + 1), "a TypeVar default")) { VISIT_QUIT(st, 0); } break;