diff --git a/Include/internal/pycore_compile.h b/Include/internal/pycore_compile.h index c18e04bf67a5df..307aeb38cf0c2d 100644 --- a/Include/internal/pycore_compile.h +++ b/Include/internal/pycore_compile.h @@ -154,6 +154,7 @@ int _PyCompile_ScopeType(struct _PyCompiler *c); int _PyCompile_OptimizationLevel(struct _PyCompiler *c); int _PyCompile_LookupArg(struct _PyCompiler *c, PyCodeObject *co, PyObject *name); PyObject *_PyCompile_Qualname(struct _PyCompiler *c); +PyObject *_PyCompile_PeekQualname(struct _PyCompiler *c, PyObject *name); _PyCompile_CodeUnitMetadata *_PyCompile_Metadata(struct _PyCompiler *c); PyObject *_PyCompile_StaticAttributesAsTuple(struct _PyCompiler *c); diff --git a/Include/internal/pycore_function.h b/Include/internal/pycore_function.h index a30d52d49bdc4d..77ba09003fec05 100644 --- a/Include/internal/pycore_function.h +++ b/Include/internal/pycore_function.h @@ -33,7 +33,7 @@ void _PyFunction_ClearCodeByVersion(uint32_t version); PyFunctionObject *_PyFunction_LookupByVersion(uint32_t version, PyObject **p_code); extern PyObject *_Py_set_function_type_params( - PyThreadState* unused, PyObject *func, PyObject *type_params); + PyThreadState *ts, PyObject *func, PyObject *type_params); /* See pycore_code.h for explanation about what "stateless" means. */ diff --git a/Include/internal/pycore_intrinsics.h b/Include/internal/pycore_intrinsics.h index 39c2a30f6e979d..bc7b78b3ba1dd9 100644 --- a/Include/internal/pycore_intrinsics.h +++ b/Include/internal/pycore_intrinsics.h @@ -29,8 +29,9 @@ #define INTRINSIC_TYPEVAR_WITH_CONSTRAINTS 3 #define INTRINSIC_SET_FUNCTION_TYPE_PARAMS 4 #define INTRINSIC_SET_TYPEPARAM_DEFAULT 5 +#define INTRINSIC_SET_TYPEPARAM_OWNER 6 -#define MAX_INTRINSIC_2 5 +#define MAX_INTRINSIC_2 6 typedef PyObject *(*intrinsic_func1)(PyThreadState* tstate, PyObject *value); typedef PyObject *(*intrinsic_func2)(PyThreadState* tstate, PyObject *value1, PyObject *value2); diff --git a/Include/internal/pycore_typevarobject.h b/Include/internal/pycore_typevarobject.h index 4d7556e68cdaee..ad2b985d7564c2 100644 --- a/Include/internal/pycore_typevarobject.h +++ b/Include/internal/pycore_typevarobject.h @@ -14,6 +14,7 @@ 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 PyObject *_Py_set_typeparam_owner(PyThreadState *, PyObject *, PyObject *); extern int _Py_initialize_generic(PyInterpreterState *); extern void _Py_clear_generic_types(PyInterpreterState *); extern int _Py_typing_type_repr(PyUnicodeWriter *, PyObject *); diff --git a/Lib/test/test_type_params.py b/Lib/test/test_type_params.py index 0f393def827271..f34e84cba5cd4c 100644 --- a/Lib/test/test_type_params.py +++ b/Lib/test/test_type_params.py @@ -1183,11 +1183,15 @@ def func1[X](x: X) -> X: ... def func2[X, Y](x: X | Y) -> X | Y: ... def func3[X, *Y, **Z](x: X, y: tuple[*Y], z: Z) -> X: ... def func4[X: int, Y: (bytes, str)](x: X, y: Y) -> X | Y: ... +def func3b[X, *Y, **Z]() -> X: return Class3[X, Y, Z]() +def func5[Baz](): return Class1[Baz]() class Class1[X]: ... class Class2[X, Y]: ... class Class3[X, *Y, **Z]: ... class Class4[X: int, Y: (bytes, str)]: ... +class Class5: + def meth[Baz](): return Class1[Baz]() class TypeParamsPickleTest(unittest.TestCase): @@ -1240,6 +1244,39 @@ def test_pickling_classes(self): # but class check is good enough: self.assertIsInstance(pickle.loads(pickled), real_class) + def test_pickling_anonymous_typeparams(self): + # see gh-129250 + thing = func5() + pickled = pickle.dumps(thing) + unpickled = pickle.loads(pickled) + self.assertIs(unpickled.__orig_class__, thing.__orig_class__) + self.assertIs(unpickled.__orig_class__.__args__[0], + func5.__type_params__[0]) + + thing = func3b() + pickled = pickle.dumps(thing) + unpickled = pickle.loads(pickled) + self.assertIs(unpickled.__orig_class__, thing.__orig_class__) + self.assertIs(unpickled.__orig_class__.__args__[0], + func3b.__type_params__[0]) + self.assertIs(unpickled.__orig_class__.__args__[1], + func3b.__type_params__[1]) + self.assertIs(unpickled.__orig_class__.__args__[2], + func3b.__type_params__[2]) + + for i in range(3): + thing = Class3.__type_params__[i] + pickled = pickle.dumps(thing) + unpickled = pickle.loads(pickled) + self.assertIs(unpickled, thing) + + thing = Class5.meth() + pickled = pickle.dumps(thing) + unpickled = pickle.loads(pickled) + self.assertIs(unpickled.__orig_class__, thing.__orig_class__) + self.assertIs(unpickled.__orig_class__.__args__[0], + Class5.meth.__type_params__[0]) + class TypeParamsWeakRefTest(unittest.TestCase): def test_weakrefs(self): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-29-15-32-02.gh-issue-129250.ExhmQQ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-29-15-32-02.gh-issue-129250.ExhmQQ.rst new file mode 100644 index 00000000000000..206b0ab249ad23 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-29-15-32-02.gh-issue-129250.ExhmQQ.rst @@ -0,0 +1 @@ +Fix pickling of generic classes instances. diff --git a/Modules/_typingmodule.c b/Modules/_typingmodule.c index e51279c808a2e1..41c00e751bf0f6 100644 --- a/Modules/_typingmodule.c +++ b/Modules/_typingmodule.c @@ -8,7 +8,8 @@ #include "internal/pycore_interp.h" #include "internal/pycore_typevarobject.h" #include "internal/pycore_unionobject.h" // _PyUnion_Type -#include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_runtime.h" // _Py_ID() #include "clinic/_typingmodule.c.h" /*[clinic input] @@ -35,8 +36,34 @@ _typing__idfunc(PyObject *module, PyObject *x) } +/*[clinic input] +_typing._restore_anonymous_typeparam -> object + + owner: object + index: object + / + +Restore previously pickled anonymous type param from object.__type_params__. +[clinic start generated code]*/ + +static PyObject * +_typing__restore_anonymous_typeparam_impl(PyObject *module, PyObject *owner, + PyObject *index) +/*[clinic end generated code: output=00baec27dbf8d2d9 input=2f048db28d8124fb]*/ +{ + PyObject *type_params = PyObject_GetAttr(owner, &_Py_ID(__type_params__)); + if (type_params == NULL) { + return NULL; + } + PyObject *res = PyObject_GetItem(type_params, index); + Py_DECREF(type_params); + return res; +} + + static PyMethodDef typing_methods[] = { _TYPING__IDFUNC_METHODDEF + _TYPING__RESTORE_ANONYMOUS_TYPEPARAM_METHODDEF {NULL, NULL, 0, NULL} }; diff --git a/Modules/clinic/_typingmodule.c.h b/Modules/clinic/_typingmodule.c.h index ea415e67153ed8..b05b82face0298 100644 --- a/Modules/clinic/_typingmodule.c.h +++ b/Modules/clinic/_typingmodule.c.h @@ -2,6 +2,8 @@ preserve [clinic start generated code]*/ +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + PyDoc_STRVAR(_typing__idfunc__doc__, "_idfunc($module, x, /)\n" "--\n" @@ -9,4 +11,35 @@ PyDoc_STRVAR(_typing__idfunc__doc__, #define _TYPING__IDFUNC_METHODDEF \ {"_idfunc", (PyCFunction)_typing__idfunc, METH_O, _typing__idfunc__doc__}, -/*[clinic end generated code: output=e7ea2a3cb7ab301a input=a9049054013a1b77]*/ + +PyDoc_STRVAR(_typing__restore_anonymous_typeparam__doc__, +"_restore_anonymous_typeparam($module, owner, index, /)\n" +"--\n" +"\n" +"Restore previously pickled anonymous type param from object.__type_params__."); + +#define _TYPING__RESTORE_ANONYMOUS_TYPEPARAM_METHODDEF \ + {"_restore_anonymous_typeparam", _PyCFunction_CAST(_typing__restore_anonymous_typeparam), METH_FASTCALL, _typing__restore_anonymous_typeparam__doc__}, + +static PyObject * +_typing__restore_anonymous_typeparam_impl(PyObject *module, PyObject *owner, + PyObject *index); + +static PyObject * +_typing__restore_anonymous_typeparam(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *owner; + PyObject *index; + + if (!_PyArg_CheckPositional("_restore_anonymous_typeparam", nargs, 2, 2)) { + goto exit; + } + owner = args[0]; + index = args[1]; + return_value = _typing__restore_anonymous_typeparam_impl(module, owner, index); + +exit: + return return_value; +} +/*[clinic end generated code: output=ad8652d1dc62a084 input=a9049054013a1b77]*/ diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c index cead6e69af5451..2e4797be365cb7 100644 --- a/Objects/typevarobject.c +++ b/Objects/typevarobject.c @@ -27,6 +27,7 @@ typedef struct { PyObject *evaluate_constraints; PyObject *default_value; PyObject *evaluate_default; + PyObject *owner; bool covariant; bool contravariant; bool infer_variance; @@ -37,6 +38,7 @@ typedef struct { PyObject *name; PyObject *default_value; PyObject *evaluate_default; + PyObject *owner; } typevartupleobject; typedef struct { @@ -45,6 +47,7 @@ typedef struct { PyObject *bound; PyObject *default_value; PyObject *evaluate_default; + PyObject *owner; bool covariant; bool contravariant; bool infer_variance; @@ -464,6 +467,73 @@ unpack_typevartuples(PyObject *params) } } +static PyObject * +typeparam_reduce_anonymous(PyObject *self, PyObject *owner) +{ + assert(PyTuple_CheckExact(owner)); + assert(PyTuple_GET_SIZE(owner) == 3); + PyObject *module_name = PyTuple_GET_ITEM(owner, 0); + PyObject *qualname = PyTuple_GET_ITEM(owner, 1); + PyObject *index = PyTuple_GET_ITEM(owner, 2); + assert(PyUnicode_CheckExact(module_name)); + assert(PyUnicode_CheckExact(qualname)); + assert(PyLong_CheckExact(index)); + PyObject *ret = NULL; + PyObject *typing = NULL; + PyObject *restore_func = NULL; + PyObject *module = NULL; + PyObject *path = NULL; + PyObject *obj = NULL; + PyObject *args = NULL; + + typing = PyImport_ImportModule("_typing"); + if (typing == NULL) { + goto done; + } + restore_func = PyObject_GetAttrString(typing, "_restore_anonymous_typeparam"); + if (restore_func == NULL) { + goto done; + } + + module = PyImport_Import(module_name); + if (module == NULL) { + goto done; + } + path = PyUnicode_Split(qualname, _Py_LATIN1_CHR('.'), -1); + if (path == NULL) { + goto done; + } + if (PyList_GET_SIZE(path) == 0) { + PyErr_SetString(PyExc_ValueError, "invalid typeparam owner qualname"); + goto done; + } + obj = module; + Py_INCREF(obj); + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(path); i++) { + PyObject *parent = obj; + obj = PyObject_GetAttr(parent, PyList_GET_ITEM(path, i)); + Py_DECREF(parent); + if (obj == NULL) { + goto done; + } + } + + args = PyTuple_Pack(2, obj, index); + if (args == NULL) { + goto done; + } + ret = PyTuple_Pack(2, restore_func, args); + +done: + Py_XDECREF(args); + Py_XDECREF(obj); + Py_XDECREF(path); + Py_XDECREF(module); + Py_XDECREF(restore_func); + Py_XDECREF(typing); + return ret; +} + static void typevar_dealloc(PyObject *self) { @@ -479,6 +549,7 @@ typevar_dealloc(PyObject *self) Py_XDECREF(tv->evaluate_constraints); Py_XDECREF(tv->default_value); Py_XDECREF(tv->evaluate_default); + Py_XDECREF(tv->owner); PyObject_ClearManagedDict(self); PyObject_ClearWeakRefs(self); @@ -497,6 +568,7 @@ typevar_traverse(PyObject *self, visitproc visit, void *arg) Py_VISIT(tv->evaluate_constraints); Py_VISIT(tv->default_value); Py_VISIT(tv->evaluate_default); + Py_VISIT(tv->owner); PyObject_VisitManagedDict(self, visit, arg); return 0; } @@ -511,6 +583,7 @@ typevar_clear(PyObject *op) Py_CLEAR(self->evaluate_constraints); Py_CLEAR(self->default_value); Py_CLEAR(self->evaluate_default); + Py_CLEAR(self->owner); PyObject_ClearManagedDict(op); return 0; } @@ -652,6 +725,7 @@ typevar_alloc(PyObject *name, PyObject *bound, PyObject *evaluate_bound, tv->evaluate_constraints = Py_XNewRef(evaluate_constraints); tv->default_value = Py_XNewRef(default_value); tv->evaluate_default = NULL; + tv->owner = NULL; tv->covariant = covariant; tv->contravariant = contravariant; @@ -828,6 +902,10 @@ static PyObject * typevar_reduce_impl(typevarobject *self) /*[clinic end generated code: output=02e5c55d7cf8a08f input=de76bc95f04fb9ff]*/ { + PyObject *owner = (PyObject *)FT_ATOMIC_LOAD_PTR(self->owner); + if (owner != NULL) { + return typeparam_reduce_anonymous((PyObject *)self, owner); + } return Py_NewRef(self->name); } @@ -909,6 +987,8 @@ be explicitly marked covariant or contravariant by passing\n\ ``covariant=True`` or ``contravariant=True``. By default, manually\n\ created type variables are invariant. See PEP 484 and PEP 695 for more\n\ details.\n\ +\n\ +Note that only TypeVars reachable from the global scope can be pickled.\n\ "); static PyType_Slot typevar_slots[] = { @@ -1175,6 +1255,7 @@ paramspec_dealloc(PyObject *self) Py_XDECREF(ps->bound); Py_XDECREF(ps->default_value); Py_XDECREF(ps->evaluate_default); + Py_XDECREF(ps->owner); PyObject_ClearManagedDict(self); PyObject_ClearWeakRefs(self); @@ -1190,6 +1271,7 @@ paramspec_traverse(PyObject *self, visitproc visit, void *arg) Py_VISIT(ps->bound); Py_VISIT(ps->default_value); Py_VISIT(ps->evaluate_default); + Py_VISIT(ps->owner); PyObject_VisitManagedDict(self, visit, arg); return 0; } @@ -1201,6 +1283,7 @@ paramspec_clear(PyObject *op) Py_CLEAR(self->bound); Py_CLEAR(self->default_value); Py_CLEAR(self->evaluate_default); + Py_CLEAR(self->owner); PyObject_ClearManagedDict(op); return 0; } @@ -1293,6 +1376,7 @@ paramspec_alloc(PyObject *name, PyObject *bound, PyObject *default_value, bool c ps->infer_variance = infer_variance; ps->default_value = Py_XNewRef(default_value); ps->evaluate_default = NULL; + ps->owner = NULL; _PyObject_GC_TRACK(ps); if (module != NULL) { if (PyObject_SetAttrString((PyObject *)ps, "__module__", module) < 0) { @@ -1397,6 +1481,10 @@ static PyObject * paramspec_reduce_impl(paramspecobject *self) /*[clinic end generated code: output=b83398674416db27 input=5bf349f0d5dd426c]*/ { + PyObject *owner = (PyObject *)FT_ATOMIC_LOAD_PTR(self->owner); + if (owner != NULL) { + return typeparam_reduce_anonymous((PyObject *)self, owner); + } return Py_NewRef(self->name); } @@ -1482,7 +1570,7 @@ Parameter specification variables can be introspected. e.g.::\n\ >>> P.__name__\n\ 'P'\n\ \n\ -Note that only parameter specification variables defined in the global\n\ +Note that only parameter specification variables reachable from the\n\ scope can be pickled.\n\ "); @@ -1522,6 +1610,7 @@ typevartuple_dealloc(PyObject *self) Py_DECREF(tvt->name); Py_XDECREF(tvt->default_value); Py_XDECREF(tvt->evaluate_default); + Py_XDECREF(tvt->owner); PyObject_ClearManagedDict(self); PyObject_ClearWeakRefs(self); @@ -1570,6 +1659,7 @@ typevartuple_alloc(PyObject *name, PyObject *module, PyObject *default_value) tvt->name = Py_NewRef(name); tvt->default_value = Py_XNewRef(default_value); tvt->evaluate_default = NULL; + tvt->owner = NULL; _PyObject_GC_TRACK(tvt); if (module != NULL) { if (PyObject_SetAttrString((PyObject *)tvt, "__module__", module) < 0) { @@ -1650,6 +1740,10 @@ static PyObject * typevartuple_reduce_impl(typevartupleobject *self) /*[clinic end generated code: output=3215bc0477913d20 input=3018a4d66147e807]*/ { + PyObject *owner = (PyObject *)FT_ATOMIC_LOAD_PTR(self->owner); + if (owner != NULL) { + return typeparam_reduce_anonymous((PyObject *)self, owner); + } return Py_NewRef(self->name); } @@ -1685,6 +1779,7 @@ typevartuple_traverse(PyObject *self, visitproc visit, void *arg) typevartupleobject *tvt = typevartupleobject_CAST(self); Py_VISIT(tvt->default_value); Py_VISIT(tvt->evaluate_default); + Py_VISIT(tvt->owner); PyObject_VisitManagedDict(self, visit, arg); return 0; } @@ -1695,6 +1790,7 @@ typevartuple_clear(PyObject *self) typevartupleobject *tvt = typevartupleobject_CAST(self); Py_CLEAR(tvt->default_value); Py_CLEAR(tvt->evaluate_default); + Py_CLEAR(tvt->owner); PyObject_ClearManagedDict(self); return 0; } @@ -1778,7 +1874,7 @@ arguments::\n\ \n\ For more details, see PEP 646.\n\ \n\ -Note that only TypeVarTuples defined in the global scope can be\n\ +Note that only TypeVarTuples reachable from the global scope can be\n\ pickled.\n\ "); @@ -2381,3 +2477,55 @@ _Py_set_typeparam_default(PyThreadState *ts, PyObject *typeparam, PyObject *eval return NULL; } } + +static int +set_typeparam_owner(PyThreadState *ts, PyObject *typeparam, PyObject *owner) +{ + if (Py_IS_TYPE(typeparam, ts->interp->cached_objects.typevar_type)) { +#ifdef Py_GIL_DISABLED + PyObject *oldowner = _Py_atomic_exchange_ptr(&((typevarobject *)typeparam)->owner, owner); + assert(oldowner == NULL); +#else + assert(((typevarobject *)typeparam)->owner == NULL); + ((typevarobject *)typeparam)->owner = owner; +#endif + } + else if (Py_IS_TYPE(typeparam, ts->interp->cached_objects.paramspec_type)) { +#ifdef Py_GIL_DISABLED + PyObject *oldowner = _Py_atomic_exchange_ptr(&((paramspecobject *)typeparam)->owner, owner); + assert(oldowner == NULL); +#else + assert(((paramspecobject *)typeparam)->owner == NULL); + ((paramspecobject *)typeparam)->owner = owner; +#endif + } + else if (Py_IS_TYPE(typeparam, ts->interp->cached_objects.typevartuple_type)) { +#ifdef Py_GIL_DISABLED + PyObject *oldowner = _Py_atomic_exchange_ptr(&((typevartupleobject *)typeparam)->owner, owner); + assert(oldowner == NULL); +#else + assert(((typevartupleobject *)typeparam)->owner == NULL); + ((typevartupleobject *)typeparam)->owner = owner; +#endif + } + else { + return -1; + } + Py_INCREF(owner); + return 0; +} + +PyObject * +_Py_set_typeparam_owner(PyThreadState *ts, PyObject *typeparam, PyObject *owner) +{ + assert(PyTuple_CheckExact(owner)); + assert(PyTuple_GET_SIZE(owner) == 3); + assert(PyUnicode_CheckExact(PyTuple_GET_ITEM(owner, 0))); + assert(PyUnicode_CheckExact(PyTuple_GET_ITEM(owner, 1))); + assert(PyLong_CheckExact(PyTuple_GET_ITEM(owner, 2))); + if (set_typeparam_owner(ts, typeparam, owner) < 0) { + PyErr_SetString(PyExc_RuntimeError, "invalid typeparam"); + return NULL; + } + return Py_NewRef(typeparam); +} diff --git a/Python/codegen.c b/Python/codegen.c index 0023d72cd5e91d..bd80de8df644a1 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -1227,13 +1227,17 @@ codegen_type_param_bound_or_default(compiler *c, expr_ty e, } static int -codegen_type_params(compiler *c, asdl_type_param_seq *type_params) +codegen_type_params(compiler *c, asdl_type_param_seq *type_params, PyObject *name) { if (!type_params) { return SUCCESS; } Py_ssize_t n = asdl_seq_LEN(type_params); bool seen_default = false; + PyObject *qualname = _PyCompile_PeekQualname(c, name); + if (qualname == NULL) { + return ERROR; + } for (Py_ssize_t i = 0; i < n; i++) { type_param_ty typeparam = asdl_seq_GET(type_params, i); @@ -1310,8 +1314,14 @@ codegen_type_params(compiler *c, asdl_type_param_seq *type_params) RETURN_IF_ERROR(codegen_nameop(c, loc, typeparam->v.ParamSpec.name, Store)); break; } + RETURN_IF_ERROR(codegen_nameop(c, loc, &_Py_ID(__name__), Load)); + ADDOP_LOAD_CONST(c, loc, qualname); + ADDOP_LOAD_CONST_NEW(c, loc, PyLong_FromSsize_t(i)); + ADDOP_I(c, loc, BUILD_TUPLE, 3); + ADDOP_I(c, loc, CALL_INTRINSIC_2, INTRINSIC_SET_TYPEPARAM_OWNER); } ADDOP_I(c, LOC(asdl_seq_GET(type_params, 0)), BUILD_TUPLE, n); + Py_DECREF(qualname); return SUCCESS; } @@ -1460,7 +1470,7 @@ codegen_function(compiler *c, stmt_ty s, int is_async) (void *)type_params, firstlineno, NULL, &umd); Py_DECREF(type_params_name); RETURN_IF_ERROR(ret); - RETURN_IF_ERROR_IN_SCOPE(c, codegen_type_params(c, type_params)); + RETURN_IF_ERROR_IN_SCOPE(c, codegen_type_params(c, type_params, name)); for (int i = 0; i < num_typeparam_args; i++) { ADDOP_I_IN_SCOPE(c, loc, LOAD_FAST, i); } @@ -1652,7 +1662,7 @@ codegen_class(compiler *c, stmt_ty s) (void *)type_params, firstlineno, s->v.ClassDef.name, NULL); Py_DECREF(type_params_name); RETURN_IF_ERROR(ret); - RETURN_IF_ERROR_IN_SCOPE(c, codegen_type_params(c, type_params)); + RETURN_IF_ERROR_IN_SCOPE(c, codegen_type_params(c, type_params, s->v.ClassDef.name)); _Py_DECLARE_STR(type_params, ".type_params"); RETURN_IF_ERROR_IN_SCOPE(c, codegen_nameop(c, loc, &_Py_STR(type_params), Store)); } @@ -1749,7 +1759,7 @@ codegen_typealias(compiler *c, stmt_ty s) Py_DECREF(type_params_name); RETURN_IF_ERROR(ret); ADDOP_LOAD_CONST_IN_SCOPE(c, loc, name); - RETURN_IF_ERROR_IN_SCOPE(c, codegen_type_params(c, type_params)); + RETURN_IF_ERROR_IN_SCOPE(c, codegen_type_params(c, type_params, name)); } else { ADDOP_LOAD_CONST(c, loc, name); diff --git a/Python/compile.c b/Python/compile.c index c04391e682f9ac..400ed9cd3325de 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -224,8 +224,8 @@ _PyCompile_MaybeAddStaticAttributeToClass(compiler *c, expr_ty e) return SUCCESS; } -static int -compiler_set_qualname(compiler *c) +static PyObject * +compiler_calc_qualname(compiler *c, PyObject *u_name) { Py_ssize_t stack_size; struct compiler_unit *u = c->u; @@ -248,8 +248,7 @@ compiler_set_qualname(compiler *c) if (stack_size == 2) { // If we're immediately within the module, we can skip // the rest and just set the qualname to be the same as name. - u->u_metadata.u_qualname = Py_NewRef(u->u_metadata.u_name); - return SUCCESS; + return Py_NewRef(u_name); } capsule = PyList_GET_ITEM(c->c_stack, stack_size - 2); parent = (struct compiler_unit *)PyCapsule_GetPointer(capsule, CAPSULE_NAME); @@ -259,15 +258,17 @@ compiler_set_qualname(compiler *c) if (u->u_scope_type == COMPILE_SCOPE_FUNCTION || u->u_scope_type == COMPILE_SCOPE_ASYNC_FUNCTION || u->u_scope_type == COMPILE_SCOPE_CLASS) { - assert(u->u_metadata.u_name); - mangled = _Py_Mangle(parent->u_private, u->u_metadata.u_name); + assert(u_name); + mangled = _Py_Mangle(parent->u_private, u_name); if (!mangled) { - return ERROR; + return NULL; } scope = _PyST_GetScope(parent->u_ste, mangled); Py_DECREF(mangled); - RETURN_IF_ERROR(scope); + if (scope == -1) { + return NULL; + } assert(scope != GLOBAL_IMPLICIT); if (scope == GLOBAL_EXPLICIT) force_global = 1; @@ -282,7 +283,7 @@ compiler_set_qualname(compiler *c) base = PyUnicode_Concat(parent->u_metadata.u_qualname, &_Py_STR(dot_locals)); if (base == NULL) { - return ERROR; + return NULL; } } else { @@ -295,18 +296,28 @@ compiler_set_qualname(compiler *c) name = PyUnicode_Concat(base, _Py_LATIN1_CHR('.')); Py_DECREF(base); if (name == NULL) { - return ERROR; + return NULL; } - PyUnicode_Append(&name, u->u_metadata.u_name); + PyUnicode_Append(&name, u_name); if (name == NULL) { - return ERROR; + return NULL; } } else { - name = Py_NewRef(u->u_metadata.u_name); + name = Py_NewRef(u_name); } - u->u_metadata.u_qualname = name; + return name; +} + +static int +compiler_set_qualname(compiler *c) +{ + PyObject *qualname = compiler_calc_qualname(c, c->u->u_metadata.u_name); + if (qualname == NULL) { + return ERROR; + } + c->u->u_metadata.u_qualname = qualname; return SUCCESS; } @@ -1315,6 +1326,12 @@ _PyCompile_Qualname(compiler *c) return c->u->u_metadata.u_qualname; } +PyObject * +_PyCompile_PeekQualname(compiler *c, PyObject *name) +{ + return compiler_calc_qualname(c, name); +} + _PyCompile_CodeUnitMetadata * _PyCompile_Metadata(compiler *c) { diff --git a/Python/intrinsics.c b/Python/intrinsics.c index 8ea920e690cd0d..75893085898e74 100644 --- a/Python/intrinsics.c +++ b/Python/intrinsics.c @@ -263,6 +263,7 @@ _PyIntrinsics_BinaryFunctions[] = { INTRINSIC_FUNC_ENTRY(INTRINSIC_TYPEVAR_WITH_CONSTRAINTS, make_typevar_with_constraints) INTRINSIC_FUNC_ENTRY(INTRINSIC_SET_FUNCTION_TYPE_PARAMS, _Py_set_function_type_params) INTRINSIC_FUNC_ENTRY(INTRINSIC_SET_TYPEPARAM_DEFAULT, _Py_set_typeparam_default) + INTRINSIC_FUNC_ENTRY(INTRINSIC_SET_TYPEPARAM_OWNER, _Py_set_typeparam_owner) }; #undef INTRINSIC_FUNC_ENTRY diff --git a/Python/symtable.c b/Python/symtable.c index a3d0fff80d24a1..785dede13547c4 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -1682,6 +1682,9 @@ symtable_enter_type_param_block(struct symtable *st, identifier name, return 0; } } + if (!symtable_add_def(st, &_Py_ID(__name__), DEF_GLOBAL, loc)) { + return 0; + } return 1; }