Author: Ronan Lamy <ronan.l...@gmail.com> Branch: multiphase Changeset: r91597:8831a8a415be Date: 2017-06-12 21:29 +0100 http://bitbucket.org/pypy/pypy/changeset/8831a8a415be/
Log: Begin implementing PyType_FromSpec() diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -624,6 +624,8 @@ 'PyObject_CallFinalizerFromDealloc', '_PyTraceMalloc_Track', '_PyTraceMalloc_Untrack', 'PyBytes_FromFormat', 'PyBytes_FromFormatV', + + 'PyType_FromSpec', ] TYPES = {} FORWARD_DECLS = [] @@ -1348,6 +1350,7 @@ source_dir / "_warnings.c", source_dir / "pylifecycle.c", source_dir / "object.c", + source_dir / "typeobject.c", ] def build_eci(code, use_micronumpy=False, translating=False): diff --git a/pypy/module/cpyext/include/object.h b/pypy/module/cpyext/include/object.h --- a/pypy/module/cpyext/include/object.h +++ b/pypy/module/cpyext/include/object.h @@ -113,6 +113,10 @@ #define PyBUF_SHADOW 0x400 /* end Py3k buffer interface */ + +PyAPI_FUNC(PyObject*) PyType_FromSpec(PyType_Spec*); + + /* Flag bits for printing: */ #define Py_PRINT_RAW 1 /* No string quotes etc. */ diff --git a/pypy/module/cpyext/parse/cpyext_object.h b/pypy/module/cpyext/parse/cpyext_object.h --- a/pypy/module/cpyext/parse/cpyext_object.h +++ b/pypy/module/cpyext/parse/cpyext_object.h @@ -304,9 +304,10 @@ typedef struct _heaptypeobject { PyTypeObject ht_type; + PyAsyncMethods as_async; PyNumberMethods as_number; PyMappingMethods as_mapping; PySequenceMethods as_sequence; PyBufferProcs as_buffer; - PyObject *ht_name, *ht_slots; + PyObject *ht_name, *ht_slots, *ht_qualname; } PyHeapTypeObject; diff --git a/pypy/module/cpyext/src/typeobject.c b/pypy/module/cpyext/src/typeobject.c new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/src/typeobject.c @@ -0,0 +1,7 @@ +#include "Python.h" + +PyObject * +PyType_FromSpec(PyType_Spec *spec) +{ + return PyType_FromSpecWithBases(spec, NULL); +} diff --git a/pypy/module/cpyext/test/_testmultiphase.c b/pypy/module/cpyext/test/_testmultiphase.c --- a/pypy/module/cpyext/test/_testmultiphase.c +++ b/pypy/module/cpyext/test/_testmultiphase.c @@ -6,98 +6,98 @@ #include "Python.h" -///* Example objects */ -//typedef struct { -// PyObject_HEAD -// PyObject *x_attr; /* Attributes dictionary */ -//} ExampleObject; -// -///* Example methods */ -// -//static int -//Example_traverse(ExampleObject *self, visitproc visit, void *arg) -//{ -// Py_VISIT(self->x_attr); -// return 0; -//} -// -//static int -//Example_finalize(ExampleObject *self) -//{ -// Py_CLEAR(self->x_attr); -// return 0; -//} -// -//static PyObject * -//Example_demo(ExampleObject *self, PyObject *args) -//{ -// PyObject *o = NULL; -// if (!PyArg_ParseTuple(args, "|O:demo", &o)) -// return NULL; -// if (o != NULL && PyUnicode_Check(o)) { -// Py_INCREF(o); -// return o; -// } -// Py_INCREF(Py_None); -// return Py_None; -//} -// -// -//static PyMethodDef Example_methods[] = { -// {"demo", (PyCFunction)Example_demo, METH_VARARGS, -// PyDoc_STR("demo() -> None")}, -// {NULL, NULL} /* sentinel */ -//}; -// -//static PyObject * -//Example_getattro(ExampleObject *self, PyObject *name) -//{ -// if (self->x_attr != NULL) { -// PyObject *v = PyDict_GetItem(self->x_attr, name); -// if (v != NULL) { -// Py_INCREF(v); -// return v; -// } -// } -// return PyObject_GenericGetAttr((PyObject *)self, name); -//} -// -//static int -//Example_setattr(ExampleObject *self, char *name, PyObject *v) -//{ -// if (self->x_attr == NULL) { -// self->x_attr = PyDict_New(); -// if (self->x_attr == NULL) -// return -1; -// } -// if (v == NULL) { -// int rv = PyDict_DelItemString(self->x_attr, name); -// if (rv < 0) -// PyErr_SetString(PyExc_AttributeError, -// "delete non-existing Example attribute"); -// return rv; -// } -// else -// return PyDict_SetItemString(self->x_attr, name, v); -//} -// -//static PyType_Slot Example_Type_slots[] = { -// {Py_tp_doc, "The Example type"}, -// {Py_tp_finalize, Example_finalize}, -// {Py_tp_traverse, Example_traverse}, -// {Py_tp_getattro, Example_getattro}, -// {Py_tp_setattr, Example_setattr}, -// {Py_tp_methods, Example_methods}, -// {0, 0}, -//}; -// -//static PyType_Spec Example_Type_spec = { -// "_testimportexec.Example", -// sizeof(ExampleObject), -// 0, -// Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, -// Example_Type_slots -//}; +/* Example objects */ +typedef struct { + PyObject_HEAD + PyObject *x_attr; /* Attributes dictionary */ +} ExampleObject; + +/* Example methods */ + +static int +Example_traverse(ExampleObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->x_attr); + return 0; +} + +static int +Example_finalize(ExampleObject *self) +{ + Py_CLEAR(self->x_attr); + return 0; +} + +static PyObject * +Example_demo(ExampleObject *self, PyObject *args) +{ + PyObject *o = NULL; + if (!PyArg_ParseTuple(args, "|O:demo", &o)) + return NULL; + if (o != NULL && PyUnicode_Check(o)) { + Py_INCREF(o); + return o; + } + Py_INCREF(Py_None); + return Py_None; +} + + +static PyMethodDef Example_methods[] = { + {"demo", (PyCFunction)Example_demo, METH_VARARGS, + PyDoc_STR("demo() -> None")}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +Example_getattro(ExampleObject *self, PyObject *name) +{ + if (self->x_attr != NULL) { + PyObject *v = PyDict_GetItem(self->x_attr, name); + if (v != NULL) { + Py_INCREF(v); + return v; + } + } + return PyObject_GenericGetAttr((PyObject *)self, name); +} + +static int +Example_setattr(ExampleObject *self, char *name, PyObject *v) +{ + if (self->x_attr == NULL) { + self->x_attr = PyDict_New(); + if (self->x_attr == NULL) + return -1; + } + if (v == NULL) { + int rv = PyDict_DelItemString(self->x_attr, name); + if (rv < 0) + PyErr_SetString(PyExc_AttributeError, + "delete non-existing Example attribute"); + return rv; + } + else + return PyDict_SetItemString(self->x_attr, name, v); +} + +static PyType_Slot Example_Type_slots[] = { + {Py_tp_doc, "The Example type"}, + {Py_tp_finalize, Example_finalize}, + {Py_tp_traverse, Example_traverse}, + {Py_tp_getattro, Example_getattro}, + {Py_tp_setattr, Example_setattr}, + {Py_tp_methods, Example_methods}, + {0, 0}, +}; + +static PyType_Spec Example_Type_spec = { + "_testimportexec.Example", + sizeof(ExampleObject), + 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, + Example_Type_slots +}; /* Function of two integers returning integer */ @@ -158,18 +158,18 @@ } */ -//static PyType_Slot Str_Type_slots[] = { -// {Py_tp_base, NULL}, /* filled out in module exec function */ -// {0, 0}, -//}; -// -//static PyType_Spec Str_Type_spec = { -// "_testimportexec.Str", -// 0, -// 0, -// Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, -// Str_Type_slots -//}; +static PyType_Slot Str_Type_slots[] = { + {Py_tp_base, NULL}, /* filled out in module exec function */ + {0, 0}, +}; + +static PyType_Spec Str_Type_spec = { + "_testimportexec.Str", + 0, + 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + Str_Type_slots +}; static PyMethodDef testexport_methods[] = { {"foo", testexport_foo, METH_VARARGS, @@ -179,46 +179,46 @@ {NULL, NULL} /* sentinel */ }; -//static int execfunc(PyObject *m) -//{ -// PyObject *temp = NULL; -// -// /* Due to cross platform compiler issues the slots must be filled -// * here. It's required for portability to Windows without requiring -// * C++. */ -// Str_Type_slots[0].pfunc = &PyUnicode_Type; -// -// /* Add a custom type */ -// temp = PyType_FromSpec(&Example_Type_spec); -// if (temp == NULL) -// goto fail; -// if (PyModule_AddObject(m, "Example", temp) != 0) -// goto fail; -// -// /* Add an exception type */ -// temp = PyErr_NewException("_testimportexec.error", NULL, NULL); -// if (temp == NULL) -// goto fail; -// if (PyModule_AddObject(m, "error", temp) != 0) -// goto fail; -// -// /* Add Str */ -// temp = PyType_FromSpec(&Str_Type_spec); -// if (temp == NULL) -// goto fail; -// if (PyModule_AddObject(m, "Str", temp) != 0) -// goto fail; -// -// if (PyModule_AddIntConstant(m, "int_const", 1969) != 0) -// goto fail; -// -// if (PyModule_AddStringConstant(m, "str_const", "something different") != 0) -// goto fail; -// -// return 0; -// fail: -// return -1; -//} +static int execfunc(PyObject *m) +{ + PyObject *temp = NULL; + + /* Due to cross platform compiler issues the slots must be filled + * here. It's required for portability to Windows without requiring + * C++. */ + Str_Type_slots[0].pfunc = &PyUnicode_Type; + + /* Add a custom type */ + temp = PyType_FromSpec(&Example_Type_spec); + if (temp == NULL) + goto fail; + if (PyModule_AddObject(m, "Example", temp) != 0) + goto fail; + + /* Add an exception type */ + temp = PyErr_NewException("_testimportexec.error", NULL, NULL); + if (temp == NULL) + goto fail; + if (PyModule_AddObject(m, "error", temp) != 0) + goto fail; + + /* Add Str */ + temp = PyType_FromSpec(&Str_Type_spec); + if (temp == NULL) + goto fail; + if (PyModule_AddObject(m, "Str", temp) != 0) + goto fail; + + if (PyModule_AddIntConstant(m, "int_const", 1969) != 0) + goto fail; + + if (PyModule_AddStringConstant(m, "str_const", "something different") != 0) + goto fail; + + return 0; + fail: + return -1; +} /* Helper for module definitions; there'll be a lot of them */ #define TEST_MODULE_DEF(name, slots, methods) { \ @@ -234,7 +234,7 @@ } PyModuleDef_Slot main_slots[] = { - //{Py_mod_exec, execfunc}, + {Py_mod_exec, execfunc}, {0, NULL}, }; diff --git a/pypy/module/cpyext/test/test_module.py b/pypy/module/cpyext/test/test_module.py --- a/pypy/module/cpyext/test/test_module.py +++ b/pypy/module/cpyext/test/test_module.py @@ -145,3 +145,17 @@ raises(AttributeError, 'module.__path__') assert module is sys.modules[NAME] assert isinstance(module.__loader__, machinery.ExtensionFileLoader) + + def test_functionality(self): + import types + NAME = '_testmultiphase' + module = self.import_module(name=NAME) + assert isinstance(module, types.ModuleType) + ex = module.Example() + assert ex.demo('abcd') == 'abcd' + assert ex.demo() is None + raises(AttributeError, ex.abc) + ex.abc = 0 + assert ex.abc == 0 + assert module.foo(9, 9) == 18 + diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py --- a/pypy/module/cpyext/typeobject.py +++ b/pypy/module/cpyext/typeobject.py @@ -14,7 +14,7 @@ cpython_api, cpython_struct, bootstrap_function, Py_ssize_t, Py_ssize_tP, slot_function, generic_cpy_call, Py_TPFLAGS_READY, Py_TPFLAGS_READYING, Py_buffer, Py_TPFLAGS_HEAPTYPE, METH_VARARGS, METH_KEYWORDS, CANNOT_FAIL, - build_type_checkers, + build_type_checkers, Py_TPFLAGS_BASETYPE, PyObjectFields, PyTypeObject, PyTypeObjectPtr, cts, parse_dir) from pypy.module.cpyext.cparser import parse_source @@ -24,7 +24,7 @@ from pypy.module.cpyext.modsupport import convert_method_defs from pypy.module.cpyext.pyobject import ( PyObject, make_ref, from_ref, get_typedescr, make_typedescr, - track_reference, Py_DecRef, as_pyobj) + track_reference, Py_DecRef, as_pyobj, incref) from pypy.module.cpyext.slotdefs import ( slotdefs_for_tp_slots, slotdefs_for_wrappers, get_slot_tp_function, llslot) @@ -867,6 +867,78 @@ return generic_cpy_call( space, type.c_tp_alloc, type, 0) +@cts.decl("""PyObject * + PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)""", + result_is_ll=True) +def PyType_FromSpecWithBases(space, spec, bases): + res = PyType_GenericAlloc(space, space.w_type, 0) + res = cts.cast('PyHeapTypeObject *', res) + typ = res.c_ht_type + typ.c_tp_flags = rffi.cast(lltype.Signed, spec.c_flags) + typ.c_tp_flags |= Py_TPFLAGS_HEAPTYPE + #s = 'foo' + #res.c_ht_name = PyUnicode_FromString(s) + #res.c_ht_qualname = res.c_ht_name + incref(space, res.c_ht_qualname) + typ.c_tp_name = spec.c_name + slotdefs = rffi.cast(rffi.CArrayPtr(cts.gettype('PyType_Slot')), spec.c_slots) + if not bases: + w_base = space.w_object + bases_w = [] + i = 0 + while True: + slotdef = slotdefs[i] + if slotdef.c_slot == 0: + break + if slotdef.c_slot == cts.macros['Py_tp_base']: + w_base = from_ref(space, cts.cast('PyObject*', slotdef.c_pfunc)) + elif slotdef.c_slot == cts.macros['Py_tp_bases']: + bases = cts.cast('PyObject*', slotdef.c_pfunc) + bases_w = space.fixedview(from_ref(space, bases)) + i += 1 + if not bases_w: + bases_w = [w_base] + else: + bases_w = space.fixed_view(from_ref(space, bases)) + w_base = best_base(space, bases_w) + base = cts.cast('PyTypeObject*', make_ref(space, w_base)) + if False: # not base.c_tp_flags & Py_TPFLAGS_BASETYPE: + raise oefmt(space.w_TypeError, + "type '%s' is not an acceptable base type", + rffi.charp2str(base.c_tp_name)) + + typ.c_tp_as_async = res.c_as_async + typ.c_tp_as_number = res.c_as_number + typ.c_tp_as_sequence = res.c_as_sequence + typ.c_tp_as_mapping = res.c_as_mapping + typ.c_tp_as_buffer = res.c_as_buffer + typ.c_tp_bases = bases + typ.c_tp_base = base + typ.c_tp_basicsize = cts.cast('Py_ssize_t', spec.c_basicsize) + typ.c_tp_itemsize = cts.cast('Py_ssize_t', spec.c_itemsize) + + i = 0 + while True: + slotdef = slotdefs[i] + if slotdef.c_slot == 0: + break + slot = slotdef.c_slot + if slot < 0: # or slot > len(slotoffsets): + raise oefmt(space.w_RuntimeError, "invalid slot offset") + if slot in (cts.macros['Py_tp_base'], cts.macros['Py_tp_bases']): + # Processed above + i += 1 + continue + #fill_slot(res, slot, slotdef.c_pfunc) + # XXX: need to make a copy of the docstring slot, which usually + # points to a static string literal + i += 1 + + if not typ.c_tp_dealloc: + typ.c_tp_dealloc = llslot(space, subtype_dealloc) + py_type_ready(space, typ) + return cts.cast('PyObject*', res) + @cpython_api([PyTypeObjectPtr, PyObject], PyObject, error=CANNOT_FAIL, result_borrowed=True) def _PyType_Lookup(space, type, w_name): @@ -894,4 +966,3 @@ return if w_obj.is_cpytype(): w_obj.mutated(None) - _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit