[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