[pypy-commit] cffi default: Finally found out the "right" way to implement ffi.gc(), in just a
arigo
noreply at buildbot.pypy.org
Thu Aug 9 11:35:07 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r793:a8efbdc7c1cc
Date: 2012-08-09 11:34 +0200
http://bitbucket.org/cffi/cffi/changeset/a8efbdc7c1cc/
Log: Finally found out the "right" way to implement ffi.gc(), in just a
few lines of Python code using weakrefs with callbacks.
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -234,6 +234,18 @@
replace_with = ' ' + replace_with
return self._backend.getcname(cdecl, replace_with)
+ def gc(self, cdata, destructor):
+ """Return a new cdata object that points to the same
+ data. Later, when this new cdata object is garbage-collected,
+ 'destructor(old_cdata_object)' will be called.
+ """
+ try:
+ gc_weakrefs = self.gc_weakrefs
+ except AttributeError:
+ from .gc_weakref import GcWeakrefs
+ gc_weakrefs = self.gc_weakrefs = GcWeakrefs(self)
+ return gc_weakrefs.build(cdata, destructor)
+
def _get_cached_btype(self, type):
try:
BType = self._cached_btypes[type]
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
--- a/cffi/backend_ctypes.py
+++ b/cffi/backend_ctypes.py
@@ -2,7 +2,7 @@
from . import model
class CTypesData(object):
- __slots__ = []
+ __slots__ = ['__weakref__']
def __init__(self, *args):
raise TypeError("cannot instantiate %r" % (self.__class__,))
diff --git a/cffi/gc_weakref.py b/cffi/gc_weakref.py
new file mode 100644
--- /dev/null
+++ b/cffi/gc_weakref.py
@@ -0,0 +1,19 @@
+from weakref import ref
+
+
+class GcWeakrefs(object):
+ # code copied and adapted from WeakKeyDictionary.
+
+ def __init__(self, ffi):
+ self.ffi = ffi
+ self.data = data = {}
+ def remove(k):
+ destructor, cdata = data.pop(k)
+ destructor(cdata)
+ self.remove = remove
+
+ def build(self, cdata, destructor):
+ # make a new cdata of the same type as the original one
+ new_cdata = self.ffi.cast(self.ffi.typeof(cdata), cdata)
+ self.data[ref(new_cdata, self.remove)] = destructor, cdata
+ return new_cdata
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -914,6 +914,14 @@
``ffi.getcname(ffi.typeof(x), "*")`` returns the string representation
of the C type "pointer to the same type than x".
+``ffi.gc(cdata, destructor)``: return a new cdata object that points to the
+same data. Later, when this new cdata object is garbage-collected,
+``destructor(old_cdata_object)`` will be called. Example of usage:
+``ptr = ffi.gc(lib.malloc(42), lib.free)``. *New in version 0.3* (together
+with the fact that any cdata object can be weakly referenced).
+
+.. "versionadded:: 0.3" --- inlined in the previous paragraph
+
Unimplemented features
----------------------
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -1279,3 +1279,18 @@
q = ffi.cast("int[3]", p)
assert q[0] == -5
assert repr(q).startswith("<cdata 'int[3]' 0x")
+
+ def test_gc(self):
+ ffi = FFI(backend=self.Backend())
+ p = ffi.new("int *", 123)
+ seen = []
+ def destructor(p1):
+ assert p1 is p
+ assert p1[0] == 123
+ seen.append(1)
+ q = ffi.gc(p, destructor)
+ import gc; gc.collect()
+ assert seen == []
+ del q
+ import gc; gc.collect(); gc.collect(); gc.collect()
+ assert seen == [1]
More information about the pypy-commit
mailing list