Author: Ronan Lamy <[email protected]>
Branch: 
Changeset: r87478:298b37893194
Date: 2016-09-30 18:53 +0100
http://bitbucket.org/pypy/pypy/changeset/298b37893194/

Log:    Merge branch 'test-cpyext'

diff --git a/pypy/module/cpyext/test/conftest.py 
b/pypy/module/cpyext/test/conftest.py
--- a/pypy/module/cpyext/test/conftest.py
+++ b/pypy/module/cpyext/test/conftest.py
@@ -2,6 +2,8 @@
 import pytest
 
 def pytest_configure(config):
+    if config.option.runappdirect:
+        return
     from pypy.tool.pytest.objspace import gettestobjspace
     # For some reason (probably a ll2ctypes cache issue on linux64)
     # it's necessary to run "import time" at least once before any
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
@@ -5,7 +5,7 @@
 import py, pytest
 
 from pypy import pypydir
-from pypy.interpreter import gateway
+from pypy.interpreter.gateway import unwrap_spec, interp2app
 from rpython.rtyper.lltypesystem import lltype, ll2ctypes
 from rpython.translator.gensupp import uniquemodulename
 from rpython.tool.udir import udir
@@ -18,7 +18,8 @@
 
 from .support import c_compile
 
-only_pypy ="config.option.runappdirect and '__pypy__' not in 
sys.builtin_module_names" 
+HERE = py.path.local(pypydir) / 'module' / 'cpyext' / 'test'
+only_pypy ="config.option.runappdirect and '__pypy__' not in 
sys.builtin_module_names"
 
 @api.cpython_api([], api.PyObject)
 def PyPy_Crash1(space):
@@ -42,19 +43,6 @@
         files.append(filename)
     return files
 
-def create_so(modname, include_dirs, source_strings=None, source_files=None,
-        compile_extra=None, link_extra=None, libraries=None):
-    dirname = (udir/uniquemodulename('module')).ensure(dir=1)
-    if source_strings:
-        assert not source_files
-        files = convert_sources_to_files(source_strings, dirname)
-        source_files = files
-    soname = c_compile(source_files, outputfilename=str(dirname/modname),
-        compile_extra=compile_extra, link_extra=link_extra,
-        include_dirs=include_dirs,
-        libraries=libraries)
-    return soname
-
 class SystemCompilationInfo(object):
     """Bundles all the generic information required to compile extensions.
 
@@ -68,6 +56,88 @@
         self.extra_libs = extra_libs
         self.ext = ext
 
+    def compile_extension_module(self, name, include_dirs=None,
+            source_files=None, source_strings=None):
+        """
+        Build an extension module and return the filename of the resulting
+        native code file.
+
+        name 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).
+        """
+        include_dirs = include_dirs or []
+        modname = name.split('.')[-1]
+        dirname = (udir/uniquemodulename('module')).ensure(dir=1)
+        if source_strings:
+            assert not source_files
+            files = convert_sources_to_files(source_strings, dirname)
+            source_files = files
+        soname = c_compile(source_files, outputfilename=str(dirname/modname),
+            compile_extra=self.compile_extra,
+            link_extra=self.link_extra,
+            include_dirs=self.include_extra + include_dirs,
+            libraries=self.extra_libs)
+        pydname = soname.new(purebasename=modname, ext=self.ext)
+        soname.rename(pydname)
+        return str(pydname)
+
+    def import_module(self, name, init=None, body='', filename=None,
+            include_dirs=None, PY_SSIZE_T_CLEAN=False):
+        """
+        init specifies the overall template of the module.
+
+        if init is None, the module source will be loaded from a file in this
+        test directory, give a name given by the filename parameter.
+
+        if filename is None, the module name will be used to construct the
+        filename.
+        """
+        if init is not None:
+            code = make_source(name, init, body, PY_SSIZE_T_CLEAN)
+            kwds = dict(source_strings=[code])
+        else:
+            assert not PY_SSIZE_T_CLEAN
+            if filename is None:
+                filename = name
+            filename = HERE / (filename + ".c")
+            kwds = dict(source_files=[filename])
+        mod = self.compile_extension_module(
+            name, include_dirs=include_dirs, **kwds)
+        return self.load_module(mod, name)
+
+    def import_extension(self, modname, functions, prologue="",
+            include_dirs=None, more_init="", PY_SSIZE_T_CLEAN=False):
+        body = prologue + make_methods(functions, modname)
+        init = """Py_InitModule("%s", methods);""" % (modname,)
+        if more_init:
+            init += more_init
+        return self.import_module(
+            name=modname, init=init, body=body, include_dirs=include_dirs,
+            PY_SSIZE_T_CLEAN=PY_SSIZE_T_CLEAN)
+
+
+class ExtensionCompiler(SystemCompilationInfo):
+    """Extension compiler for appdirect mode"""
+    def load_module(space, mod, name):
+        import imp
+        return imp.load_dynamic(name, mod)
+
+class SpaceCompiler(SystemCompilationInfo):
+    """Extension compiler for regular (untranslated PyPy) mode"""
+    def __init__(self, space, *args, **kwargs):
+        self.space = space
+        SystemCompilationInfo.__init__(self, *args, **kwargs)
+
+    def load_module(self, mod, name):
+        space = self.space
+        api.load_extension_module(space, mod, name)
+        return space.getitem(
+            space.sys.get('modules'), space.wrap(name))
+
+
 def get_cpyext_info(space):
     from pypy.module.imp.importing import get_so_extension
     state = space.fromcache(State)
@@ -88,7 +158,7 @@
             link_extra = ["-g"]
         else:
             compile_extra = link_extra = None
-    return SystemCompilationInfo(
+    return SpaceCompiler(space,
         include_extra=api.include_dirs,
         compile_extra=compile_extra,
         link_extra=link_extra,
@@ -96,30 +166,6 @@
         ext=get_so_extension(space))
 
 
-def compile_extension_module(sys_info, modname, include_dirs=[],
-        source_files=None, source_strings=None):
-    """
-    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).
-    """
-    modname = modname.split('.')[-1]
-    soname = create_so(modname,
-        include_dirs=sys_info.include_extra + include_dirs,
-        source_files=source_files,
-        source_strings=source_strings,
-        compile_extra=sys_info.compile_extra,
-        link_extra=sys_info.link_extra,
-        libraries=sys_info.extra_libs)
-    pydname = soname.new(purebasename=modname, ext=sys_info.ext)
-    soname.rename(pydname)
-    return str(pydname)
-
 def get_so_suffix():
     from imp import get_suffixes, C_EXTENSION
     for suffix, mode, typ in get_suffixes():
@@ -142,12 +188,60 @@
             "-O0", "-g", "-Werror=implicit-function-declaration", "-fPIC"]
         link_extra = None
     ext = get_so_suffix()
-    return SystemCompilationInfo(
+    return ExtensionCompiler(
         include_extra=[get_python_inc()],
         compile_extra=compile_extra,
         link_extra=link_extra,
         ext=get_so_suffix())
 
+def make_methods(functions, modname):
+    methods_table = []
+    codes = []
+    for funcname, flags, code in functions:
+        cfuncname = "%s_%s" % (modname, funcname)
+        methods_table.append(
+            "{\"%s\", %s, %s}," % (funcname, cfuncname, flags))
+        func_code = """
+        static PyObject* %s(PyObject* self, PyObject* args)
+        {
+        %s
+        }
+        """ % (cfuncname, code)
+        codes.append(func_code)
+
+    body = "\n".join(codes) + """
+    static PyMethodDef methods[] = {
+    %s
+    { NULL }
+    };
+    """ % ('\n'.join(methods_table),)
+    return body
+
+def make_source(name, init, body, PY_SSIZE_T_CLEAN):
+    code = """
+    %(PY_SSIZE_T_CLEAN)s
+    #include <Python.h>
+    /* fix for cpython 2.7 Python.h if running tests with -A
+        since pypy compiles with -fvisibility-hidden */
+    #undef PyMODINIT_FUNC
+    #ifdef __GNUC__
+    #  define RPY_EXPORTED extern __attribute__((visibility("default")))
+    #else
+    #  define RPY_EXPORTED extern __declspec(dllexport)
+    #endif
+    #define PyMODINIT_FUNC RPY_EXPORTED void
+
+    %(body)s
+
+    PyMODINIT_FUNC
+    init%(name)s(void) {
+    %(init)s
+    }
+    """ % dict(name=name, init=init, body=body,
+            PY_SSIZE_T_CLEAN='#define PY_SSIZE_T_CLEAN'
+                if PY_SSIZE_T_CLEAN else '')
+    return code
+
 
 def freeze_refcnts(self):
     rawrefcount._dont_free_any_more()
@@ -159,22 +253,6 @@
     #state.print_refcounts()
     self.frozen_ll2callocations = set(ll2ctypes.ALLOCATED.values())
 
-class FakeSpace(object):
-    """Like TinyObjSpace, but different"""
-    def __init__(self, config):
-        self.config = config
-
-    def passthrough(self, arg):
-        return arg
-    listview = passthrough
-    str_w = passthrough
-
-    def unwrap(self, args):
-        try:
-            return args.str_w(None)
-        except:
-            return args
-
 class LeakCheckingTest(object):
     """Base class for all cpyext tests."""
     spaceconfig = dict(usemodules=['cpyext', 'thread', '_rawffi', 'array',
@@ -265,9 +343,12 @@
             cls.w_libc = cls.space.wrap(get_libc_name())
 
     def setup_method(self, meth):
-        freeze_refcnts(self)
+        if not self.runappdirect:
+            freeze_refcnts(self)
 
     def teardown_method(self, meth):
+        if self.runappdirect:
+            return
         self.cleanup_references(self.space)
         # XXX: like AppTestCpythonExtensionBase.teardown_method:
         # find out how to disable check_and_print_leaks() if the
@@ -293,21 +374,42 @@
             skip("Windows Python >= 2.6 only")
         assert isinstance(sys.dllhandle, int)
 
+
+def _unwrap_include_dirs(space, w_include_dirs):
+    if w_include_dirs is None:
+        return None
+    else:
+        return [space.str_w(s) for s in space.listview(w_include_dirs)]
+
+def debug_collect(space):
+    rawrefcount._collect()
+
 class AppTestCpythonExtensionBase(LeakCheckingTest):
 
     def setup_class(cls):
         space = cls.space
-        space.getbuiltinmodule("cpyext")
-        # 'import os' to warm up reference counts
-        w_import = space.builtin.getdictvalue(space, '__import__')
-        space.call_function(w_import, space.wrap("os"))
-        #state = cls.space.fromcache(RefcountState) ZZZ
-        #state.non_heaptypes_w[:] = []
+        cls.w_here = space.wrap(str(HERE))
         if not cls.runappdirect:
             cls.w_runappdirect = space.wrap(cls.runappdirect)
+            space.getbuiltinmodule("cpyext")
+            # 'import os' to warm up reference counts
+            w_import = space.builtin.getdictvalue(space, '__import__')
+            space.call_function(w_import, space.wrap("os"))
+            #state = cls.space.fromcache(RefcountState) ZZZ
+            #state.non_heaptypes_w[:] = []
+            cls.w_debug_collect = space.wrap(interp2app(debug_collect))
+
+    def record_imported_module(self, name):
+        """
+        Record a module imported in a test so that it can be cleaned up in
+        teardown before the check for leaks is done.
+
+        name gives the name of the module in the space's sys.modules.
+        """
+        self.imported_module_names.append(name)
 
     def setup_method(self, func):
-        @gateway.unwrap_spec(name=str)
+        @unwrap_spec(name=str)
         def compile_module(space, name,
                            w_source_files=None,
                            w_source_strings=None):
@@ -322,172 +424,68 @@
                 source_strings = space.listview_bytes(w_source_strings)
             else:
                 source_strings = None
-            pydname = compile_extension_module(
-                self.sys_info, name,
+            pydname = self.sys_info.compile_extension_module(
+                name,
                 source_files=source_files,
                 source_strings=source_strings)
+
+            # hackish, but tests calling compile_module() always end up
+            # importing the result
+            self.record_imported_module(name)
+
             return space.wrap(pydname)
 
-        @gateway.unwrap_spec(name=str, init='str_or_None', body=str,
-                     load_it=bool, filename='str_or_None',
-                     PY_SSIZE_T_CLEAN=bool)
-        def import_module(space, name, init=None, body='', load_it=True,
+        @unwrap_spec(name=str, init='str_or_None', body=str,
+                     filename='str_or_None', PY_SSIZE_T_CLEAN=bool)
+        def import_module(space, name, init=None, body='',
                           filename=None, w_include_dirs=None,
                           PY_SSIZE_T_CLEAN=False):
-            """
-            init specifies the overall template of the module.
+            include_dirs = _unwrap_include_dirs(space, w_include_dirs)
+            w_result = self.sys_info.import_module(
+                name, init, body, filename, include_dirs, PY_SSIZE_T_CLEAN)
+            self.record_imported_module(name)
+            return w_result
 
-            if init is None, the module source will be loaded from a file in 
this
-            test direcory, give a name given by the filename parameter.
 
-            if filename is None, the module name will be used to construct the
-            filename.
-            """
-            if w_include_dirs is None:
-                include_dirs = []
-            else:
-                include_dirs = [space.str_w(s) for s in 
space.listview(w_include_dirs)]
-            if init is not None:
-                code = """
-                %(PY_SSIZE_T_CLEAN)s
-                #include <Python.h>
-                /* fix for cpython 2.7 Python.h if running tests with -A
-                   since pypy compiles with -fvisibility-hidden */
-                #undef PyMODINIT_FUNC
-                #ifdef __GNUC__
-                #  define RPY_EXPORTED extern 
__attribute__((visibility("default")))
-                #else
-                #  define RPY_EXPORTED extern __declspec(dllexport)
-                #endif
-                #define PyMODINIT_FUNC RPY_EXPORTED void
+        @unwrap_spec(mod=str, name=str)
+        def load_module(space, mod, name):
+            return self.sys_info.load_module(mod, name)
 
-                %(body)s
-
-                PyMODINIT_FUNC
-                init%(name)s(void) {
-                %(init)s
-                }
-                """ % dict(name=name, init=init, body=body,
-                           PY_SSIZE_T_CLEAN='#define PY_SSIZE_T_CLEAN'
-                                            if PY_SSIZE_T_CLEAN else '')
-                kwds = dict(source_strings=[code])
-            else:
-                assert not PY_SSIZE_T_CLEAN
-                if filename is None:
-                    filename = name
-                filename = py.path.local(pypydir) / 'module' \
-                        / 'cpyext'/ 'test' / (filename + ".c")
-                kwds = dict(source_files=[filename])
-            mod = compile_extension_module(self.sys_info, name,
-                    include_dirs=include_dirs, **kwds)
-
-            if load_it:
-                if self.runappdirect:
-                    import imp
-                    return imp.load_dynamic(name, mod)
-                else:
-                    api.load_extension_module(space, mod, name)
-                    self.imported_module_names.append(name)
-                    return space.getitem(
-                        space.sys.get('modules'),
-                        space.wrap(name))
-            else:
-                path = os.path.dirname(mod)
-                if self.runappdirect:
-                    return path
-                else:
-                    return space.wrap(path)
-
-        @gateway.unwrap_spec(mod=str, name=str)
-        def reimport_module(space, mod, name):
-            if self.runappdirect:
-                import imp
-                return imp.load_dynamic(name, mod)
-            else:
-                api.load_extension_module(space, mod, name)
-            return space.getitem(
-                space.sys.get('modules'),
-                space.wrap(name))
-
-        @gateway.unwrap_spec(modname=str, prologue=str,
+        @unwrap_spec(modname=str, prologue=str,
                              more_init=str, PY_SSIZE_T_CLEAN=bool)
         def import_extension(space, modname, w_functions, prologue="",
                              w_include_dirs=None, more_init="", 
PY_SSIZE_T_CLEAN=False):
             functions = space.unwrap(w_functions)
-            methods_table = []
-            codes = []
-            for funcname, flags, code in functions:
-                cfuncname = "%s_%s" % (modname, funcname)
-                methods_table.append("{\"%s\", %s, %s}," %
-                                     (funcname, cfuncname, flags))
-                func_code = """
-                static PyObject* %s(PyObject* self, PyObject* args)
-                {
-                %s
-                }
-                """ % (cfuncname, code)
-                codes.append(func_code)
-
-            body = prologue + "\n".join(codes) + """
-            static PyMethodDef methods[] = {
-            %s
-            { NULL }
-            };
-            """ % ('\n'.join(methods_table),)
-            init = """Py_InitModule("%s", methods);""" % (modname,)
-            if more_init:
-                init += more_init
-            return import_module(space, name=modname, init=init, body=body,
-                                 w_include_dirs=w_include_dirs,
-                                 PY_SSIZE_T_CLEAN=PY_SSIZE_T_CLEAN)
-
-        @gateway.unwrap_spec(name=str)
-        def record_imported_module(name):
-            """
-            Record a module imported in a test so that it can be cleaned up in
-            teardown before the check for leaks is done.
-
-            name gives the name of the module in the space's sys.modules.
-            """
-            self.imported_module_names.append(name)
-
-        def debug_collect(space):
-            rawrefcount._collect()
+            include_dirs = _unwrap_include_dirs(space, w_include_dirs)
+            w_result = self.sys_info.import_extension(
+                modname, functions, prologue, include_dirs, more_init,
+                PY_SSIZE_T_CLEAN)
+            self.record_imported_module(modname)
+            return w_result
 
         # A list of modules which the test caused to be imported (in
         # self.space).  These will be cleaned up automatically in teardown.
         self.imported_module_names = []
 
         if self.runappdirect:
-            fake = FakeSpace(self.space.config)
-            def interp2app(func):
-                def run(*args, **kwargs):
-                    for k in kwargs.keys():
-                        if k not in func.unwrap_spec and not 
k.startswith('w_'):
-                            v = kwargs.pop(k)
-                            kwargs['w_' + k] = v
-                    return func(fake, *args, **kwargs)
-                return run
-            def wrap(func):
-                return func
             self.sys_info = get_sys_info_app()
+            self.compile_module = self.sys_info.compile_extension_module
+            self.load_module = self.sys_info.load_module
+            self.import_module = self.sys_info.import_module
+            self.import_extension = self.sys_info.import_extension
         else:
-            interp2app = gateway.interp2app
             wrap = self.space.wrap
             self.sys_info = get_cpyext_info(self.space)
-        self.w_compile_module = wrap(interp2app(compile_module))
-        self.w_import_module = wrap(interp2app(import_module))
-        self.w_reimport_module = wrap(interp2app(reimport_module))
-        self.w_import_extension = wrap(interp2app(import_extension))
-        self.w_record_imported_module = 
wrap(interp2app(record_imported_module))
-        self.w_here = wrap(str(py.path.local(pypydir)) + 
'/module/cpyext/test/')
-        self.w_debug_collect = wrap(interp2app(debug_collect))
+            self.w_compile_module = wrap(interp2app(compile_module))
+            self.w_load_module = wrap(interp2app(load_module))
+            self.w_import_module = wrap(interp2app(import_module))
+            self.w_import_extension = wrap(interp2app(import_extension))
 
-        # create the file lock before we count allocations
-        self.space.call_method(self.space.sys.get("stdout"), "flush")
+            # create the file lock before we count allocations
+            self.space.call_method(self.space.sys.get("stdout"), "flush")
 
-        freeze_refcnts(self)
-        #self.check_and_print_leaks()
+            freeze_refcnts(self)
+            #self.check_and_print_leaks()
 
     def unimport_module(self, name):
         """
@@ -498,6 +496,8 @@
         self.space.delitem(w_modules, w_name)
 
     def teardown_method(self, func):
+        if self.runappdirect:
+            return
         for name in self.imported_module_names:
             self.unimport_module(name)
         self.cleanup_references(self.space)
@@ -632,19 +632,15 @@
         If `cherry.date` is an extension module which imports `apple.banana`,
         the latter is added to `sys.modules` for the `"apple.banana"` key.
         """
-        if self.runappdirect:
-            skip('record_imported_module not supported in runappdirect mode')
+        import sys, types, os
         # Build the extensions.
         banana = self.compile_module(
-            "apple.banana", source_files=[self.here + 'banana.c'])
-        self.record_imported_module("apple.banana")
+            "apple.banana", source_files=[os.path.join(self.here, 'banana.c')])
         date = self.compile_module(
-            "cherry.date", source_files=[self.here + 'date.c'])
-        self.record_imported_module("cherry.date")
+            "cherry.date", source_files=[os.path.join(self.here, 'date.c')])
 
         # Set up some package state so that the extensions can actually be
         # imported.
-        import sys, types, os
         cherry = sys.modules['cherry'] = types.ModuleType('cherry')
         cherry.__path__ = [os.path.dirname(date)]
 
@@ -652,7 +648,6 @@
         apple.__path__ = [os.path.dirname(banana)]
 
         import cherry.date
-        import apple.banana
 
         assert sys.modules['apple.banana'].__name__ == 'apple.banana'
         assert sys.modules['cherry.date'].__name__ == 'cherry.date'
@@ -989,7 +984,7 @@
             f.write('not again!\n')
             f.close()
             m1 = sys.modules['foo']
-            m2 = self.reimport_module(m1.__file__, name='foo')
+            m2 = self.load_module(m1.__file__, name='foo')
             assert m1 is m2
             assert m1 is sys.modules['foo']
 
diff --git a/pypy/module/cpyext/test/test_import.py 
b/pypy/module/cpyext/test/test_import.py
--- a/pypy/module/cpyext/test/test_import.py
+++ b/pypy/module/cpyext/test/test_import.py
@@ -1,6 +1,6 @@
 from pypy.module.cpyext.test.test_api import BaseApiTest
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
-from rpython.rtyper.lltypesystem import rffi, lltype
+from rpython.rtyper.lltypesystem import rffi
 
 class TestImport(BaseApiTest):
     def test_import(self, space, api):
@@ -39,9 +39,9 @@
 
 class AppTestImportLogic(AppTestCpythonExtensionBase):
     def test_import_logic(self):
-        path = self.import_module(name='test_import_module', load_it=False)
-        import sys
-        sys.path.append(path)
+        import sys, os
+        path = self.compile_module('test_import_module',
+            source_files=[os.path.join(self.here, 'test_import_module.c')])
+        sys.path.append(os.path.dirname(path))
         import test_import_module
         assert test_import_module.TEST is None
-
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to