new python cleanups
seems to work now, try an operation next
This commit is contained in:
parent
3628692799
commit
e346187043
105
python/finalizable.py
Normal file
105
python/finalizable.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import weakref
|
||||||
|
|
||||||
|
class Finalizable(object):
|
||||||
|
"""
|
||||||
|
Base class enabling the use a __finalize__ method without all the
|
||||||
|
problems associated with __del__ and reference cycles.
|
||||||
|
|
||||||
|
If you call enable_finalizer(), it will call __finalize__
|
||||||
|
with a single "ghost instance" argument after the object has been
|
||||||
|
deleted. Creation of this "ghost instance" does not involve calling
|
||||||
|
the __init__ method, but merely copying the attributes whose names
|
||||||
|
were given as arguments to enable_finalizer().
|
||||||
|
|
||||||
|
A Finalizable can be part of any reference cycle, but you must be careful
|
||||||
|
that the attributes given to enable_finalizer() don't reference back to
|
||||||
|
self, otherwise self will be immortal.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__wr_map = {}
|
||||||
|
__wr_id = None
|
||||||
|
|
||||||
|
def bind_finalizer(self, *attrs):
|
||||||
|
"""
|
||||||
|
Enable __finalize__ on the current instance.
|
||||||
|
The __finalize__ method will be called with a "ghost instance" as
|
||||||
|
single argument.
|
||||||
|
This ghost instance is constructed from the attributes whose names
|
||||||
|
are given to bind_finalizer(), *at the time bind_finalizer() is called*.
|
||||||
|
"""
|
||||||
|
cls = type(self)
|
||||||
|
ghost = object.__new__(cls)
|
||||||
|
ghost.__dict__.update((k, getattr(self, k)) for k in attrs)
|
||||||
|
cls_wr_map = cls.__wr_map
|
||||||
|
def _finalize(ref):
|
||||||
|
try:
|
||||||
|
ghost.__finalize__()
|
||||||
|
finally:
|
||||||
|
del cls_wr_map[id_ref]
|
||||||
|
ref = weakref.ref(self, _finalize)
|
||||||
|
id_ref = id(ref)
|
||||||
|
cls_wr_map[id_ref] = ref
|
||||||
|
self.__wr_id = id_ref
|
||||||
|
|
||||||
|
def remove_finalizer(self):
|
||||||
|
"""
|
||||||
|
Disable __finalize__, provided it has been enabled.
|
||||||
|
"""
|
||||||
|
if self.__wr_id:
|
||||||
|
cls = type(self)
|
||||||
|
cls_wr_map = cls.__wr_map
|
||||||
|
del cls_wr_map[self.__wr_id]
|
||||||
|
del self.__wr_id
|
||||||
|
|
||||||
|
|
||||||
|
class TransactionBase(Finalizable):
|
||||||
|
"""
|
||||||
|
A convenience base class to write transaction-like objects,
|
||||||
|
with automatic rollback() on object destruction if required.
|
||||||
|
"""
|
||||||
|
|
||||||
|
finished = False
|
||||||
|
|
||||||
|
def enable_auto_rollback(self):
|
||||||
|
self.bind_finalizer(*self.ghost_attributes)
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
assert not self.finished
|
||||||
|
self.remove_finalizer()
|
||||||
|
self.do_commit()
|
||||||
|
self.finished = True
|
||||||
|
|
||||||
|
def rollback(self):
|
||||||
|
assert not self.finished
|
||||||
|
self.remove_finalizer()
|
||||||
|
self.do_rollback(auto=False)
|
||||||
|
self.finished = True
|
||||||
|
|
||||||
|
def __finalize__(ghost):
|
||||||
|
ghost.do_rollback(auto=True)
|
||||||
|
|
||||||
|
|
||||||
|
class TransactionExample(TransactionBase):
|
||||||
|
"""
|
||||||
|
A transaction example which close()s a resource on rollback
|
||||||
|
"""
|
||||||
|
ghost_attributes = ('resource', )
|
||||||
|
|
||||||
|
def __init__(self, resource):
|
||||||
|
self.resource = resource
|
||||||
|
self.enable_auto_rollback()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "ghost-or-object %s" % object.__str__(self)
|
||||||
|
|
||||||
|
def do_commit(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def do_rollback(self, auto):
|
||||||
|
if auto:
|
||||||
|
print "auto rollback", self
|
||||||
|
else:
|
||||||
|
print "manual rollback", self
|
||||||
|
self.resource.close()
|
@ -1,41 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# Finalization with weakrefs
|
|
||||||
# This is designed for avoiding __del__.
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import traceback
|
|
||||||
import logging
|
|
||||||
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 as e:
|
|
||||||
logging.debug("Exception %s running %s", repr(e), format(finalizer))
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
_finalize_refs = {}
|
|
||||||
|
|
||||||
def track(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
|
|
@ -1,14 +1,36 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import gc
|
||||||
|
|
||||||
import vipsimage
|
import vipsimage
|
||||||
|
|
||||||
logging.basicConfig(level = logging.DEBUG)
|
logging.basicConfig(level = logging.DEBUG)
|
||||||
|
|
||||||
|
# test unref
|
||||||
|
for i in range (1,10):
|
||||||
|
a = vipsimage.VipsImage('/home/john/pics/healthygirl.jpg')
|
||||||
|
|
||||||
# should work
|
# should work
|
||||||
a = vipsimage.VipsImage('/home/john/pics/healthygirl.jpg')
|
a = vipsimage.VipsImage('/home/john/pics/healthygirl.jpg')
|
||||||
a.write('x.png')
|
print 'width =', a.width()
|
||||||
|
print 'height =', a.height()
|
||||||
|
print 'bands =', a.bands()
|
||||||
|
print 'format =', vipsimage.VipsBandFormat.name(a.format())
|
||||||
|
print 'coding =', vipsimage.VipsCoding.name(a.coding())
|
||||||
|
print 'interpretation =', vipsimage.VipsInterpretation.name(a.interpretation())
|
||||||
|
print 'xres =', a.xres()
|
||||||
|
print 'yres =', a.yres()
|
||||||
|
print 'xoffset =', a.xoffset()
|
||||||
|
print 'yoffset =', a.yoffset()
|
||||||
|
|
||||||
# should raise an error
|
# should raise an error
|
||||||
a = vipsimage.VipsImage('banana')
|
# a = vipsimage.VipsImage('banana')
|
||||||
|
|
||||||
|
print 'starting shutdown ...'
|
||||||
|
del a
|
||||||
|
# sometimes have to do several GCs to get them all, not sure why
|
||||||
|
for i in range(10):
|
||||||
|
gc.collect ()
|
||||||
|
print 'shutdown!'
|
||||||
|
|
||||||
|
@ -11,22 +11,95 @@ import ctypes
|
|||||||
|
|
||||||
import vipsobject
|
import vipsobject
|
||||||
|
|
||||||
|
# image enums
|
||||||
|
class VipsDemandStyle:
|
||||||
|
SMALLTILE = 0
|
||||||
|
FATSTRIP = 1
|
||||||
|
THINSTRIP = 2
|
||||||
|
ANY = 3
|
||||||
|
|
||||||
|
# turn 3 into 'ANY', handy for printing
|
||||||
|
# is there a clever way to define this in a base Enum class? I can't think
|
||||||
|
# of it
|
||||||
|
@staticmethod
|
||||||
|
def name(value):
|
||||||
|
return vipsobject.class_value(VipsDemandStyle, value)
|
||||||
|
|
||||||
|
class VipsInterpretation:
|
||||||
|
MULTIBAND = 0
|
||||||
|
B_W = 1
|
||||||
|
HISTOGRAM = 10
|
||||||
|
FOURIER = 24
|
||||||
|
XYZ = 12
|
||||||
|
LAB = 13
|
||||||
|
CMYK = 15
|
||||||
|
LABQ = 16
|
||||||
|
RGB = 17
|
||||||
|
UCS = 18
|
||||||
|
LCH = 19
|
||||||
|
LABS = 21
|
||||||
|
sRGB = 22
|
||||||
|
YXY = 23
|
||||||
|
RGB16 = 25
|
||||||
|
GREY16 = 26
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def name(value):
|
||||||
|
return vipsobject.class_value(VipsInterpretation, value)
|
||||||
|
|
||||||
|
class VipsBandFormat:
|
||||||
|
NOTSET = -1
|
||||||
|
UCHAR = 0
|
||||||
|
CHAR = 1
|
||||||
|
USHORT = 2
|
||||||
|
SHORT = 3
|
||||||
|
UINT = 4
|
||||||
|
INT = 5
|
||||||
|
FLOAT = 6
|
||||||
|
COMPLEX = 7
|
||||||
|
DOUBLE = 8,
|
||||||
|
DPCOMPLEX = 9
|
||||||
|
LAST = 10
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def name(value):
|
||||||
|
return vipsobject.class_value(VipsBandFormat, value)
|
||||||
|
|
||||||
|
class VipsCoding:
|
||||||
|
NONE = 0
|
||||||
|
LABQ = 2
|
||||||
|
RAD = 6
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def name(value):
|
||||||
|
return vipsobject.class_value(VipsCoding, value)
|
||||||
|
|
||||||
libvips = vipsobject.libvips
|
libvips = vipsobject.libvips
|
||||||
|
|
||||||
vips_image_new = libvips.vips_image_new
|
vips_image_new = libvips.vips_image_new
|
||||||
vips_image_new.restype = vipsobject.check_pointer_return
|
vips_image_new.restype = ctypes.c_void_p
|
||||||
|
vips_image_new.errcheck = vipsobject.check_pointer_return
|
||||||
|
|
||||||
vips_image_new_from_file = libvips.vips_image_new_from_file
|
vips_image_new_from_file = libvips.vips_image_new_from_file
|
||||||
vips_image_new_from_file.restype = vipsobject.check_pointer_return
|
|
||||||
vips_image_new_from_file.argtypes = [ctypes.c_char_p]
|
vips_image_new_from_file.argtypes = [ctypes.c_char_p]
|
||||||
|
vips_image_new_from_file.restype = ctypes.c_void_p
|
||||||
|
vips_image_new_from_file.errcheck = vipsobject.check_pointer_return
|
||||||
|
|
||||||
vips_image_new_mode = libvips.vips_image_new_mode
|
vips_image_new_mode = libvips.vips_image_new_mode
|
||||||
vips_image_new_mode.restype = vipsobject.check_pointer_return
|
|
||||||
vips_image_new_mode.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
|
vips_image_new_mode.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
|
||||||
|
vips_image_new_mode.restype = ctypes.c_void_p
|
||||||
|
vips_image_new_mode.errcheck = vipsobject.check_pointer_return
|
||||||
|
|
||||||
vips_image_write = libvips.vips_image_write
|
vips_image_write = libvips.vips_image_write
|
||||||
vips_image_write.restype = vipsobject.check_int_return
|
|
||||||
vips_image_write.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
|
vips_image_write.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
|
||||||
|
vips_image_write.restype = ctypes.c_void_p
|
||||||
|
vips_image_write.errcheck = vipsobject.check_int_return
|
||||||
|
|
||||||
|
vips_image_get_xres = libvips.vips_image_get_xres
|
||||||
|
vips_image_get_xres.restype = ctypes.c_double;
|
||||||
|
|
||||||
|
vips_image_get_yres = libvips.vips_image_get_yres
|
||||||
|
vips_image_get_yres.restype = ctypes.c_double;
|
||||||
|
|
||||||
class VipsImage(vipsobject.VipsObject):
|
class VipsImage(vipsobject.VipsObject):
|
||||||
"""Manipulate a libvips image."""
|
"""Manipulate a libvips image."""
|
||||||
@ -43,6 +116,40 @@ class VipsImage(vipsobject.VipsObject):
|
|||||||
else:
|
else:
|
||||||
self.vipsobject = vips_image_new_mode(filename, mode)
|
self.vipsobject = vips_image_new_mode(filename, mode)
|
||||||
|
|
||||||
|
logging.debug('vipsimage: made %s' % hex(self.vipsobject))
|
||||||
|
|
||||||
|
self.enable_finalize()
|
||||||
|
|
||||||
|
def width(self):
|
||||||
|
return libvips.vips_image_get_width(self.vipsobject)
|
||||||
|
|
||||||
|
def height(self):
|
||||||
|
return libvips.vips_image_get_height(self.vipsobject)
|
||||||
|
|
||||||
|
def bands(self):
|
||||||
|
return libvips.vips_image_get_bands(self.vipsobject)
|
||||||
|
|
||||||
|
def format(self):
|
||||||
|
return libvips.vips_image_get_format(self.vipsobject)
|
||||||
|
|
||||||
|
def coding(self):
|
||||||
|
return libvips.vips_image_get_coding(self.vipsobject)
|
||||||
|
|
||||||
|
def interpretation(self):
|
||||||
|
return libvips.vips_image_get_interpretation(self.vipsobject)
|
||||||
|
|
||||||
|
def xres(self):
|
||||||
|
return vips_image_get_xres(self.vipsobject)
|
||||||
|
|
||||||
|
def yres(self):
|
||||||
|
return vips_image_get_yres(self.vipsobject)
|
||||||
|
|
||||||
|
def xoffset(self):
|
||||||
|
return libvips.vips_image_get_xoffset(self.vipsobject)
|
||||||
|
|
||||||
|
def yoffset(self):
|
||||||
|
return libvips.vips_image_get_yoffset(self.vipsobject)
|
||||||
|
|
||||||
def write(self, filename):
|
def write(self, filename):
|
||||||
vips_image_write(self.vipsobject, filename)
|
vips_image_write(self.vipsobject, filename)
|
||||||
|
|
||||||
|
@ -12,12 +12,21 @@ import logging
|
|||||||
import sys
|
import sys
|
||||||
import ctypes
|
import ctypes
|
||||||
|
|
||||||
import finalize
|
import finalizable
|
||||||
|
|
||||||
# .15 is 7.25+ with the new vips8 API
|
# .15 is 7.25+ with the new vips8 API
|
||||||
libvips = ctypes.CDLL('libvips.so.15')
|
libvips = ctypes.CDLL('libvips.so.15')
|
||||||
libvips.vips_init(sys.argv[0])
|
libvips.vips_init(sys.argv[0])
|
||||||
|
|
||||||
|
# given a class and value, search for a class member with that value
|
||||||
|
# handy for enum classes, use to turn numbers to strings
|
||||||
|
def class_value(classobject, value):
|
||||||
|
for name in dir(classobject):
|
||||||
|
if getattr(classobject, name) == value:
|
||||||
|
return classobject.__name__ + '.' + name
|
||||||
|
|
||||||
|
return 'unknown'
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
|
|
||||||
"""An error from libvips.
|
"""An error from libvips.
|
||||||
@ -31,36 +40,45 @@ class Error(Exception):
|
|||||||
self.detail = vips_error_buffer()
|
self.detail = vips_error_buffer()
|
||||||
libvips.vips_error_clear()
|
libvips.vips_error_clear()
|
||||||
|
|
||||||
logging.debug('vipsobject: Error %s %s', self.message, self.detail)
|
logging.debug('vipsobject: Error: %s %s', self.message, self.detail)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s - %s' %(self.message, self.detail)
|
return '%s %s' %(self.message, self.detail)
|
||||||
|
|
||||||
def check_int_return(value):
|
# handy checkers, assign to errcheck
|
||||||
if value != 0:
|
def check_int_return(result, func, args):
|
||||||
raise Error('Error calling vips function.')
|
if result != 0:
|
||||||
return value
|
raise Error('Error calling vips function %s.' % func.__name__)
|
||||||
|
return result
|
||||||
|
|
||||||
def check_pointer_return(value):
|
def check_pointer_return(result, func, args):
|
||||||
if value == None:
|
if result == None:
|
||||||
raise Error('Error calling vips function.')
|
raise Error('Error calling vips function %s.' % func.__name__)
|
||||||
return value
|
return result
|
||||||
|
|
||||||
vips_error_buffer = libvips.vips_error_buffer
|
vips_error_buffer = libvips.vips_error_buffer
|
||||||
vips_error_buffer.restype = ctypes.c_char_p
|
vips_error_buffer.restype = ctypes.c_char_p
|
||||||
|
|
||||||
class VipsObject:
|
class VipsObject(finalizable.Finalizable):
|
||||||
"""Abstract base class for libvips."""
|
"""Abstract base class for libvips."""
|
||||||
|
|
||||||
def unref_vips(self):
|
# attributes we finalize
|
||||||
|
ghost_attributes = ('vipsobject', )
|
||||||
|
|
||||||
|
def __finalize__(self):
|
||||||
|
logging.debug('vipsobject: __finalize__')
|
||||||
|
|
||||||
if self.vipsobject != None:
|
if self.vipsobject != None:
|
||||||
|
logging.debug('vipsobject: unref %s' % hex(self.vipsobject))
|
||||||
libvips.g_object_unref(self.vipsobject)
|
libvips.g_object_unref(self.vipsobject)
|
||||||
self.vipsobject = None
|
self.vipsobject = None
|
||||||
|
|
||||||
|
def enable_finalize(self):
|
||||||
|
self.bind_finalizer(*self.ghost_attributes)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
logging.debug('vipsobject: init')
|
logging.debug('vipsobject: init')
|
||||||
|
|
||||||
self.vipsobject = None
|
self.vipsobject = None
|
||||||
finalize.track(self, self, self.unref_vips)
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user