I’m working with the pylibtiff library (hosted here) and I’m trying to extend class TIFF(ctypes.c_void_p). I’m having trouble with the following method:
# lib has been loaded with the path to libtiff.so, or equivalent
libtiff = ctypes.cdll.LoadLibrary(lib)
class TIFF(ctypes.c_void_p):
@classmethod
def open(cls, filename, mode='r'):
""" Open tiff file as TIFF.
"""
tiff = libtiff.TIFFOpen(filename, mode)
if tiff.value is None:
raise TypeError ('Failed to open file '+`filename`)
return tiff
(full class here)
Now, the expected use pattern here is that we open a file and receive a class TIFF instance, like this:
import libtiff
t = libtiff.TIFF.open('myfile.tiff')
# e.g. grab a numpy array of the data:
arr = t.read_image()
What is this magic? How is the C variable from libtiff typed as a TIFF* arriving back in pythonland and automagically becoming a class TIFF instance? presumably this has something to do with the subclassing of ctypes.c_void_p
I ask because I am trying to override a method on class TIFF:
import libtiff
class TIFFdifferent(libtiff.TIFF):
def read_image(self, verbose=False, different=False):
if not different:
return libtiff.TIFF.read_image(self, verbose)
# my different code ...
however, when I try to create a class TIFFdifferent instance, I get a class TIFF instead:
In [3]: t = libtiffdifferent.TIFFdifferent.open('file.tiff')
In [4]: arr = t.read_image(different=True)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-8bdc24ec874c> in <module>()
----> 1 arr = t.read_image(different=True)
TypeError: read_image() got an unexpected keyword argument 'different'
In [5]: t.read_image?
Type: instancemethod
String Form:<bound method TIFF.read_image of <TIFF object at 0x10b67df80>>
File: /Library/Python/2.7/site-packages/libtiff/libtiff_ctypes.py
Definition: t.read_image(self, verbose=False)
Docstring:
Read image from TIFF and return it as an array.
In [6]: t
Out[6]: <TIFF object at 0x10b67df80>
So, what I need to do is to override open as well – which I don’t know how to do without understanding the magic transformation from C TIFF* to python class TIFF instance, without a constructor, cast or anything else explicit.
The magic is at line 1041 of libtiff_ctypes.py:
As described in 15.17.1.8 Return types in the
ctypesdocs, you don’t have to use actypestype as the result type; you can specify any Python callable—typically either a function, which will validate the real C return value to raise an exception, or a class, which will take the real C return value as its constructor argument.You may notice that the
TIFFclass doesn’t define an__init__method. This is where the inheritance fromc_void_pcomes in: You can construct ac_void_pfrom a pointer; therefore, if you have a subclass ofc_void_p, and it doesn’t need to do any other initialization, it can just rely on the base-class construction to take care of it.Well, you obviously could just do an explicit construction call in your override, even if the original version doesn’t do it:
But you’d only want to do this if you can’t just define
libtiff.cfunc.restype(e.g., because some other code inpylibtiffrelies on the fact that it return something different).Anyway, I’m not sure this is what you actually want to do. What you’re trying to do is write something like this:
That works, without you having to understand the magic behind
restypeat all. Unless you’re worried about the cost of constructing a short-lived temporaryTIFFobject (which, as you’ve seen, has no members and no constructor code beyond those of ac_void_p), isn’t this simple enough?One more possibility, which I wouldn’t normally suggest, and probably isn’t appropriate here, but might be: If
pylibtiffis designed in such a way that overriding its behavior via subclasses is hard to do, you can either (a) fork the code and use your own version, or (b) monkeypatch it at runtime. As suggested in your own comment, something like this:You’d want to wrap the stashing up in a
contextmanager(or use atry:/finally:), if your constructor could ever possibly throw an exception; otherwise you’ll leave therestypepatched.