Author: Ronan Lamy <[email protected]>
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)
[email protected]("""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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit