This recipe is meant for Python classes wrapping ctypes bindings where a C-level finalizer must be invoked when the wrapper is destroyed. It uses weakref callbacks to avoid problems with __del__
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | """Finalization with weakrefs
This is designed for avoiding __del__.
"""
import sys
import traceback
import weakref
__author__ = "Benjamin Peterson <benjamin@python.org>"
class OwnerRef(weakref.ref):
"""A simple weakref.ref subclass, so attributes can be added."""
pass
def _run_finalizer(ref):
"""Internal weakref callback to run finalizers"""
del _finalize_refs[id(ref)]
finalizer = ref.finalizer
item = ref.item
try:
finalizer(item)
except Exception:
print("Exception running {}:".format(finalizer), file=sys.stderr)
traceback.print_exc()
_finalize_refs = {}
def track_for_finalization(owner, item, finalizer):
"""Register an object for finalization.
``owner`` is the the object which is responsible for ``item``.
``finalizer`` will be called with ``item`` as its only argument when
``owner`` is destroyed by the garbage collector.
"""
ref = OwnerRef(owner, _run_finalizer)
ref.item = item
ref.finalizer = finalizer
_finalize_refs[id(ref)] = ref
|
C libraries often require that a function be explicitly called to close opened resources and free memory. When writing Python bindings with ctypes, its hard to render this pythonically because Python's garbage collector will not call the C finalizers for you. The usual solution is to write a __del__
on the wrapper class which closes the C resources. However, __del__
has quite a list of problems and caveats. This recipe avoids the problems of __del__
by using weakref callbacks.
To use this recipe, call track_for_finalization
with owner of the C-level object, the C-level object itself, and the finalizer to call. When the owner is destroyed, the finalizer will be invoked with the C-level object as its sole argument.
The syntax is Python 3, but it should be a simple matter of using the print statement instead of the print function to use this on Python 2.
An example of a simple malloc
wrapper:
import ctypes
import ctypes.util
import gc
import finalize
libc = ctypes.CDLL(ctypes.util.find_library("c"))
libc.malloc.argtypes = (ctypes.c_size_t,)
libc.malloc.restype = ctypes.c_void_p
libc.free.argtypes = (ctypes.c_void_p,)
libc.free.restype = None
def _free_mem(mem):
print("Freeing memory at {}".format(mem))
libc.free(mem)
class MemoryChunk:
"""A wrapper for a C level chunk of memory"""
def __init__(self, size):
self.memory = libc.malloc(size)
finalize.track_for_finalization(self, self.memory, _free_mem)
def example():
mem = MemoryChunk(64)
del mem
gc.collect()
example()
This is great, definately going to help me with releasing handles in wininet calls THANKS! : )