[pypy-commit] pypy default: import cffi/307f969f209f

arigo noreply at buildbot.pypy.org
Mon Jul 6 18:36:39 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r78470:98ab85a7127c
Date: 2015-07-06 18:36 +0200
http://bitbucket.org/pypy/pypy/changeset/98ab85a7127c/

Log:	import cffi/307f969f209f

diff --git a/lib_pypy/cffi/api.py b/lib_pypy/cffi/api.py
--- a/lib_pypy/cffi/api.py
+++ b/lib_pypy/cffi/api.py
@@ -236,6 +236,30 @@
             cdecl = self._typeof(cdecl)
         return self._backend.newp(cdecl, init)
 
+    def new_allocator(self, alloc=None, free=None,
+                      should_clear_after_alloc=True):
+        """Return a new allocator, i.e. a function that behaves like ffi.new()
+        but uses the provided low-level 'alloc' and 'free' functions.
+
+        'alloc' is called with the size as argument.  If it returns NULL, a
+        MemoryError is raised.  'free' is called with the result of 'alloc'
+        as argument.  Both can be either Python function or directly C
+        functions.  If 'free' is None, then no free function is called.
+        If both 'alloc' and 'free' are None, the default is used.
+
+        If 'should_clear_after_alloc' is set to False, then the memory
+        returned by 'alloc' is assumed to be already cleared (or you are
+        fine with garbage); otherwise CFFI will clear it.
+        """
+        compiled_ffi = self._backend.FFI()
+        allocator = compiled_ffi.new_allocator(alloc, free,
+                                               should_clear_after_alloc)
+        def allocate(cdecl, init=None):
+            if isinstance(cdecl, basestring):
+                cdecl = self._typeof(cdecl)
+            return allocator(cdecl, init)
+        return allocate
+
     def cast(self, cdecl, source):
         """Similar to a C cast: returns an instance of the named C
         type initialized with the given 'source'.  The source is
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py
@@ -58,6 +58,77 @@
         assert tb.tb_frame.f_code.co_name == 'cb'
         assert tb.tb_frame.f_locals['n'] == 234
 
+    def test_ffi_new_allocator_2(self):
+        ffi = FFI(backend=self.Backend())
+        seen = []
+        def myalloc(size):
+            seen.append(size)
+            return ffi.new("char[]", "X" * size)
+        def myfree(raw):
+            seen.append(raw)
+        alloc1 = ffi.new_allocator(myalloc, myfree)
+        alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree,
+                                   should_clear_after_alloc=False)
+        p1 = alloc1("int[10]")
+        p2 = alloc2("int[]", 10)
+        assert seen == [40, 40]
+        assert ffi.typeof(p1) == ffi.typeof("int[10]")
+        assert ffi.sizeof(p1) == 40
+        assert ffi.typeof(p2) == ffi.typeof("int[]")
+        assert ffi.sizeof(p2) == 40
+        assert p1[5] == 0
+        assert p2[6] == ord('X') * 0x01010101
+        raw1 = ffi.cast("char *", p1)
+        raw2 = ffi.cast("char *", p2)
+        del p1, p2
+        retries = 0
+        while len(seen) != 4:
+            retries += 1
+            assert retries <= 5
+            import gc; gc.collect()
+        assert seen == [40, 40, raw1, raw2]
+        assert repr(seen[2]) == "<cdata 'char[]' owning 41 bytes>"
+        assert repr(seen[3]) == "<cdata 'char[]' owning 41 bytes>"
+
+    def test_ffi_new_allocator_3(self):
+        ffi = FFI(backend=self.Backend())
+        seen = []
+        def myalloc(size):
+            seen.append(size)
+            return ffi.new("char[]", "X" * size)
+        alloc1 = ffi.new_allocator(myalloc)    # no 'free'
+        p1 = alloc1("int[10]")
+        assert seen == [40]
+        assert ffi.typeof(p1) == ffi.typeof("int[10]")
+        assert ffi.sizeof(p1) == 40
+        assert p1[5] == 0
+
+    def test_ffi_new_allocator_4(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(TypeError, ffi.new_allocator, free=lambda x: None)
+        #
+        def myalloc2(size):
+            raise LookupError
+        alloc2 = ffi.new_allocator(myalloc2)
+        py.test.raises(LookupError, alloc2, "int[5]")
+        #
+        def myalloc3(size):
+            return 42
+        alloc3 = ffi.new_allocator(myalloc3)
+        e = py.test.raises(TypeError, alloc3, "int[5]")
+        assert str(e.value) == "alloc() must return a cdata object (got int)"
+        #
+        def myalloc4(size):
+            return ffi.cast("int", 42)
+        alloc4 = ffi.new_allocator(myalloc4)
+        e = py.test.raises(TypeError, alloc4, "int[5]")
+        assert str(e.value) == "alloc() must return a cdata pointer, not 'int'"
+        #
+        def myalloc5(size):
+            return ffi.NULL
+        alloc5 = ffi.new_allocator(myalloc5)
+        py.test.raises(MemoryError, alloc5, "int[5]")
+
 
 class TestBitfield:
     def check(self, source, expected_ofs_y, expected_align, expected_size):
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
@@ -225,3 +225,95 @@
     n = (1 << 29) + 42
     code, message = ffi.getwinerror(code=n)
     assert code == n
+
+def test_ffi_new_allocator_1():
+    ffi = _cffi1_backend.FFI()
+    alloc1 = ffi.new_allocator()
+    alloc2 = ffi.new_allocator(should_clear_after_alloc=False)
+    for retry in range(100):
+        p1 = alloc1("int[10]")
+        p2 = alloc2("int[10]")
+        combination = 0
+        for i in range(10):
+            assert p1[i] == 0
+            combination |= p2[i]
+            p1[i] = -42
+            p2[i] = -43
+        if combination != 0:
+            break
+        del p1, p2
+        import gc; gc.collect()
+    else:
+        raise AssertionError("cannot seem to get an int[10] not "
+                             "completely cleared")
+
+def test_ffi_new_allocator_2():
+    ffi = _cffi1_backend.FFI()
+    seen = []
+    def myalloc(size):
+        seen.append(size)
+        return ffi.new("char[]", "X" * size)
+    def myfree(raw):
+        seen.append(raw)
+    alloc1 = ffi.new_allocator(myalloc, myfree)
+    alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree,
+                               should_clear_after_alloc=False)
+    p1 = alloc1("int[10]")
+    p2 = alloc2("int[]", 10)
+    assert seen == [40, 40]
+    assert ffi.typeof(p1) == ffi.typeof("int[10]")
+    assert ffi.sizeof(p1) == 40
+    assert ffi.typeof(p2) == ffi.typeof("int[]")
+    assert ffi.sizeof(p2) == 40
+    assert p1[5] == 0
+    assert p2[6] == ord('X') * 0x01010101
+    raw1 = ffi.cast("char *", p1)
+    raw2 = ffi.cast("char *", p2)
+    del p1, p2
+    retries = 0
+    while len(seen) != 4:
+        retries += 1
+        assert retries <= 5
+        import gc; gc.collect()
+    assert seen == [40, 40, raw1, raw2]
+    assert repr(seen[2]) == "<cdata 'char[]' owning 41 bytes>"
+    assert repr(seen[3]) == "<cdata 'char[]' owning 41 bytes>"
+
+def test_ffi_new_allocator_3():
+    ffi = _cffi1_backend.FFI()
+    seen = []
+    def myalloc(size):
+        seen.append(size)
+        return ffi.new("char[]", "X" * size)
+    alloc1 = ffi.new_allocator(myalloc)    # no 'free'
+    p1 = alloc1("int[10]")
+    assert seen == [40]
+    assert ffi.typeof(p1) == ffi.typeof("int[10]")
+    assert ffi.sizeof(p1) == 40
+    assert p1[5] == 0
+
+def test_ffi_new_allocator_4():
+    ffi = _cffi1_backend.FFI()
+    py.test.raises(TypeError, ffi.new_allocator, free=lambda x: None)
+    #
+    def myalloc2(size):
+        raise LookupError
+    alloc2 = ffi.new_allocator(myalloc2)
+    py.test.raises(LookupError, alloc2, "int[5]")
+    #
+    def myalloc3(size):
+        return 42
+    alloc3 = ffi.new_allocator(myalloc3)
+    e = py.test.raises(TypeError, alloc3, "int[5]")
+    assert str(e.value) == "alloc() must return a cdata object (got int)"
+    #
+    def myalloc4(size):
+        return ffi.cast("int", 42)
+    alloc4 = ffi.new_allocator(myalloc4)
+    e = py.test.raises(TypeError, alloc4, "int[5]")
+    assert str(e.value) == "alloc() must return a cdata pointer, not 'int'"
+    #
+    def myalloc5(size):
+        return ffi.NULL
+    alloc5 = ffi.new_allocator(myalloc5)
+    py.test.raises(MemoryError, alloc5, "int[5]")


More information about the pypy-commit mailing list