[pypy-svn] r72419 - in pypy/trunk/pypy: doc/config module/cpyext module/cpyext/include module/cpyext/test
afa at codespeak.net
afa at codespeak.net
Fri Mar 19 19:03:00 CET 2010
Author: afa
Date: Fri Mar 19 19:02:58 2010
New Revision: 72419
Added:
pypy/trunk/pypy/doc/config/objspace.usemodules.cpyext.txt (contents, props changed)
pypy/trunk/pypy/module/cpyext/ (props changed)
pypy/trunk/pypy/module/cpyext/__init__.py (contents, props changed)
pypy/trunk/pypy/module/cpyext/api.py (contents, props changed)
pypy/trunk/pypy/module/cpyext/include/
pypy/trunk/pypy/module/cpyext/include/Python.h (contents, props changed)
pypy/trunk/pypy/module/cpyext/include/modsupport.h (contents, props changed)
pypy/trunk/pypy/module/cpyext/include/pythonrun.h (contents, props changed)
pypy/trunk/pypy/module/cpyext/modsupport.py (contents, props changed)
pypy/trunk/pypy/module/cpyext/pythonrun.py (contents, props changed)
pypy/trunk/pypy/module/cpyext/test/ (props changed)
pypy/trunk/pypy/module/cpyext/test/autopath.py
- copied unchanged from r72407, pypy/branch/kill-python-h/pypy/module/sys/test/autopath.py
pypy/trunk/pypy/module/cpyext/test/test_cpytext.py (contents, props changed)
Log:
crazy: try to import CPython modules into pypy.
So far, only the test framework is done, plus 2 functions
Added: pypy/trunk/pypy/doc/config/objspace.usemodules.cpyext.txt
==============================================================================
--- (empty file)
+++ pypy/trunk/pypy/doc/config/objspace.usemodules.cpyext.txt Fri Mar 19 19:02:58 2010
@@ -0,0 +1 @@
+Use (experimental) cpyext module, that tries to load and run CPython extension modules
Added: pypy/trunk/pypy/module/cpyext/__init__.py
==============================================================================
--- (empty file)
+++ pypy/trunk/pypy/module/cpyext/__init__.py Fri Mar 19 19:02:58 2010
@@ -0,0 +1,33 @@
+from pypy.interpreter.mixedmodule import MixedModule
+from pypy.rlib.objectmodel import we_are_translated
+import pypy.module.cpyext.api
+
+class State:
+ def __init__(self, space):
+ if not we_are_translated():
+ self.api_lib = str(api.build_bridge(space))
+ else:
+ XXX # build an import library when translating pypy.
+
+class Module(MixedModule):
+ interpleveldefs = {
+ }
+
+ appleveldefs = {
+ }
+
+ def setup_after_space_initialization(self):
+ """NOT_RPYTHON"""
+ state = self.space.fromcache(State)
+
+ def startup(self, space):
+ state = space.fromcache(State)
+ space.setattr(space.wrap(self),
+ space.wrap('api_lib'),
+ space.wrap(state.api_lib))
+
+# import these modules to register api functions by side-effect
+import pypy.module.cpyext.modsupport
+import pypy.module.cpyext.pythonrun
+
+
Added: pypy/trunk/pypy/module/cpyext/api.py
==============================================================================
--- (empty file)
+++ pypy/trunk/pypy/module/cpyext/api.py Fri Mar 19 19:02:58 2010
@@ -0,0 +1,95 @@
+from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.rpython.lltypesystem import ll2ctypes
+from pypy.rpython.annlowlevel import llhelper
+from pypy.translator.c.database import LowLevelDatabase
+from pypy.translator.tool.cbuild import ExternalCompilationInfo
+from pypy.translator import platform
+
+class ApiFunction:
+ def __init__(self, argtypes, restype, callable):
+ self.argtypes = argtypes
+ self.restype = restype
+ self.functype = lltype.Ptr(lltype.FuncType(argtypes, restype))
+ self.callable = callable
+
+def cpython_api(argtypes, restype):
+ def decorate(func):
+ FUNCTIONS[func.func_name] = ApiFunction(argtypes, restype, func)
+ return func
+ return decorate
+
+def cpython_struct(name):
+ struct = rffi.CStruct('PyMethodDef')
+ TYPES[name] = struct
+ return struct
+
+FUNCTIONS = {}
+TYPES = {}
+
+#_____________________________________________________
+# Build the bridge DLL, Allow extension DLLs to call
+# back into Pypy space functions
+def build_bridge(space):
+ db = LowLevelDatabase()
+
+ structindex = {}
+
+ # Structure declaration code
+ members = []
+ for name, func in FUNCTIONS.iteritems():
+ cdecl = db.gettype(func.functype)
+ members.append(cdecl.replace('@', name) + ';')
+ structindex[name] = len(structindex)
+ structmembers = '\n'.join(members)
+ struct_declaration_code = """\
+ struct PyPyAPI {
+ %(members)s
+ } _pypyAPI;
+ struct PyPyAPI* pypyAPI = &_pypyAPI;
+ """ % dict(members=structmembers)
+
+ # implement function callbacks
+ functions = []
+ for name, func in FUNCTIONS.iteritems():
+ restype = db.gettype(func.restype).replace('@', '')
+ args = []
+ for i, argtype in enumerate(func.argtypes):
+ arg = db.gettype(argtype)
+ arg = arg.replace('@', 'arg%d' % (i,))
+ args.append(arg)
+ args = ', '.join(args)
+ callargs = ', '.join('arg%d' % (i,) for i in range(len(func.argtypes)))
+ header = "%s %s(%s)" % (restype, name, args)
+ body = "{ return _pypyAPI.%s(%s); }" % (name, callargs)
+ functions.append('%s\n%s\n' % (header, body))
+
+ code = struct_declaration_code + '\n' + '\n'.join(functions)
+
+ # Build code and get pointer to the structure
+ eci = ExternalCompilationInfo(
+ separate_module_sources=[code],
+ export_symbols=['pypyAPI'] + list(FUNCTIONS),
+ )
+ eci = eci.convert_sources_to_files()
+ modulename = platform.platform.compile(
+ [], eci,
+ standalone=False)
+
+ # load the bridge, and init structure
+ import ctypes
+ bridge = ctypes.CDLL(str(modulename))
+ pypyAPI = ctypes.POINTER(ctypes.c_void_p).in_dll(bridge, 'pypyAPI')
+
+ def make_wrapper(callable):
+ def wrapper(*args):
+ return callable(space, *args)
+ return wrapper
+
+ # implement structure initialization code
+ for name, func in FUNCTIONS.iteritems():
+ pypyAPI[structindex[name]] = ctypes.cast(
+ ll2ctypes.lltype2ctypes(llhelper(func.functype, make_wrapper(func.callable))),
+ ctypes.c_void_p)
+
+ return modulename.new(ext='')
+
Added: pypy/trunk/pypy/module/cpyext/include/Python.h
==============================================================================
--- (empty file)
+++ pypy/trunk/pypy/module/cpyext/include/Python.h Fri Mar 19 19:02:58 2010
@@ -0,0 +1,8 @@
+#ifndef Py_PYTHON_H
+#define Py_PYTHON_H
+
+#include <stdio.h>
+#include "modsupport.h"
+#include "pythonrun.h"
+
+#endif
Added: pypy/trunk/pypy/module/cpyext/include/modsupport.h
==============================================================================
--- (empty file)
+++ pypy/trunk/pypy/module/cpyext/include/modsupport.h Fri Mar 19 19:02:58 2010
@@ -0,0 +1,2 @@
+typedef struct PyMethodDef PyMethodDef;
+void Py_InitModule(const char* name, PyMethodDef* methods);
Added: pypy/trunk/pypy/module/cpyext/include/pythonrun.h
==============================================================================
--- (empty file)
+++ pypy/trunk/pypy/module/cpyext/include/pythonrun.h Fri Mar 19 19:02:58 2010
@@ -0,0 +1,14 @@
+/* Interfaces to parse and execute pieces of python code */
+
+#ifndef Py_PYTHONRUN_H
+#define Py_PYTHONRUN_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int Py_IsInitialized(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_PYTHONRUN_H */
Added: pypy/trunk/pypy/module/cpyext/modsupport.py
==============================================================================
--- (empty file)
+++ pypy/trunk/pypy/module/cpyext/modsupport.py Fri Mar 19 19:02:58 2010
@@ -0,0 +1,19 @@
+from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.module.cpyext.api import cpython_api, cpython_struct
+from pypy.interpreter.module import Module
+
+PyMethodDef = cpython_struct('PyMethodDef')
+
+def PyImport_AddModule(space, name):
+ w_name = space.wrap(name)
+ w_mod = space.wrap(Module(space, w_name))
+
+ w_modules = space.sys.get('modules')
+ space.setitem(w_modules, w_name, w_mod)
+ return w_mod
+
+ at cpython_api([rffi.CCHARP, lltype.Ptr(PyMethodDef)], lltype.Void)
+def Py_InitModule(space, name, methods):
+ name = rffi.charp2str(name)
+ PyImport_AddModule(space, name)
+ assert not methods # For the moment
Added: pypy/trunk/pypy/module/cpyext/pythonrun.py
==============================================================================
--- (empty file)
+++ pypy/trunk/pypy/module/cpyext/pythonrun.py Fri Mar 19 19:02:58 2010
@@ -0,0 +1,6 @@
+from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.module.cpyext.api import cpython_api
+
+ at cpython_api([], lltype.Signed)
+def Py_IsInitialized(space):
+ return 1
Added: pypy/trunk/pypy/module/cpyext/test/test_cpytext.py
==============================================================================
--- (empty file)
+++ pypy/trunk/pypy/module/cpyext/test/test_cpytext.py Fri Mar 19 19:02:58 2010
@@ -0,0 +1,67 @@
+import py, autopath
+
+from pypy.conftest import gettestobjspace
+from pypy.interpreter.error import OperationError
+from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.translator.tool.cbuild import ExternalCompilationInfo
+from pypy.translator import platform
+from pypy.module.cpyext import api
+
+class TestApi():
+ def test_signature(self):
+ assert 'Py_InitModule' in api.FUNCTIONS
+ assert api.FUNCTIONS['Py_InitModule'].argtypes == [
+ rffi.CCHARP, lltype.Ptr(api.TYPES['PyMethodDef'])]
+ assert api.FUNCTIONS['Py_InitModule'].restype == lltype.Void
+
+def compile_module(name, code, libraries=()):
+ include_dir = py.path.local(autopath.pypydir).join(
+ 'module', 'cpyext', 'include')
+ eci = ExternalCompilationInfo(
+ separate_module_sources=[code],
+ export_symbols=['init%s' % (name,)],
+ include_dirs=[include_dir],
+ libraries=libraries,
+ )
+ eci = eci.convert_sources_to_files()
+ soname = platform.platform.compile(
+ [], eci,
+ standalone=False)
+ return str(soname)
+
+class AppTestCpythonExtension:
+ def setup_class(cls):
+ cls.api_library = api.build_bridge(cls.space)
+
+ def import_module(self, name, init, body=''):
+ code = """
+ #include <Python.h>
+ %(body)s
+
+ void init%(name)s(void) {
+ %(init)s
+ }
+ """ % dict(name=name, init=init, body=body)
+ mod = compile_module(name, code, libraries=[self.api_library])
+ import ctypes
+ initfunc = ctypes.CDLL(mod)['init%s' % (name,)]
+ initfunc()
+
+ def setup_method(self, func):
+ self.w_import_module = self.space.wrap(self.import_module)
+
+ def teardown_method(self, func):
+ try:
+ self.space.delitem(self.space.sys.get('modules'),
+ self.space.wrap('foo'))
+ except OperationError:
+ pass
+
+ def test_createmodule(self):
+ import sys
+ init = """
+ if (Py_IsInitialized())
+ Py_InitModule("foo", NULL);
+ """
+ self.import_module(name='foo', init=init)
+ assert 'foo' in sys.modules
More information about the Pypy-commit
mailing list