1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
import py, os, sys
from .support import setup_make
from pypy.interpreter.gateway import interp2app, unwrap_spec
from rpython.translator.tool.cbuild import ExternalCompilationInfo
from rpython.translator import platform
from rpython.translator.gensupp import uniquemodulename
from rpython.tool.udir import udir
from pypy.module.cpyext import api
from pypy.module.cpyext.state import State
currpath = py.path.local(__file__).dirpath()
test_dct = str(currpath.join("crossingDict.so"))
def setup_module(mod):
setup_make("crossingDict.so")
# from pypy/module/cpyext/test/test_cpyext.py; modified to accept more external
# symbols and called directly instead of import_module
def compile_extension_module(space, modname, **kwds):
"""
Build an extension module and return the filename of the resulting native
code file.
modname is the name of the module, possibly including dots if it is a module
inside a package.
Any extra keyword arguments are passed on to ExternalCompilationInfo to
build the module (so specify your source with one of those).
"""
state = space.fromcache(State)
api_library = state.api_lib
if sys.platform == 'win32':
kwds["libraries"] = []#[api_library]
# '%s' undefined; assuming extern returning int
kwds["compile_extra"] = ["/we4013"]
# prevent linking with PythonXX.lib
w_maj, w_min = space.fixedview(space.sys.get('version_info'), 5)[:2]
kwds["link_extra"] = ["/NODEFAULTLIB:Python%d%d.lib" %
(space.int_w(w_maj), space.int_w(w_min))]
elif sys.platform == 'darwin':
kwds["link_files"] = [str(api_library + '.dylib')]
else:
kwds["link_files"] = [str(api_library + '.so')]
if sys.platform.startswith('linux'):
kwds["compile_extra"]=["-Werror", "-g", "-O0"]
kwds["link_extra"]=["-g"]
modname = modname.split('.')[-1]
eci = ExternalCompilationInfo(
include_dirs=api.include_dirs,
**kwds
)
eci = eci.convert_sources_to_files()
dirname = (udir/uniquemodulename('module')).ensure(dir=1)
soname = platform.platform.compile(
[], eci,
outputfilename=str(dirname/modname),
standalone=False)
from pypy.module.imp.importing import get_so_extension
pydname = soname.new(purebasename=modname, ext=get_so_extension(space))
soname.rename(pydname)
return str(pydname)
class AppTestCrossing:
spaceconfig = dict(usemodules=['_cppyy', '_rawffi', 'itertools'])
def setup_class(cls):
# _cppyy specific additions (note that test_dct is loaded late
# to allow the generated extension module be loaded first)
cls.w_test_dct = cls.space.newtext(test_dct)
cls.w_pre_imports = cls.space.appexec([], """():
import ctypes, _cppyy
_cppyy._post_import_startup()""") # early import of ctypes
# prevents leak-checking complaints on ctypes' statics
def setup_method(self, func):
@unwrap_spec(name='text', init='text', body='text')
def create_cdll(space, name, init, body):
# the following is loosely from test_cpyext.py import_module; it
# is copied here to be able to tweak the call to
# compile_extension_module and to get a different return result
# than in that function
code = """
#include <Python.h>
/* fix for cpython 2.7 Python.h if running tests with -A
since pypy compiles with -fvisibility-hidden */
#undef PyMODINIT_FUNC
#define PyMODINIT_FUNC RPY_EXPORTED void
%(body)s
PyMODINIT_FUNC
#if PY_MAJOR_VERSION >= 3
PyInit_%(name)s(void)
#else
init%(name)s(void)
#endif
{
%(init)s
}
""" % dict(name=name, init=init, body=body)
kwds = dict(separate_module_sources=[code])
mod = compile_extension_module(space, name, **kwds)
# explicitly load the module as a CDLL rather than as a module
from pypy.module.imp.importing import get_so_extension
fullmodname = os.path.join(
os.path.dirname(mod), name + get_so_extension(space))
return space.newtext(fullmodname)
self.w_create_cdll = self.space.wrap(interp2app(create_cdll))
def test01_build_bar_extension(self):
"""Test that builds the needed extension; runs as test to keep it loaded"""
import os, ctypes
name = 'bar'
init = """
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"bar", "Module Doc", -1, methods, NULL, NULL, NULL, NULL,
};
#endif
if (Py_IsInitialized()) {
#if PY_MAJOR_VERSION >= 3
PyObject *module = PyModule_Create(&moduledef);
#else
Py_InitModule("bar", methods);
#endif
}
"""
# note: only the symbols are needed for C, none for python
body = """
RPY_EXPORTED
long bar_unwrap(PyObject* arg)
{
return 13;//PyLong_AsLong(arg);
}
RPY_EXPORTED
PyObject* bar_wrap(long l)
{
return PyLong_FromLong(l);
}
static PyMethodDef methods[] = {
{ NULL }
};
"""
# explicitly load the module as a CDLL rather than as a module
import ctypes
self.cmodule = ctypes.CDLL(
self.create_cdll(name, init, body), ctypes.RTLD_GLOBAL)
def test02_crossing_dict(self):
"""Test availability of all needed classes in the dict"""
import _cppyy, ctypes
lib = ctypes.CDLL(self.test_dct, ctypes.RTLD_GLOBAL)
assert _cppyy.gbl.crossing == _cppyy.gbl.crossing
crossing = _cppyy.gbl.crossing
assert crossing.A == crossing.A
@py.test.mark.dont_track_allocations("fine when running standalone, though?!")
def test03_send_pyobject(self):
"""Test sending a true pyobject to C++"""
import _cppyy
crossing = _cppyy.gbl.crossing
a = crossing.A()
assert a.unwrap(13) == 13
@py.test.mark.dont_track_allocations("fine when running standalone, though?!")
def test04_send_and_receive_pyobject(self):
"""Test receiving a true pyobject from C++"""
import _cppyy
crossing = _cppyy.gbl.crossing
a = crossing.A()
assert a.wrap(41) == 41
|