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
|
||||
|
||||
import logging
|
||||
import gc
|
||||
|
||||
import vipsimage
|
||||
|
||||
logging.basicConfig(level = logging.DEBUG)
|
||||
|
||||
# test unref
|
||||
for i in range (1,10):
|
||||
a = vipsimage.VipsImage('/home/john/pics/healthygirl.jpg')
|
||||
|
||||
# should work
|
||||
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
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
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.restype = vipsobject.check_pointer_return
|
||||
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.restype = vipsobject.check_pointer_return
|
||||
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.restype = vipsobject.check_int_return
|
||||
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):
|
||||
"""Manipulate a libvips image."""
|
||||
@ -43,6 +116,40 @@ class VipsImage(vipsobject.VipsObject):
|
||||
else:
|
||||
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):
|
||||
vips_image_write(self.vipsobject, filename)
|
||||
|
||||
|
@ -12,12 +12,21 @@ import logging
|
||||
import sys
|
||||
import ctypes
|
||||
|
||||
import finalize
|
||||
import finalizable
|
||||
|
||||
# .15 is 7.25+ with the new vips8 API
|
||||
libvips = ctypes.CDLL('libvips.so.15')
|
||||
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):
|
||||
|
||||
"""An error from libvips.
|
||||
@ -31,36 +40,45 @@ class Error(Exception):
|
||||
self.detail = vips_error_buffer()
|
||||
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):
|
||||
return '%s - %s' %(self.message, self.detail)
|
||||
return '%s %s' %(self.message, self.detail)
|
||||
|
||||
def check_int_return(value):
|
||||
if value != 0:
|
||||
raise Error('Error calling vips function.')
|
||||
return value
|
||||
# handy checkers, assign to errcheck
|
||||
def check_int_return(result, func, args):
|
||||
if result != 0:
|
||||
raise Error('Error calling vips function %s.' % func.__name__)
|
||||
return result
|
||||
|
||||
def check_pointer_return(value):
|
||||
if value == None:
|
||||
raise Error('Error calling vips function.')
|
||||
return value
|
||||
def check_pointer_return(result, func, args):
|
||||
if result == None:
|
||||
raise Error('Error calling vips function %s.' % func.__name__)
|
||||
return result
|
||||
|
||||
vips_error_buffer = libvips.vips_error_buffer
|
||||
vips_error_buffer.restype = ctypes.c_char_p
|
||||
|
||||
class VipsObject:
|
||||
class VipsObject(finalizable.Finalizable):
|
||||
"""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:
|
||||
logging.debug('vipsobject: unref %s' % hex(self.vipsobject))
|
||||
libvips.g_object_unref(self.vipsobject)
|
||||
self.vipsobject = None
|
||||
|
||||
def enable_finalize(self):
|
||||
self.bind_finalizer(*self.ghost_attributes)
|
||||
|
||||
def __init__(self):
|
||||
logging.debug('vipsobject: init')
|
||||
|
||||
self.vipsobject = None
|
||||
finalize.track(self, self, self.unref_vips)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user