[pypy-commit] pypy py3.5: Merged in multiphase (pull request #567)
rlamy
pypy.commits at gmail.com
Fri Sep 1 11:39:48 EDT 2017
Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: py3.5
Changeset: r92297:cc9f1669c568
Date: 2017-09-01 15:39 +0000
http://bitbucket.org/pypy/pypy/changeset/cc9f1669c568/
Log: Merged in multiphase (pull request #567)
Multiphase
diff too long, truncating to 2000 out of 2006 lines
diff --git a/lib-python/3/test/test_importlib/extension/test_loader.py b/lib-python/3/test/test_importlib/extension/test_loader.py
--- a/lib-python/3/test/test_importlib/extension/test_loader.py
+++ b/lib-python/3/test/test_importlib/extension/test_loader.py
@@ -88,6 +88,7 @@
def setUp(self):
self.name = '_testmultiphase'
+ __import__(self.name) # PyPy hack
finder = self.machinery.FileFinder(None)
self.spec = importlib.util.find_spec(self.name)
assert self.spec
@@ -145,7 +146,8 @@
importlib.reload(module)
self.assertIs(ex_class, module.Example)
- def test_try_registration(self):
+ # XXX: PyPy doesn't support the PyState_* functions yet
+ def XXXtest_try_registration(self):
'''Assert that the PyState_{Find,Add,Remove}Module C API doesn't work'''
module = self.load_module()
with self.subTest('PyState_FindModule'):
diff --git a/lib_pypy/_testmultiphase.c b/lib_pypy/_testmultiphase.c
new file mode 100644
--- /dev/null
+++ b/lib_pypy/_testmultiphase.c
@@ -0,0 +1,627 @@
+/* Copied from CPython's Modules/_testmultiphase.c */
+/***************************************************/
+
+/* Testing module for multi-phase initialization of extension modules (PEP 489)
+ */
+
+#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
+};
+
+/* Function of two integers returning integer */
+
+PyDoc_STRVAR(testexport_foo_doc,
+"foo(i,j)\n\
+\n\
+Return the sum of i and j.");
+
+static PyObject *
+testexport_foo(PyObject *self, PyObject *args)
+{
+ long i, j;
+ long res;
+ if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
+ return NULL;
+ res = i + j;
+ return PyLong_FromLong(res);
+}
+
+/* Test that PyState registration fails */
+
+//PyDoc_STRVAR(call_state_registration_func_doc,
+//"register_state(0): call PyState_FindModule()\n\
+//register_state(1): call PyState_AddModule()\n\
+//register_state(2): call PyState_RemoveModule()");
+//
+//static PyObject *
+//call_state_registration_func(PyObject *mod, PyObject *args)
+//{
+// int i, ret;
+// PyModuleDef *def = PyModule_GetDef(mod);
+// if (def == NULL) {
+// return NULL;
+// }
+// if (!PyArg_ParseTuple(args, "i:call_state_registration_func", &i))
+// return NULL;
+// switch (i) {
+// case 0:
+// mod = PyState_FindModule(def);
+// if (mod == NULL) {
+// Py_RETURN_NONE;
+// }
+// return mod;
+// case 1:
+// ret = PyState_AddModule(mod, def);
+// if (ret != 0) {
+// return NULL;
+// }
+// break;
+// case 2:
+// ret = PyState_RemoveModule(def);
+// if (ret != 0) {
+// return NULL;
+// }
+// break;
+// }
+// Py_RETURN_NONE;
+//}
+
+
+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,
+ testexport_foo_doc},
+// {"call_state_registration_func", call_state_registration_func,
+// METH_VARARGS, call_state_registration_func_doc},
+ {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;
+}
+
+/* Helper for module definitions; there'll be a lot of them */
+#define TEST_MODULE_DEF(name, slots, methods) { \
+ PyModuleDef_HEAD_INIT, /* m_base */ \
+ name, /* m_name */ \
+ PyDoc_STR("Test module " name), /* m_doc */ \
+ 0, /* m_size */ \
+ methods, /* m_methods */ \
+ slots, /* m_slots */ \
+ NULL, /* m_traverse */ \
+ NULL, /* m_clear */ \
+ NULL, /* m_free */ \
+}
+
+PyModuleDef_Slot main_slots[] = {
+ {Py_mod_exec, execfunc},
+ {0, NULL},
+};
+
+static PyModuleDef main_def = TEST_MODULE_DEF("main", main_slots, testexport_methods);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase(PyObject *spec)
+{
+ return PyModuleDef_Init(&main_def);
+}
+
+
+/**** Importing a non-module object ****/
+
+static PyModuleDef def_nonmodule;
+static PyModuleDef def_nonmodule_with_methods;
+
+/* Create a SimpleNamespace(three=3) */
+static PyObject*
+createfunc_nonmodule(PyObject *spec, PyModuleDef *def)
+{
+ PyObject *dct, *ns, *three;
+
+ if (def != &def_nonmodule && def != &def_nonmodule_with_methods) {
+ PyErr_SetString(PyExc_SystemError, "def does not match");
+ return NULL;
+ }
+
+ dct = PyDict_New();
+ if (dct == NULL)
+ return NULL;
+
+ three = PyLong_FromLong(3);
+ if (three == NULL) {
+ Py_DECREF(dct);
+ return NULL;
+ }
+ PyDict_SetItemString(dct, "three", three);
+ Py_DECREF(three);
+
+ ns = _PyNamespace_New(dct);
+ Py_DECREF(dct);
+ return ns;
+}
+
+static PyModuleDef_Slot slots_create_nonmodule[] = {
+ {Py_mod_create, createfunc_nonmodule},
+ {0, NULL},
+};
+
+static PyModuleDef def_nonmodule = TEST_MODULE_DEF(
+ "_testmultiphase_nonmodule", slots_create_nonmodule, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_nonmodule(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_nonmodule);
+}
+
+PyDoc_STRVAR(nonmodule_bar_doc,
+"bar(i,j)\n\
+\n\
+Return the difference of i - j.");
+
+static PyObject *
+nonmodule_bar(PyObject *self, PyObject *args)
+{
+ long i, j;
+ long res;
+ if (!PyArg_ParseTuple(args, "ll:bar", &i, &j))
+ return NULL;
+ res = i - j;
+ return PyLong_FromLong(res);
+}
+
+static PyMethodDef nonmodule_methods[] = {
+ {"bar", nonmodule_bar, METH_VARARGS, nonmodule_bar_doc},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyModuleDef def_nonmodule_with_methods = TEST_MODULE_DEF(
+ "_testmultiphase_nonmodule_with_methods", slots_create_nonmodule, nonmodule_methods);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_nonmodule_with_methods(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_nonmodule_with_methods);
+}
+
+/**** Non-ASCII-named modules ****/
+
+static PyModuleDef def_nonascii_latin = { \
+ PyModuleDef_HEAD_INIT, /* m_base */
+ "_testmultiphase_nonascii_latin", /* m_name */
+ PyDoc_STR("Module named in Czech"), /* m_doc */
+ 0, /* m_size */
+ NULL, /* m_methods */
+ NULL, /* m_slots */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+
+PyMODINIT_FUNC
+PyInitU__testmultiphase_zkouka_naten_evc07gi8e(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_nonascii_latin);
+}
+
+static PyModuleDef def_nonascii_kana = { \
+ PyModuleDef_HEAD_INIT, /* m_base */
+ "_testmultiphase_nonascii_kana", /* m_name */
+ PyDoc_STR("Module named in Japanese"), /* m_doc */
+ 0, /* m_size */
+ NULL, /* m_methods */
+ NULL, /* m_slots */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+
+PyMODINIT_FUNC
+PyInitU_eckzbwbhc6jpgzcx415x(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_nonascii_kana);
+}
+
+/*** Module with a single-character name ***/
+
+PyMODINIT_FUNC
+PyInit_x(PyObject *spec)
+{
+ return PyModuleDef_Init(&main_def);
+}
+
+/**** Testing NULL slots ****/
+
+static PyModuleDef null_slots_def = TEST_MODULE_DEF(
+ "_testmultiphase_null_slots", NULL, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_null_slots(PyObject *spec)
+{
+ return PyModuleDef_Init(&null_slots_def);
+}
+
+/**** Problematic modules ****/
+
+static PyModuleDef_Slot slots_bad_large[] = {
+ {_Py_mod_LAST_SLOT + 1, NULL},
+ {0, NULL},
+};
+
+static PyModuleDef def_bad_large = TEST_MODULE_DEF(
+ "_testmultiphase_bad_slot_large", slots_bad_large, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_bad_slot_large(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_bad_large);
+}
+
+static PyModuleDef_Slot slots_bad_negative[] = {
+ {-1, NULL},
+ {0, NULL},
+};
+
+static PyModuleDef def_bad_negative = TEST_MODULE_DEF(
+ "_testmultiphase_bad_slot_negative", slots_bad_negative, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_bad_slot_negative(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_bad_negative);
+}
+
+static PyModuleDef def_create_int_with_state = { \
+ PyModuleDef_HEAD_INIT, /* m_base */
+ "create_with_state", /* m_name */
+ PyDoc_STR("Not a PyModuleObject object, but requests per-module state"),
+ 10, /* m_size */
+ NULL, /* m_methods */
+ slots_create_nonmodule, /* m_slots */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_create_int_with_state(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_create_int_with_state);
+}
+
+
+static PyModuleDef def_negative_size = { \
+ PyModuleDef_HEAD_INIT, /* m_base */
+ "negative_size", /* m_name */
+ PyDoc_STR("PyModuleDef with negative m_size"),
+ -1, /* m_size */
+ NULL, /* m_methods */
+ slots_create_nonmodule, /* m_slots */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_negative_size(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_negative_size);
+}
+
+
+static PyModuleDef uninitialized_def = TEST_MODULE_DEF("main", main_slots, testexport_methods);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_export_uninitialized(PyObject *spec)
+{
+ return (PyObject*) &uninitialized_def;
+}
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_export_null(PyObject *spec)
+{
+ return NULL;
+}
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_export_raise(PyObject *spec)
+{
+ PyErr_SetString(PyExc_SystemError, "bad export function");
+ return NULL;
+}
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_export_unreported_exception(PyObject *spec)
+{
+ PyErr_SetString(PyExc_SystemError, "bad export function");
+ return PyModuleDef_Init(&main_def);
+}
+
+static PyObject*
+createfunc_null(PyObject *spec, PyModuleDef *def)
+{
+ return NULL;
+}
+
+PyModuleDef_Slot slots_create_null[] = {
+ {Py_mod_create, createfunc_null},
+ {0, NULL},
+};
+
+static PyModuleDef def_create_null = TEST_MODULE_DEF(
+ "_testmultiphase_create_null", slots_create_null, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_create_null(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_create_null);
+}
+
+static PyObject*
+createfunc_raise(PyObject *spec, PyModuleDef *def)
+{
+ PyErr_SetString(PyExc_SystemError, "bad create function");
+ return NULL;
+}
+
+static PyModuleDef_Slot slots_create_raise[] = {
+ {Py_mod_create, createfunc_raise},
+ {0, NULL},
+};
+
+static PyModuleDef def_create_raise = TEST_MODULE_DEF(
+ "_testmultiphase_create_null", slots_create_raise, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_create_raise(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_create_raise);
+}
+
+static PyObject*
+createfunc_unreported_exception(PyObject *spec, PyModuleDef *def)
+{
+ PyErr_SetString(PyExc_SystemError, "bad create function");
+ return PyModule_New("foo");
+}
+
+static PyModuleDef_Slot slots_create_unreported_exception[] = {
+ {Py_mod_create, createfunc_unreported_exception},
+ {0, NULL},
+};
+
+static PyModuleDef def_create_unreported_exception = TEST_MODULE_DEF(
+ "_testmultiphase_create_unreported_exception", slots_create_unreported_exception, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_create_unreported_exception(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_create_unreported_exception);
+}
+
+static PyModuleDef_Slot slots_nonmodule_with_exec_slots[] = {
+ {Py_mod_create, createfunc_nonmodule},
+ {Py_mod_exec, execfunc},
+ {0, NULL},
+};
+
+static PyModuleDef def_nonmodule_with_exec_slots = TEST_MODULE_DEF(
+ "_testmultiphase_nonmodule_with_exec_slots", slots_nonmodule_with_exec_slots, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_nonmodule_with_exec_slots(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_nonmodule_with_exec_slots);
+}
+
+static int
+execfunc_err(PyObject *mod)
+{
+ return -1;
+}
+
+static PyModuleDef_Slot slots_exec_err[] = {
+ {Py_mod_exec, execfunc_err},
+ {0, NULL},
+};
+
+static PyModuleDef def_exec_err = TEST_MODULE_DEF(
+ "_testmultiphase_exec_err", slots_exec_err, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_exec_err(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_exec_err);
+}
+
+static int
+execfunc_raise(PyObject *spec)
+{
+ PyErr_SetString(PyExc_SystemError, "bad exec function");
+ return -1;
+}
+
+static PyModuleDef_Slot slots_exec_raise[] = {
+ {Py_mod_exec, execfunc_raise},
+ {0, NULL},
+};
+
+static PyModuleDef def_exec_raise = TEST_MODULE_DEF(
+ "_testmultiphase_exec_raise", slots_exec_raise, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_exec_raise(PyObject *mod)
+{
+ return PyModuleDef_Init(&def_exec_raise);
+}
+
+static int
+execfunc_unreported_exception(PyObject *mod)
+{
+ PyErr_SetString(PyExc_SystemError, "bad exec function");
+ return 0;
+}
+
+static PyModuleDef_Slot slots_exec_unreported_exception[] = {
+ {Py_mod_exec, execfunc_unreported_exception},
+ {0, NULL},
+};
+
+static PyModuleDef def_exec_unreported_exception = TEST_MODULE_DEF(
+ "_testmultiphase_exec_unreported_exception", slots_exec_unreported_exception, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_exec_unreported_exception(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_exec_unreported_exception);
+}
+
+/*** Helper for imp test ***/
+
+static PyModuleDef imp_dummy_def = TEST_MODULE_DEF("imp_dummy", main_slots, testexport_methods);
+
+PyMODINIT_FUNC
+PyInit_imp_dummy(PyObject *spec)
+{
+ return PyModuleDef_Init(&imp_dummy_def);
+}
diff --git a/lib_pypy/_testmultiphase.py b/lib_pypy/_testmultiphase.py
new file mode 100644
--- /dev/null
+++ b/lib_pypy/_testmultiphase.py
@@ -0,0 +1,18 @@
+import imp
+import os
+
+try:
+ import cpyext
+except ImportError:
+ raise ImportError("No module named '_testmultiphase'")
+import _pypy_testcapi
+cfile = '_testmultiphase.c'
+thisdir = os.path.dirname(__file__)
+output_dir = _pypy_testcapi.get_hashed_dir(os.path.join(thisdir, cfile))
+try:
+ fp, filename, description = imp.find_module('_test_multiphase', path=[output_dir])
+ with fp:
+ imp.load_module('_testmultiphase', fp, filename, description)
+except ImportError:
+ print('could not find _testmultiphase in %s' % output_dir)
+ _pypy_testcapi.compile_shared('_testmultiphase.c', '_testmultiphase', output_dir)
diff --git a/pypy/doc/whatsnew-pypy3-head.rst b/pypy/doc/whatsnew-pypy3-head.rst
--- a/pypy/doc/whatsnew-pypy3-head.rst
+++ b/pypy/doc/whatsnew-pypy3-head.rst
@@ -5,4 +5,7 @@
.. this is the revision after release-pypy3.3-5.8.x was branched
.. startrev: c173df164527
+.. branch: multiphase
+Implement PyType_FromSpec (PEP 384) and fix issues with PEP 489 support.
+
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
@@ -26,6 +26,7 @@
from pypy.interpreter.module import Module
from pypy.interpreter.function import StaticMethod
from pypy.objspace.std.sliceobject import W_SliceObject
+from pypy.objspace.std.unicodeobject import encode_object
from pypy.module.__builtin__.descriptor import W_Property
#from pypy.module.micronumpy.base import W_NDimArray
from rpython.rlib.entrypoint import entrypoint_lowlevel
@@ -596,6 +597,8 @@
'PyObject_CallFinalizerFromDealloc',
'_PyTraceMalloc_Track', '_PyTraceMalloc_Untrack',
'PyBytes_FromFormat', 'PyBytes_FromFormatV',
+
+ 'PyType_FromSpec',
]
TYPES = {}
FORWARD_DECLS = []
@@ -1319,6 +1322,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):
@@ -1473,12 +1477,11 @@
# order of things here.
from rpython.rlib import rdynload
- name = space.text_w(space.getattr(w_spec, space.newtext("name")))
+ w_name = space.getattr(w_spec, space.newtext("name"))
path = space.text_w(space.getattr(w_spec, space.newtext("origin")))
if os.sep not in path:
path = os.curdir + os.sep + path # force a '/' in the path
- basename = name.split('.')[-1]
try:
ll_libname = rffi.str2charp(path)
try:
@@ -1486,13 +1489,14 @@
finally:
lltype.free(ll_libname, flavor='raw')
except rdynload.DLOpenError as e:
- w_name = space.newunicode(name.decode('ascii'))
w_path = space.newfilename(path)
raise raise_import_error(space,
space.newfilename(e.msg), w_name, w_path)
look_for = None
+ name = space.text_w(w_name)
#
if space.config.objspace.usemodules._cffi_backend:
+ basename = name.split('.')[-1]
look_for = '_cffi_pypyinit_%s' % (basename,)
try:
initptr = rdynload.dlsym(dll, look_for)
@@ -1507,7 +1511,7 @@
raise
#
if space.config.objspace.usemodules.cpyext:
- also_look_for = 'PyInit_%s' % (basename,)
+ also_look_for = get_init_name(space, w_name)
try:
initptr = rdynload.dlsym(dll, also_look_for)
except KeyError:
@@ -1518,12 +1522,24 @@
look_for += ' or ' + also_look_for
else:
look_for = also_look_for
+ assert look_for is not None
msg = u"function %s not found in library %s" % (
- unicode(look_for), space.unicode_w(space.newfilename(path)))
- w_name = space.newunicode(name.decode('ascii'))
+ look_for.decode('utf-8'), space.unicode_w(space.newfilename(path)))
w_path = space.newfilename(path)
raise_import_error(space, space.newunicode(msg), w_name, w_path)
+def get_init_name(space, w_name):
+ name_u = space.unicode_w(w_name)
+ basename_u = name_u.split(u'.')[-1]
+ try:
+ basename = basename_u.encode('ascii')
+ return 'PyInit_%s' % (basename,)
+ except UnicodeEncodeError:
+ basename = space.bytes_w(encode_object(
+ space, space.newunicode(basename_u), 'punycode', None))
+ basename = basename.replace('-', '_')
+ return 'PyInitU_%s' % (basename,)
+
initfunctype = lltype.Ptr(lltype.FuncType([], PyObject))
@@ -1542,7 +1558,16 @@
try:
initfunc = rffi.cast(initfunctype, initptr)
initret = generic_cpy_call_dont_convert_result(space, initfunc)
- state.check_and_raise_exception()
+ if not initret:
+ state.check_and_raise_exception()
+ raise oefmt(space.w_SystemError,
+ "initialization of %s failed without raising an exception",
+ name)
+ else:
+ if state.clear_exception():
+ raise oefmt(space.w_SystemError,
+ "initialization of %s raised unreported exception",
+ name)
if not initret.c_ob_type:
raise oefmt(space.w_SystemError,
"init function of %s returned uninitialized object",
@@ -1557,6 +1582,7 @@
name)
finally:
state.package_context = old_context
+ # XXX: should disable single-step init for non-ascii module names
w_mod = get_w_obj_and_decref(space, initret)
state.fixup_extension(w_mod, name, path)
return w_mod
@@ -1570,6 +1596,9 @@
space.getbuiltinmodule("cpyext")
mod_as_pyobj = rawrefcount.from_obj(PyObject, w_mod)
if mod_as_pyobj:
+ if cts.cast('PyModuleObject*', mod_as_pyobj).c_md_state:
+ # already initialised
+ return
return exec_def(space, w_mod, mod_as_pyobj)
@specialize.ll()
@@ -1641,10 +1670,9 @@
cpyext_glob_tid_ptr[0] = 0
keepalive_until_here(*keepalives)
- if is_PyObject(RESULT_TYPE):
- if not convert_result or not is_pyobj(result):
+ if convert_result and is_PyObject(RESULT_TYPE):
+ if not is_pyobj(result):
ret = result
- has_result = bool(ret)
else:
# The object reference returned from a C function
# that is called from Python must be an owned reference
@@ -1653,13 +1681,13 @@
ret = get_w_obj_and_decref(space, result)
else:
ret = None
- has_result = ret is not None
# Check for exception consistency
# XXX best attempt, will miss preexisting error that is
# overwritten with a new error of the same type
error = PyErr_Occurred(space)
has_new_error = (error is not None) and (error is not preexist_error)
+ has_result = ret is not None
if not expect_null and has_new_error and has_result:
raise oefmt(space.w_SystemError,
"An exception was set, but function returned a "
diff --git a/pypy/module/cpyext/include/Python.h b/pypy/module/cpyext/include/Python.h
--- a/pypy/module/cpyext/include/Python.h
+++ b/pypy/module/cpyext/include/Python.h
@@ -84,6 +84,7 @@
#include "pyconfig.h"
#include "object.h"
+#include "typeslots.h"
#include "abstract.h"
#include "pymath.h"
#include "pyport.h"
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
@@ -140,6 +140,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/modsupport.py b/pypy/module/cpyext/modsupport.py
--- a/pypy/module/cpyext/modsupport.py
+++ b/pypy/module/cpyext/modsupport.py
@@ -1,7 +1,8 @@
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.module.cpyext.api import (
cpython_api, METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, cts,
- parse_dir, bootstrap_function, generic_cpy_call, slot_function)
+ parse_dir, bootstrap_function, generic_cpy_call,
+ generic_cpy_call_dont_convert_result, slot_function)
from pypy.module.cpyext.pyobject import PyObject, as_pyobj, make_typedescr
from pypy.interpreter.module import Module
from pypy.module.cpyext.methodobject import (
@@ -142,23 +143,26 @@
mod = rffi.cast(PyModuleObject, mod_as_pyobj)
moddef = mod.c_md_def
cur_slot = rffi.cast(rffi.CArrayPtr(PyModuleDef_Slot), moddef.c_m_slots)
+ if moddef.c_m_size >= 0 and not mod.c_md_state:
+ # Always set md_state, to use as marker for exec_extension_module()
+ # (cf. CPython's PyModule_ExecDef)
+ mod.c_md_state = lltype.malloc(
+ rffi.VOIDP.TO, moddef.c_m_size, flavor='raw', zero=True)
while cur_slot and rffi.cast(lltype.Signed, cur_slot[0].c_slot):
if rffi.cast(lltype.Signed, cur_slot[0].c_slot) == 2:
execf = rffi.cast(execfunctype, cur_slot[0].c_value)
- res = generic_cpy_call(space, execf, w_mod)
- has_error = PyErr_Occurred(space) is not None
- if rffi.cast(lltype.Signed, res):
- if has_error:
- state = space.fromcache(State)
- state.check_and_raise_exception()
- else:
+ res = generic_cpy_call_dont_convert_result(space, execf, w_mod)
+ state = space.fromcache(State)
+ if res:
+ state.check_and_raise_exception()
+ raise oefmt(space.w_SystemError,
+ "execution of module %S failed without "
+ "setting an exception", w_mod.w_name)
+ else:
+ if state.clear_exception():
raise oefmt(space.w_SystemError,
- "execution of module %S failed without "
- "setting an exception", w_mod.w_name)
- if has_error:
- raise oefmt(space.w_SystemError,
- "execution of module %S raised unreported "
- "exception", w_mod.w_name)
+ "execution of module %S raised unreported "
+ "exception", w_mod.w_name)
cur_slot = rffi.ptradd(cur_slot, 1)
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
@@ -289,11 +289,25 @@
destructor tp_finalize;
} PyTypeObject;
+typedef struct{
+ int slot; /* slot id, see below */
+ void *pfunc; /* function pointer */
+} PyType_Slot;
+
+typedef struct{
+ const char* name;
+ int basicsize;
+ int itemsize;
+ unsigned int flags;
+ PyType_Slot *slots; /* terminated by slot==0. */
+} PyType_Spec;
+
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/parse/typeslots.h b/pypy/module/cpyext/parse/typeslots.h
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/parse/typeslots.h
@@ -0,0 +1,80 @@
+/* Do not renumber the file; these numbers are part of the stable ABI. */
+#define Py_mp_ass_subscript 3
+#define Py_mp_length 4
+#define Py_mp_subscript 5
+#define Py_nb_absolute 6
+#define Py_nb_add 7
+#define Py_nb_and 8
+#define Py_nb_bool 9
+#define Py_nb_divmod 10
+#define Py_nb_float 11
+#define Py_nb_floor_divide 12
+#define Py_nb_index 13
+#define Py_nb_inplace_add 14
+#define Py_nb_inplace_and 15
+#define Py_nb_inplace_floor_divide 16
+#define Py_nb_inplace_lshift 17
+#define Py_nb_inplace_multiply 18
+#define Py_nb_inplace_or 19
+#define Py_nb_inplace_power 20
+#define Py_nb_inplace_remainder 21
+#define Py_nb_inplace_rshift 22
+#define Py_nb_inplace_subtract 23
+#define Py_nb_inplace_true_divide 24
+#define Py_nb_inplace_xor 25
+#define Py_nb_int 26
+#define Py_nb_invert 27
+#define Py_nb_lshift 28
+#define Py_nb_multiply 29
+#define Py_nb_negative 30
+#define Py_nb_or 31
+#define Py_nb_positive 32
+#define Py_nb_power 33
+#define Py_nb_remainder 34
+#define Py_nb_rshift 35
+#define Py_nb_subtract 36
+#define Py_nb_true_divide 37
+#define Py_nb_xor 38
+#define Py_sq_ass_item 39
+#define Py_sq_concat 40
+#define Py_sq_contains 41
+#define Py_sq_inplace_concat 42
+#define Py_sq_inplace_repeat 43
+#define Py_sq_item 44
+#define Py_sq_length 45
+#define Py_sq_repeat 46
+#define Py_tp_alloc 47
+#define Py_tp_base 48
+#define Py_tp_bases 49
+#define Py_tp_call 50
+#define Py_tp_clear 51
+#define Py_tp_dealloc 52
+#define Py_tp_del 53
+#define Py_tp_descr_get 54
+#define Py_tp_descr_set 55
+#define Py_tp_doc 56
+#define Py_tp_getattr 57
+#define Py_tp_getattro 58
+#define Py_tp_hash 59
+#define Py_tp_init 60
+#define Py_tp_is_gc 61
+#define Py_tp_iter 62
+#define Py_tp_iternext 63
+#define Py_tp_methods 64
+#define Py_tp_new 65
+#define Py_tp_repr 66
+#define Py_tp_richcompare 67
+#define Py_tp_setattr 68
+#define Py_tp_setattro 69
+#define Py_tp_str 70
+#define Py_tp_traverse 71
+#define Py_tp_members 72
+#define Py_tp_getset 73
+#define Py_tp_free 74
+#define Py_nb_matrix_multiply 75
+#define Py_nb_inplace_matrix_multiply 76
+#define Py_am_await 77
+#define Py_am_aiter 78
+#define Py_am_anext 79
+/* New in 3.5 */
+#define Py_tp_finalize 80
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/multiphase2.c b/pypy/module/cpyext/test/multiphase2.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/multiphase2.c
@@ -0,0 +1,627 @@
+/* Copied from CPython's Modules/_testmultiphase.c */
+/***************************************************/
+
+/* Testing module for multi-phase initialization of extension modules (PEP 489)
+ */
+
+#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
+};
+
+/* Function of two integers returning integer */
+
+PyDoc_STRVAR(testexport_foo_doc,
+"foo(i,j)\n\
+\n\
+Return the sum of i and j.");
+
+static PyObject *
+testexport_foo(PyObject *self, PyObject *args)
+{
+ long i, j;
+ long res;
+ if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
+ return NULL;
+ res = i + j;
+ return PyLong_FromLong(res);
+}
+
+/* Test that PyState registration fails */
+/*
+PyDoc_STRVAR(call_state_registration_func_doc,
+"register_state(0): call PyState_FindModule()\n\
+register_state(1): call PyState_AddModule()\n\
+register_state(2): call PyState_RemoveModule()");
+
+static PyObject *
+call_state_registration_func(PyObject *mod, PyObject *args)
+{
+ int i, ret;
+ PyModuleDef *def = PyModule_GetDef(mod);
+ if (def == NULL) {
+ return NULL;
+ }
+ if (!PyArg_ParseTuple(args, "i:call_state_registration_func", &i))
+ return NULL;
+ switch (i) {
+ case 0:
+ mod = PyState_FindModule(def);
+ if (mod == NULL) {
+ Py_RETURN_NONE;
+ }
+ return mod;
+ case 1:
+ ret = PyState_AddModule(mod, def);
+ if (ret != 0) {
+ return NULL;
+ }
+ break;
+ case 2:
+ ret = PyState_RemoveModule(def);
+ if (ret != 0) {
+ return NULL;
+ }
+ break;
+ }
+ Py_RETURN_NONE;
+}
+*/
+
+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,
+ testexport_foo_doc},
+/* {"call_state_registration_func", call_state_registration_func,
+ METH_VARARGS, call_state_registration_func_doc},*/
+ {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;
+}
+
+/* Helper for module definitions; there'll be a lot of them */
+#define TEST_MODULE_DEF(name, slots, methods) { \
+ PyModuleDef_HEAD_INIT, /* m_base */ \
+ name, /* m_name */ \
+ PyDoc_STR("Test module " name), /* m_doc */ \
+ 0, /* m_size */ \
+ methods, /* m_methods */ \
+ slots, /* m_slots */ \
+ NULL, /* m_traverse */ \
+ NULL, /* m_clear */ \
+ NULL, /* m_free */ \
+}
+
+PyModuleDef_Slot main_slots[] = {
+ {Py_mod_exec, execfunc},
+ {0, NULL},
+};
+
+static PyModuleDef main_def = TEST_MODULE_DEF("main", main_slots, testexport_methods);
+
+PyMODINIT_FUNC
+PyInit_multiphase2(PyObject *spec)
+{
+ return PyModuleDef_Init(&main_def);
+}
+
+
+/**** Importing a non-module object ****/
+
+static PyModuleDef def_nonmodule;
+static PyModuleDef def_nonmodule_with_methods;
+
+/* Create a SimpleNamespace(three=3) */
+static PyObject*
+createfunc_nonmodule(PyObject *spec, PyModuleDef *def)
+{
+ PyObject *dct, *ns, *three;
+
+ if (def != &def_nonmodule && def != &def_nonmodule_with_methods) {
+ PyErr_SetString(PyExc_SystemError, "def does not match");
+ return NULL;
+ }
+
+ dct = PyDict_New();
+ if (dct == NULL)
+ return NULL;
+
+ three = PyLong_FromLong(3);
+ if (three == NULL) {
+ Py_DECREF(dct);
+ return NULL;
+ }
+ PyDict_SetItemString(dct, "three", three);
+ Py_DECREF(three);
+
+ ns = _PyNamespace_New(dct);
+ Py_DECREF(dct);
+ return ns;
+}
+
+static PyModuleDef_Slot slots_create_nonmodule[] = {
+ {Py_mod_create, createfunc_nonmodule},
+ {0, NULL},
+};
+
+static PyModuleDef def_nonmodule = TEST_MODULE_DEF(
+ "_testmultiphase_nonmodule", slots_create_nonmodule, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_nonmodule(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_nonmodule);
+}
+
+PyDoc_STRVAR(nonmodule_bar_doc,
+"bar(i,j)\n\
+\n\
+Return the difference of i - j.");
+
+static PyObject *
+nonmodule_bar(PyObject *self, PyObject *args)
+{
+ long i, j;
+ long res;
+ if (!PyArg_ParseTuple(args, "ll:bar", &i, &j))
+ return NULL;
+ res = i - j;
+ return PyLong_FromLong(res);
+}
+
+static PyMethodDef nonmodule_methods[] = {
+ {"bar", nonmodule_bar, METH_VARARGS, nonmodule_bar_doc},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyModuleDef def_nonmodule_with_methods = TEST_MODULE_DEF(
+ "_testmultiphase_nonmodule_with_methods", slots_create_nonmodule, nonmodule_methods);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_nonmodule_with_methods(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_nonmodule_with_methods);
+}
+
+/**** Non-ASCII-named modules ****/
+
+static PyModuleDef def_nonascii_latin = { \
+ PyModuleDef_HEAD_INIT, /* m_base */
+ "_testmultiphase_nonascii_latin", /* m_name */
+ PyDoc_STR("Module named in Czech"), /* m_doc */
+ 0, /* m_size */
+ NULL, /* m_methods */
+ NULL, /* m_slots */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+
+PyMODINIT_FUNC
+PyInitU__testmultiphase_zkouka_naten_evc07gi8e(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_nonascii_latin);
+}
+
+static PyModuleDef def_nonascii_kana = { \
+ PyModuleDef_HEAD_INIT, /* m_base */
+ "_testmultiphase_nonascii_kana", /* m_name */
+ PyDoc_STR("Module named in Japanese"), /* m_doc */
+ 0, /* m_size */
+ NULL, /* m_methods */
+ NULL, /* m_slots */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+
+PyMODINIT_FUNC
+PyInitU_eckzbwbhc6jpgzcx415x(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_nonascii_kana);
+}
+
+/*** Module with a single-character name ***/
+
+PyMODINIT_FUNC
+PyInit_x(PyObject *spec)
+{
+ return PyModuleDef_Init(&main_def);
+}
+
+/**** Testing NULL slots ****/
+
+static PyModuleDef null_slots_def = TEST_MODULE_DEF(
+ "_testmultiphase_null_slots", NULL, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_null_slots(PyObject *spec)
+{
+ return PyModuleDef_Init(&null_slots_def);
+}
+
+/**** Problematic modules ****/
+
+static PyModuleDef_Slot slots_bad_large[] = {
+ {_Py_mod_LAST_SLOT + 1, NULL},
+ {0, NULL},
+};
+
+static PyModuleDef def_bad_large = TEST_MODULE_DEF(
+ "_testmultiphase_bad_slot_large", slots_bad_large, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_bad_slot_large(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_bad_large);
+}
+
+static PyModuleDef_Slot slots_bad_negative[] = {
+ {-1, NULL},
+ {0, NULL},
+};
+
+static PyModuleDef def_bad_negative = TEST_MODULE_DEF(
+ "_testmultiphase_bad_slot_negative", slots_bad_negative, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_bad_slot_negative(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_bad_negative);
+}
+
+static PyModuleDef def_create_int_with_state = { \
+ PyModuleDef_HEAD_INIT, /* m_base */
+ "create_with_state", /* m_name */
+ PyDoc_STR("Not a PyModuleObject object, but requests per-module state"),
+ 10, /* m_size */
+ NULL, /* m_methods */
+ slots_create_nonmodule, /* m_slots */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_create_int_with_state(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_create_int_with_state);
+}
+
+
+static PyModuleDef def_negative_size = { \
+ PyModuleDef_HEAD_INIT, /* m_base */
+ "negative_size", /* m_name */
+ PyDoc_STR("PyModuleDef with negative m_size"),
+ -1, /* m_size */
+ NULL, /* m_methods */
+ slots_create_nonmodule, /* m_slots */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_negative_size(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_negative_size);
+}
+
+
+static PyModuleDef uninitialized_def = TEST_MODULE_DEF("main", main_slots, testexport_methods);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_export_uninitialized(PyObject *spec)
+{
+ return (PyObject*) &uninitialized_def;
+}
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_export_null(PyObject *spec)
+{
+ return NULL;
+}
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_export_raise(PyObject *spec)
+{
+ PyErr_SetString(PyExc_SystemError, "bad export function");
+ return NULL;
+}
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_export_unreported_exception(PyObject *spec)
+{
+ PyErr_SetString(PyExc_SystemError, "bad export function");
+ return PyModuleDef_Init(&main_def);
+}
+
+static PyObject*
+createfunc_null(PyObject *spec, PyModuleDef *def)
+{
+ return NULL;
+}
+
+PyModuleDef_Slot slots_create_null[] = {
+ {Py_mod_create, createfunc_null},
+ {0, NULL},
+};
+
+static PyModuleDef def_create_null = TEST_MODULE_DEF(
+ "_testmultiphase_create_null", slots_create_null, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_create_null(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_create_null);
+}
+
+static PyObject*
+createfunc_raise(PyObject *spec, PyModuleDef *def)
+{
+ PyErr_SetString(PyExc_SystemError, "bad create function");
+ return NULL;
+}
+
+static PyModuleDef_Slot slots_create_raise[] = {
+ {Py_mod_create, createfunc_raise},
+ {0, NULL},
+};
+
+static PyModuleDef def_create_raise = TEST_MODULE_DEF(
+ "_testmultiphase_create_null", slots_create_raise, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_create_raise(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_create_raise);
+}
+
+static PyObject*
+createfunc_unreported_exception(PyObject *spec, PyModuleDef *def)
+{
+ PyErr_SetString(PyExc_SystemError, "bad create function");
+ return PyModule_New("foo");
+}
+
+static PyModuleDef_Slot slots_create_unreported_exception[] = {
+ {Py_mod_create, createfunc_unreported_exception},
+ {0, NULL},
+};
+
+static PyModuleDef def_create_unreported_exception = TEST_MODULE_DEF(
+ "_testmultiphase_create_unreported_exception", slots_create_unreported_exception, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_create_unreported_exception(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_create_unreported_exception);
+}
+
+static PyModuleDef_Slot slots_nonmodule_with_exec_slots[] = {
+ {Py_mod_create, createfunc_nonmodule},
+ {Py_mod_exec, execfunc},
+ {0, NULL},
+};
+
+static PyModuleDef def_nonmodule_with_exec_slots = TEST_MODULE_DEF(
+ "_testmultiphase_nonmodule_with_exec_slots", slots_nonmodule_with_exec_slots, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_nonmodule_with_exec_slots(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_nonmodule_with_exec_slots);
+}
+
+static int
+execfunc_err(PyObject *mod)
+{
+ return -1;
+}
+
+static PyModuleDef_Slot slots_exec_err[] = {
+ {Py_mod_exec, execfunc_err},
+ {0, NULL},
+};
+
+static PyModuleDef def_exec_err = TEST_MODULE_DEF(
+ "_testmultiphase_exec_err", slots_exec_err, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_exec_err(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_exec_err);
+}
+
+static int
+execfunc_raise(PyObject *spec)
+{
+ PyErr_SetString(PyExc_SystemError, "bad exec function");
+ return -1;
+}
+
+static PyModuleDef_Slot slots_exec_raise[] = {
+ {Py_mod_exec, execfunc_raise},
+ {0, NULL},
+};
+
+static PyModuleDef def_exec_raise = TEST_MODULE_DEF(
+ "_testmultiphase_exec_raise", slots_exec_raise, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_exec_raise(PyObject *mod)
+{
+ return PyModuleDef_Init(&def_exec_raise);
+}
+
+static int
+execfunc_unreported_exception(PyObject *mod)
+{
+ PyErr_SetString(PyExc_SystemError, "bad exec function");
+ return 0;
+}
+
+static PyModuleDef_Slot slots_exec_unreported_exception[] = {
+ {Py_mod_exec, execfunc_unreported_exception},
+ {0, NULL},
+};
+
+static PyModuleDef def_exec_unreported_exception = TEST_MODULE_DEF(
+ "_testmultiphase_exec_unreported_exception", slots_exec_unreported_exception, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_exec_unreported_exception(PyObject *spec)
+{
+ return PyModuleDef_Init(&def_exec_unreported_exception);
+}
+
+/*** Helper for imp test ***/
+
+static PyModuleDef imp_dummy_def = TEST_MODULE_DEF("imp_dummy", main_slots, testexport_methods);
+
+PyMODINIT_FUNC
+PyInit_imp_dummy(PyObject *spec)
+{
+ return PyModuleDef_Init(&imp_dummy_def);
+}
diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py
--- a/pypy/module/cpyext/test/test_cpyext.py
+++ b/pypy/module/cpyext/test/test_cpyext.py
@@ -363,6 +363,8 @@
for name in self.imported_module_names:
self.unimport_module(name)
self.cleanup()
+ state = self.space.fromcache(State)
+ assert not state.operror
class AppTestCpythonExtension(AppTestCpythonExtensionBase):
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
@@ -134,3 +134,117 @@
"""
raises(SystemError, self.import_module, name='multiphase', body=body,
init=init)
+
+class AppTestMultiPhase2(AppTestCpythonExtensionBase):
+ def setup_class(cls):
+ cls.w_name = cls.space.wrap('multiphase2')
+ AppTestCpythonExtensionBase.setup_class.im_func(cls)
+
+ def test_multiphase2(self):
+ import sys
+ from importlib import machinery, util
+ module = self.import_module(name=self.name)
+ finder = machinery.FileFinder(None)
+ spec = util.find_spec(self.name)
+ assert spec
+ assert module.__name__ == self.name
+ #assert module.__file__ == spec.origin
+ assert module.__package__ == ''
+ raises(AttributeError, 'module.__path__')
+ assert module is sys.modules[self.name]
+ assert isinstance(module.__loader__, machinery.ExtensionFileLoader)
+
+ def test_functionality(self):
+ import types
+ module = self.import_module(name=self.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
+ assert isinstance(module.Str(), str)
+ assert module.Str(1) + '23' == '123'
+ raises(module.error, 'raise module.error()')
+ assert module.int_const == 1969
+ assert module.str_const == 'something different'
+
+ def test_reload(self):
+ import importlib
+ module = self.import_module(name=self.name)
+ ex_class = module.Example
+ # Simulate what importlib.reload() does, without recomputing the spec
+ module.__spec__.loader.exec_module(module)
+ assert ex_class is module.Example
+
+ def w_load_from_name(self, name, origin=None, use_prefix=True):
+ from importlib import machinery, util
+ if not origin:
+ module = self.import_module(name=self.name)
+ origin = module.__loader__.path
+ if use_prefix:
+ name = '_testmultiphase_' + name
+ loader = machinery.ExtensionFileLoader(name, origin)
+ spec = util.spec_from_loader(name, loader)
+ module = util.module_from_spec(spec)
+ loader.exec_module(module)
+ return module
+
+ def test_bad_modules(self):
+ # XXX: not a very good test, since most internal issues in cpyext
+ # cause SystemErrors.
+ module = self.import_module(name=self.name)
+ origin = module.__loader__.path
+ for name in [
+ 'bad_slot_large',
+ 'bad_slot_negative',
+ 'create_int_with_state',
+ 'negative_size',
+ 'create_null',
+ 'create_raise',
+ 'create_unreported_exception',
+ 'nonmodule_with_exec_slots',
+ 'exec_err',
+ 'exec_raise',
+ 'exec_unreported_exception',
+ ]:
+ raises(SystemError, self.load_from_name, name, origin)
+
+ def test_export_null(self):
+ excinfo = raises(SystemError, self.load_from_name, 'export_null')
+ assert "initialization" in excinfo.value.args[0]
+ assert "without raising" in excinfo.value.args[0]
+
+ def test_export_uninit(self):
+ excinfo = raises(SystemError, self.load_from_name, 'export_uninitialized')
+ assert "init function" in excinfo.value.args[0]
+ assert "uninitialized object" in excinfo.value.args[0]
+
+ def test_export_raise(self):
+ excinfo = raises(SystemError, self.load_from_name, 'export_raise')
+ assert "bad export function" == excinfo.value.args[0]
+
+ def test_export_unreported(self):
+ excinfo = raises(SystemError, self.load_from_name, 'export_unreported_exception')
+ assert "initialization" in excinfo.value.args[0]
+ assert "unreported exception" in excinfo.value.args[0]
+
+ def test_unloadable_nonascii(self):
+ name = u"fo\xf3"
+ excinfo = raises(ImportError, self.load_from_name, name)
+ assert excinfo.value.name == '_testmultiphase_' + name
+
+ def test_nonascii(self):
+ module = self.import_module(name=self.name)
+ origin = module.__loader__.path
+ cases = [
+ ('_testmultiphase_zkou\u0161ka_na\u010dten\xed', 'Czech'),
+ ('\uff3f\u30a4\u30f3\u30dd\u30fc\u30c8\u30c6\u30b9\u30c8',
+ 'Japanese'),
+ ]
+ for name, lang in cases:
+ module = self.load_from_name(name, origin=origin, use_prefix=False)
+ assert module.__name__ == name
+ assert module.__doc__ == "Module named in %s" % lang
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -4,7 +4,7 @@
from pypy.module.cpyext.test.test_api import BaseApiTest
from pypy.module.cpyext.api import generic_cpy_call
from pypy.module.cpyext.pyobject import make_ref, from_ref
-from pypy.module.cpyext.typeobject import PyTypeObjectPtr
+from pypy.module.cpyext.typeobject import cts, PyTypeObjectPtr
import sys
import pytest
@@ -502,6 +502,9 @@
ref = make_ref(space, w_obj)
api.Py_DecRef(ref)
+ def test_typeslots(self, space):
+ assert cts.macros['Py_tp_doc'] == 56
+
class AppTestSlots(AppTestCpythonExtensionBase):
def setup_class(cls):
AppTestCpythonExtensionBase.setup_class.im_func(cls)
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
@@ -1,5 +1,6 @@
import os
+from rpython.rlib.unroll import unrolling_iterable
from rpython.rlib import jit
from rpython.rlib.objectmodel import specialize, we_are_translated
from rpython.rtyper.lltypesystem import rffi, lltype
@@ -14,17 +15,17 @@
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
+from pypy.module.cpyext.cparser import CTypeSpace
from pypy.module.cpyext.methodobject import (W_PyCClassMethodObject,
W_PyCWrapperObject, PyCFunction_NewEx, PyCFunction, PyMethodDef,
W_PyCMethodObject, W_PyCFunctionObject)
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)
@@ -42,6 +43,8 @@
PyHeapTypeObject = cts.gettype('PyHeapTypeObject *')
+cts.parse_header(parse_dir / "typeslots.h")
+
class W_GetSetPropertyEx(GetSetProperty):
def __init__(self, getset, w_type):
@@ -594,6 +597,7 @@
if obj_pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE:
heaptype = rffi.cast(PyHeapTypeObject, obj)
Py_DecRef(space, heaptype.c_ht_name)
+ Py_DecRef(space, heaptype.c_ht_qualname)
Py_DecRef(space, base_pyo)
_dealloc(space, obj)
@@ -876,6 +880,115 @@
return generic_cpy_call(
space, type.c_tp_alloc, type, 0)
+def _parse_typeslots():
+ slots_hdr = CTypeSpace()
+ slots_hdr.parse_header(parse_dir / "typeslots.h")
+ prefix2member = {
+ 'tp': "ht_type",
+ 'am': "as_async",
+ 'nb': "as_number",
+ 'mp': "as_mapping",
+ 'sq': "as_sequence",
+ 'bf': "as_buffer"}
+
+ TABLE = []
+ HTO = cts.gettype('PyHeapTypeObject')
+ for name, num in slots_hdr.macros.items():
+ assert isinstance(num, int)
+ assert name.startswith('Py_')
+ name = name[3:]
+ membername = 'c_' + prefix2member[name[:2]]
+ slotname = 'c_' + name
+ TARGET = HTO._flds[membername]._flds[slotname]
+ TABLE.append((num, membername, slotname, TARGET))
+ return unrolling_iterable(TABLE)
+SLOT_TABLE = _parse_typeslots()
+
+def fill_slot(ht, slotnum, ptr):
+ for num, membername, slotname, TARGET in SLOT_TABLE:
+ if num == slotnum:
+ setattr(getattr(ht, membername), slotname, rffi.cast(TARGET, ptr))
+
+
+ at cts.decl("""PyObject *
+ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)""",
+ result_is_ll=True)
+def PyType_FromSpecWithBases(space, spec, bases):
+ from pypy.module.cpyext.unicodeobject import PyUnicode_FromString
+ 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
+ specname = rffi.charp2str(cts.cast('char*', spec.c_name))
+ dotpos = specname.rfind('.')
+ if dotpos < 0:
+ name = specname
+ else:
+ name = specname[dotpos + 1:]
+ res.c_ht_name = make_ref(space, space.newtext(name))
+ 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]
+ slotnum = rffi.cast(lltype.Signed, slotdef.c_slot)
+ if slotnum == 0:
+ break
+ elif slotnum == cts.macros['Py_tp_base']:
+ w_base = from_ref(space, cts.cast('PyObject*', slotdef.c_pfunc))
+ elif slotnum == 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.fixedview(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]
+ slot = rffi.cast(lltype.Signed, slotdef.c_slot)
+ if slot == 0:
+ break
+ 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)
More information about the pypy-commit
mailing list