8000 Use a heap type for Generic · python/cpython@b38dea4 · GitHub
[go: up one dir, main page]

Skip to content

Commit b38dea4

Browse files
committed
Use a heap type for Generic
1 parent d916974 commit b38dea4

File tree

7 files changed

+91
-52
lines changed

7 files changed

+91
-52
lines changed

Include/internal/pycore_typeobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ struct types_state {
8282
struct type_cache type_cache;
8383
size_t num_builtins_initialized;
8484
static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
85+
PyObject *generic_type;
8586
};
8687

8788

Include/internal/pycore_typevarobject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ extern PyTypeObject _PyTypeVarTuple_Type;
1313
extern PyTypeObject _PyParamSpec_Type;
1414
extern PyTypeObject _PyParamSpecArgs_Type;
1515
extern PyTypeObject _PyParamSpecKwargs_Type;
16-
extern PyTypeObject _PyGeneric_Type;
1716

1817
extern PyObject *_Py_make_typevar(const char *, PyObject *);
1918
extern PyObject *_Py_make_paramspec(const char *);
2019
extern PyObject *_Py_make_typevartuple(const char *);
2120
extern PyObject *_Py_subscript_generic(PyObject *);
21+
extern int _Py_initialize_generic(PyInterpreterState *);
2222

2323
#ifdef __cplusplus
2424
}

Lib/typing.py

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,7 +1045,12 @@ def _generic_class_getitem(cls, params):
10451045
params = (params,)
10461046

10471047
params = tuple(_type_convert(p) for p in params)
1048-
if cls in (Generic, Protocol):
1048+
try:
1049+
is_generic_or_protocol = cls in (Generic, Protocol)
1050+
except NameError:
1051+
# Happens during interpreter startup
1052+
is_generic_or_protocol = True
1053+
if is_generic_or_protocol:
10491054
# Generic and Protocol can only be subscripted with unique type variables.
10501055
if not params:
10511056
raise TypeError(
@@ -1079,12 +1084,18 @@ def _generic_class_getitem(cls, params):
10791084

10801085

10811086
def _generic_init_subclass(cls, *args, **kwargs):
1082-
super(Generic, cls).__init_subclass__(*args, **kwargs)
1087+
try:
1088+
generic_cls = Generic
1089+
except NameError:
1090+
# Happens during interpreter startup. We are creating
1091+
# the _Dummy class.
1092+
generic_cls = cls.__mro__[1]
1093+
super(generic_cls, cls).__init_subclass__(*args, **kwargs)
10831094
tvars = []
10841095
if '__orig_bases__' in cls.__dict__:
1085-
error = Generic in cls.__orig_bases__
1096+
error = generic_cls in cls.__orig_bases__
10861097
else:
1087-
error = (Generic in cls.__bases__ and
1098+
error = (generic_cls in cls.__bases__ and
10881099
cls.__name__ != 'Protocol' and
10891100
type(cls) != _TypedDictMeta)
10901101
if error:
@@ -1099,7 +1110,7 @@ def _generic_init_subclass(cls, *args, **kwargs):
10991110
gvars = None
11001111
for base in cls.__orig_bases__:
11011112
if (isinstance(base, _GenericAlias) and
1102-
base.__origin__ is Generic):
1113+
base.__origin__ is generic_cls):
11031114
if gvars is not None:
11041115
raise TypeError(
11051116
"Cannot inherit from Generic[...] multiple types.")
@@ -1405,7 +1416,12 @@ def __mro_entries__(self, bases):
14051416

14061417
if self._name: # generic version of an ABC or built-in class
14071418
return super().__mro_entries__(bases)
1408-
if self.__origin__ is Generic:
1419+
try:
1420+
is_Generic = self.__origin__ is Generic
1421+
except NameError:
1422+
# Happens during interpreter startup
1423+
return (self.__origin__,)
1424+
if is_Generic:
14091425
if Protocol in bases:
14101426
return ()
14111427
i = bases.index(self)
@@ -1757,6 +1773,28 @@ def _lazy_load_getattr_static():
17571773

17581774
_cleanups.append(_lazy_load_getattr_static.cache_clear)
17591775

1776+
class _Dummy[T, *Ts, **P]():
1777+
pass
1778+
1779+
TypeVar = type(_Dummy.__type_variables__[0])
1780+
TypeVarTuple = type(_Dummy.__type_variables__[1])
1781+
ParamSpec = type(_Dummy.__type_variables__[2])
1782+
ParamSpecArgs = type(ParamSpec("P").args)
1783+
ParamSpecKwargs = type(ParamSpec("P").kwargs)
1784+
Generic = _Dummy.__mro__[1]
1785+
1786+
def _pickle_psargs(psargs):
1787+
return ParamSpecArgs, (psargs.__origin__,)
1788+
1789+
copyreg.pickle(ParamSpecArgs, _pickle_psargs)
1790+
1791+
def _pickle_pskwargs(pskwargs):
1792+
return ParamSpecKwargs, (pskwargs.__origin__,)
1793+
1794+
copyreg.pickle(ParamSpecKwargs, _pickle_pskwargs)
1795+
1796+
del _Dummy, _pickle_psargs, _pickle_pskwargs
1797+
17601798

17611799
class _ProtocolMeta(ABCMeta):
17621800
# This metaclass is really unfortunate and exists only because of
@@ -1899,27 +1937,6 @@ def _proto_hook(other):
18991937
if cls.__init__ is Protocol.__init__:
19001938
cls.__init__ = _no_init_or_replace_init
19011939

1902-
class _Dummy[T, *Ts, **P]():
1903-
pass
1904-
1905-
TypeVar = type(_Dummy.__type_variables__[0])
1906-
TypeVarTuple = type(_Dummy.__type_variables__[1])
1907-
ParamSpec = type(_Dummy.__type_variables__[2])
1908-
ParamSpecArgs = type(ParamSpec("P").args)
1909-
ParamSpecKwargs = type(ParamSpec("P").kwargs)
1910-
Generic = _Dummy.__mro__[1]
1911-
1912-
del _Dummy
1913-
1914-
def _pickle_psargs(psargs):
1915-
return ParamSpecArgs, (psargs.__origin__,)
1916-
1917-
copyreg.pickle(ParamSpecArgs, _pickle_psargs)
1918-
1919-
def _pickle_pskwargs(pskwargs):
1920-
return ParamSpecKwargs, (pskwargs.__origin__,)
1921-
1922-
copyreg.pickle(ParamSpecKwargs, _pickle_pskwargs)
19231940

19241941

19251942
class _AnnotatedAlias(_NotIterable, _GenericAlias, _root=True):

Objects/object.c

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
1515
#include "pycore_pystate.h" // _PyThreadState_GET()
1616
#include "pycore_symtable.h" // PySTEntry_Type
17-
#include "pycore_typevarobject.h" // _PyTypeVar_Type etc.
17+
#include "pycore_typevarobject.h" // _PyTypeVar_Type etc., _Py_initialize_generic
1818
#include "pycore_unionobject.h" // _PyUnion_Type
1919
#include "pycore_interpreteridobject.h" // _PyInterpreterID_Type
2020

@@ -2104,21 +2104,24 @@ static PyTypeObject* static_types[] = {
21042104
PyStatus
21052105
_PyTypes_InitTypes(PyInterpreterState *interp)
21062106
{
2107-
if (!_Py_IsMainInterpreter(interp)) {
2108-
return _PyStatus_OK();
2107+
if (_Py_IsMainInterpreter(interp)) {
2108+
// All other static types (unless initialized elsewhere)
2109+
for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) {
2110+
PyTypeObject *type = static_types[i];
2111+
if (_PyStaticType_InitBuiltin(type) < 0) {
2112+
return _PyStatus_ERR("Can't initialize builtin type");
2113+
}
2114+
if (type == &PyType_Type) {
2115+
// Sanitify checks of the two most important types
2116+
assert(PyBaseObject_Type.tp_base == NULL);
2117+
assert(PyType_Type.tp_base == &PyBaseObject_Type);
2118+
}
2119+
}
21092120
}
21102121

2111-
// All other static types (unless initialized elsewhere)
2112-
for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) {
2113-
PyTypeObject *type = static_types[i];
2114-
if (_PyStaticType_InitBuiltin(type) < 0) {
2115-
return _PyStatus_ERR("Can't initialize builtin type");
2116-
}
2117-
if (type == &PyType_Type) {
2118-
// Sanitify checks of the two most important types
2119-
assert(PyBaseObject_Type.tp_base == NULL);
2120-
assert(PyType_Type.tp_base == &PyBaseObject_Type);
2121-
}
2122+
// Must be after static types are initialized
2123+
if (_Py_initialize_generic(interp) < 0) {
2124+
return _PyStatus_ERR("Can't initialize Generic type");
21222125
}
21232126

21242127
return _PyStatus_OK();

Objects/typevarobject.c

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,7 +1074,12 @@ generic_class_getitem(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
10741074
PyObject *
10751075
_Py_subscript_generic(PyObject *params)
10761076
{
1077-
PyObject *args = PyTuple_Pack(2, &_PyGeneric_Type, params);
1077+
PyInterpreterState *interp = PyInterpreterState_Get();
1078+
if (interp->types.generic_type == NULL) {
1079+
PyErr_SetString(PyExc_SystemError, "Cannot find Generic type");
1080+
return NULL;
1081+
}
1082+
PyObject *args = PyTuple_Pack(2, interp->types.generic_type, params);
10781083
if (args == NULL) {
10791084
return NULL;
10801085
}
@@ -1091,12 +1096,25 @@ static PyMethodDef generic_methods[] = {
10911096
{NULL} /* Sentinel */
10921097
};
10931098

1094-
PyTypeObject _PyGeneric_Type = {
1095-
PyVarObject_HEAD_INIT(&PyType_Type, 0)
1096-
.tp_name = "typing.Generic",
1097-
.tp_basicsize = sizeof(PyObject),
1098-
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1099-
.tp_doc = generic_doc,
1100-
.tp_methods = generic_methods,
1101-
.tp_new = PyType_GenericNew,
1099+
static PyType_Slot generic_slots[] = {
1100+
{Py_tp_doc, (void *)generic_doc},
1101+
{Py_tp_methods, generic_methods},
1102+
{0, NULL},
1103+
};
1104+
1105+
PyType_Spec generic_spec = {
1106+
.name = "typing.Generic",
1107+
.basicsize = sizeof(PyObject),
1108+
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1109+
.slots = generic_slots,
11021110
};
1111+
1112+
int _Py_initialize_generic(PyInterpreterState *interp)
1113+
{
1114+
PyObject *type = PyType_FromSpec(&generic_spec);
1115+
if (type == NULL) {
1116+
return -1;
1117+
}
1118+
interp->types.generic_type = type;
1119+
return 0;
1120+
}

Python/bltinmodule.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3096,7 +3096,6 @@ _PyBuiltin_Init(PyInterpreterState *interp)
30963096
SETBUILTIN("TypeVar", &_PyTypeVar_Type);
30973097
SETBUILTIN("TypeVarTuple", &_PyTypeVarTuple_Type);
30983098
SETBUILTIN("ParamSpec", &_PyParamSpec_Type);
3099-
SETBUILTIN("Generic", &_PyGeneric_Type);
31003099
debug = PyBool_FromLong(config->optimization_level == 0);
31013100
if (PyDict_SetItemString(dict, "__debug__", debug) < 0) {
31023101
Py_DECREF(debug);

Python/pylifecycle.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,7 @@ finalize_interp_clear(PyThreadState *tstate)
17021702
int is_main_interp = _Py_IsMainInterpreter(tstate->interp);
17031703

17041704
_PyExc_ClearExceptionGroupType(tstate->interp);
1705+
Py_CLEAR(tstate->interp->types.generic_type);
17051706

17061707
/* Clear interpreter state and all thread states */
17071708
_PyInterpreterState_Clear(tstate);

0 commit comments

Comments
 (0)
0