add experimental ctypes Python binding

start hacking on a new Python interface
This commit is contained in:
John Cupitt 2011-06-17 14:50:14 +01:00
parent 3920f5dc7e
commit 3628692799
7 changed files with 184 additions and 5 deletions

1
.gitignore vendored
View File

@ -7,6 +7,7 @@ TAGS
*.lo
*.la
*.pc
*.pyc
.deps
.libs
aclocal.m4

6
TODO
View File

@ -69,10 +69,6 @@
- some way for nip2 to get the vips bin area
- some way for nip2 to get EXEEXT
- wrap a basic set of Magick filters
- need vips_image_invalidate_area()
@ -93,7 +89,7 @@
- get rid of a lot of the command-line programs, who wants to write a man page
for batch_image_convert etc yuk
- can we make man pages for the API as well? probably not from googling a bit
- can we make man pages for the API as well? probably not from googling a bit

11
python/README Normal file
View File

@ -0,0 +1,11 @@
Experimental Python binding using ctypes
The current Python binding uses swig to wrap the C++ interface. This causes
problems on Windows, since compiling all of the DLLs correctly to work with
the various python binaries is painful, and makes the bainding very large.
The idea here is to use ctypes to make a binding that's just Python and which
directly wraps the C API. The new vips8 C API is much more wrapper-friendly,
plus we can reuse parts of the gobject binding, so this should be possible.

41
python/finalize.py Normal file
View File

@ -0,0 +1,41 @@
#!/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

14
python/test.py Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/python
import logging
import vipsimage
logging.basicConfig(level = logging.DEBUG)
# should work
a = vipsimage.VipsImage('/home/john/pics/healthygirl.jpg')
a.write('x.png')
# should raise an error
a = vipsimage.VipsImage('banana')

50
python/vipsimage.py Normal file
View File

@ -0,0 +1,50 @@
#!/usr/bin/python
"""This module wraps up libvips in a less awful interface.
Author: J.Cupitt
GNU LESSER GENERAL PUBLIC LICENSE
"""
import logging
import ctypes
import vipsobject
libvips = vipsobject.libvips
vips_image_new = libvips.vips_image_new
vips_image_new.restype = 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_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_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]
class VipsImage(vipsobject.VipsObject):
"""Manipulate a libvips image."""
def __init__(self, filename = None, mode = None):
logging.debug('vipsimage: init')
vipsobject.VipsObject.__init__(self)
if filename == None and mode == None:
self.vipsobject = vips_image_new()
elif filename != None and mode == None:
self.vipsobject = vips_image_new_from_file(filename)
else:
self.vipsobject = vips_image_new_mode(filename, mode)
def write(self, filename):
vips_image_write(self.vipsobject, filename)

66
python/vipsobject.py Normal file
View File

@ -0,0 +1,66 @@
#!/usr/bin/python
"""This module wraps up libvips in a less awful interface.
Wrap VipsObject.
Author: J.Cupitt
GNU LESSER GENERAL PUBLIC LICENSE
"""
import logging
import sys
import ctypes
import finalize
# .15 is 7.25+ with the new vips8 API
libvips = ctypes.CDLL('libvips.so.15')
libvips.vips_init(sys.argv[0])
class Error(Exception):
"""An error from libvips.
message -- a high-level description of the error
detail -- a string with some detailed diagnostics
"""
def __init__(self, message):
self.message = message
self.detail = vips_error_buffer()
libvips.vips_error_clear()
logging.debug('vipsobject: Error %s %s', self.message, self.detail)
def __str__(self):
return '%s - %s' %(self.message, self.detail)
def check_int_return(value):
if value != 0:
raise Error('Error calling vips function.')
return value
def check_pointer_return(value):
if value == None:
raise Error('Error calling vips function.')
return value
vips_error_buffer = libvips.vips_error_buffer
vips_error_buffer.restype = ctypes.c_char_p
class VipsObject:
"""Abstract base class for libvips."""
def unref_vips(self):
if self.vipsobject != None:
libvips.g_object_unref(self.vipsobject)
self.vipsobject = None
def __init__(self):
logging.debug('vipsobject: init')
self.vipsobject = None
finalize.track(self, self, self.unref_vips)