Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 35.2k
gh-60074: add new stable API function PyType_FromMetaclass#93012
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
05940a830e4e1b0525d78db62080b4a478c872249eFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| Added the new function :c:func:`PyType_FromMetaclass`, which generalizes the | ||
| existing :c:func:`PyType_FromModuleAndSpec` using an additional metaclass | ||
| argument. This is useful for language binding tools, where it can be used to | ||
| intercept type-related operations like subclassing or static attribute access | ||
| by specifying a metaclass with custom slots. | ||
| Importantly, :c:func:`PyType_FromMetaclass` is available in the Limited API, | ||
| which provides a path towards migrating more binding tools onto the Stable ABI. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -2275,3 +2275,5 @@ | ||
| added = '3.11' | ||
| [function.PyErr_SetHandledException] | ||
| added = '3.11' | ||
| [function.PyType_FromMetaclass] | ||
| added = '3.12' | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -308,6 +308,32 @@ test_dict_inner(int count) | ||
| } | ||
| } | ||
| static PyObject *pytype_fromspec_meta(PyObject* self, PyObject *meta) | ||
| { | ||
| if (!PyType_Check(meta)) { | ||
| PyErr_SetString( | ||
| TestError, | ||
| "pytype_fromspec_meta: must be invoked with a type argument!"); | ||
| return NULL; | ||
| } | ||
encukou marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| PyType_Slot HeapCTypeViaMetaclass_slots[] = { | ||
| {0}, | ||
| }; | ||
| PyType_Spec HeapCTypeViaMetaclass_spec = { | ||
| "_testcapi.HeapCTypeViaMetaclass", | ||
| sizeof(PyObject), | ||
| 0, | ||
| Py_TPFLAGS_DEFAULT, | ||
| HeapCTypeViaMetaclass_slots | ||
| }; | ||
| return PyType_FromMetaclass( | ||
| (PyTypeObject *) meta, NULL, &HeapCTypeViaMetaclass_spec, NULL); | ||
| } | ||
| static PyObject* | ||
| test_dict_iteration(PyObject* self, PyObject *Py_UNUSED(ignored)) | ||
| { | ||
| @@ -5886,6 +5912,7 @@ static PyMethodDef TestMethods[] = { | ||
| {"test_long_numbits", test_long_numbits, METH_NOARGS}, | ||
| {"test_k_code", test_k_code, METH_NOARGS}, | ||
| {"test_empty_argparse", test_empty_argparse, METH_NOARGS}, | ||
| {"pytype_fromspec_meta", pytype_fromspec_meta, METH_O}, | ||
| {"parse_tuple_and_keywords", parse_tuple_and_keywords, METH_VARARGS}, | ||
| {"pyobject_repr_from_null", pyobject_repr_from_null, METH_NOARGS}, | ||
| {"pyobject_str_from_null", pyobject_str_from_null, METH_NOARGS}, | ||
| @@ -7078,6 +7105,38 @@ static PyType_Spec HeapCTypeSubclassWithFinalizer_spec = { | ||
| HeapCTypeSubclassWithFinalizer_slots | ||
| }; | ||
| static PyType_Slot HeapCTypeMetaclass_slots[] = { | ||
| {0}, | ||
| }; | ||
| static PyType_Spec HeapCTypeMetaclass_spec = { | ||
| "_testcapi.HeapCTypeMetaclass", | ||
| sizeof(PyHeapTypeObject), | ||
| sizeof(PyMemberDef), | ||
| Py_TPFLAGS_DEFAULT, | ||
| HeapCTypeMetaclass_slots | ||
| }; | ||
| static PyObject * | ||
| heap_ctype_metaclass_custom_tp_new(PyTypeObject *tp, PyObject *args, PyObject *kwargs) | ||
| { | ||
| return PyType_Type.tp_new(tp, args, kwargs); | ||
| } | ||
| static PyType_Slot HeapCTypeMetaclassCustomNew_slots[] = { | ||
| { Py_tp_new, heap_ctype_metaclass_custom_tp_new }, | ||
| {0}, | ||
| }; | ||
| static PyType_Spec HeapCTypeMetaclassCustomNew_spec = { | ||
| "_testcapi.HeapCTypeMetaclassCustomNew", | ||
| sizeof(PyHeapTypeObject), | ||
| sizeof(PyMemberDef), | ||
| Py_TPFLAGS_DEFAULT, | ||
| HeapCTypeMetaclassCustomNew_slots | ||
| }; | ||
| typedef struct { | ||
| PyObject_HEAD | ||
| PyObject *dict; | ||
| @@ -7591,6 +7650,20 @@ PyInit__testcapi(void) | ||
| Py_DECREF(subclass_with_finalizer_bases); | ||
| PyModule_AddObject(m, "HeapCTypeSubclassWithFinalizer", HeapCTypeSubclassWithFinalizer); | ||
| PyObject *HeapCTypeMetaclass = PyType_FromMetaclass( | ||
| &PyType_Type, m, &HeapCTypeMetaclass_spec, (PyObject *) &PyType_Type); | ||
| if (HeapCTypeMetaclass == NULL) { | ||
| return NULL; | ||
| } | ||
| PyModule_AddObject(m, "HeapCTypeMetaclass", HeapCTypeMetaclass); | ||
| PyObject *HeapCTypeMetaclassCustomNew = PyType_FromMetaclass( | ||
| &PyType_Type, m, &HeapCTypeMetaclassCustomNew_spec, (PyObject *) &PyType_Type); | ||
| if (HeapCTypeMetaclassCustomNew == NULL) { | ||
| return NULL; | ||
| } | ||
| PyModule_AddObject(m, "HeapCTypeMetaclassCustomNew", HeapCTypeMetaclassCustomNew); | ||
| if (PyType_Ready(&ContainerNoGC_type) < 0) { | ||
| return NULL; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -3366,13 +3366,8 @@ static const PySlot_Offset pyslot_offsets[] = { | ||
| }; | ||
| PyObject * | ||
| PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) | ||
| { | ||
| return PyType_FromModuleAndSpec(NULL, spec, bases); | ||
| } | ||
| PyObject * | ||
| PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) | ||
| PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, | ||
| PyType_Spec *spec, PyObject *bases) | ||
| { | ||
| PyHeapTypeObject *res; | ||
| PyObject *modname; | ||
| @@ -3384,6 +3379,16 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) | ||
| char *res_start; | ||
| short slot_offset, subslot_offset; | ||
| if (!metaclass) { | ||
| metaclass = &PyType_Type; | ||
| } | ||
| if (metaclass->tp_new != PyType_Type.tp_new) { | ||
| PyErr_SetString(PyExc_TypeError, | ||
| "Metaclasses with custom tp_new are not supported."); | ||
| return NULL; | ||
| } | ||
wjakob marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| nmembers = weaklistoffset = dictoffset = vectorcalloffset = 0; | ||
| for (slot = spec->slots; slot->slot; slot++) { | ||
| if (slot->slot == Py_tp_members) { | ||
| @@ -3412,7 +3417,7 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) | ||
| } | ||
| } | ||
| res = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, nmembers); | ||
| res = (PyHeapTypeObject*)metaclass->tp_alloc(metaclass, nmembers); | ||
| if (res == NULL) | ||
| return NULL; | ||
| res_start = (char*)res; | ||
| @@ -3639,10 +3644,22 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) | ||
| return NULL; | ||
| } | ||
| PyObject * | ||
| PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) | ||
| { | ||
| return PyType_FromMetaclass(NULL, module, spec, bases); | ||
| } | ||
| PyObject * | ||
| PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) | ||
| { | ||
| return PyType_FromMetaclass(NULL, NULL, spec, bases); | ||
| } | ||
| PyObject * | ||
| PyType_FromSpec(PyType_Spec *spec) | ||
| { | ||
| return PyType_FromSpecWithBases(spec, NULL); | ||
| return PyType_FromMetaclass(NULL, NULL, spec, NULL); | ||
| } | ||
| PyObject * | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.