Making C Extensions More Pythonic  
by Andrew Dalke

Listing One
# Wrapper object to garbage collect the toolkit handle when no longer needed.
import dayswig_python
class smart_ptr:
    def __init__(self, handle):
        self.handle = handle
    def __del__(self, dt_dealloc = dayswig_python.dt_dealloc):
        dt_dealloc(self.handle)
    def __int__(self):
        return self.handle

Listing Two
#  Getting and setting an atom's charge using (a) toolkit function calls and
# (b) attributes. (c) shows how attributes are converted to function calls.

# Part (a)
print "The charge is", dt_charge(atom)
dt_setcharge(atom, 1)

# Part (b)
print "The charge is", atom.charge
atom.charge = 1

# Part (c)
class Atom:
    def __init__(self, handle):
        self.handle = handle
    def __int__(self):
        return int(self.handle)
    def __getattr__(self, name):
        if name == "charge":
            return dt_charge(self.handle)
        elif name == "symbol":
            return dt_symbol(self.handle)
        raise AttributeError, name
    def __setattr__(self, name, val):
        if name == "charge":
            dt_setcharge(self.handle, val)
        elif name == "symbol":
            raise TypeError, "readonly attribute"
        else:
            self.__dict__[name] = val

Listing Three
#  (a) Part of the dispatch table used in PyDaylight's base class.
#  (b) Derived class which adds atom-specific attributes.

# Part (a)
dayobject_properties = {
   "type": (dt_type, None),
   "typename": (dt_typename, None),
   "stringvalue": (dt_stringvalue, dt_setstringvalue),
}
class dayobject:
    __members__ = dayobject_properties.keys()
    _properties = dayobject_properties
    def __init__(self, handle):
        self.handle = handle
        return int(self.handle)
    def __getattr__(self, name):
        get_set = self._properties.get(name, None)
        if get_set is None:
            raise AttributeError, name
        return get_set[0](self.handle)
    def __setattr__(self, name, val):
        get_set = self._properties.get(name, None)
        if get_set is None:
            self.__dict__[name] = val
        else:
            set = get_set[1]
            if set is None:
                raise TypeError, "readonly attribute"
            set(self.handle, val)
# Part (b)
atom_properties = dayobject_properties.copy()
atom_properties.update( {
    "charge": (dt_charge, dt_setcharge),
    "symbol": (dt_symbol, None),
    "weight": (dt_weight, dt_setweight),
})
class Atom(dayobject):
    __members__ = atom_properties.keys()
    _properties = atom_properties

Listing Four
#  Enforcing a toolkit dependency by deleting dependent objects first.
class Path(dayobject):
    def __init__(self, path, mol):
        self.handle = path
        self.mol = mol
        #  .. more initialization code ..
    def __del__(self):
        del self.handle
        del self.mol

Listing Five
#   Converting from toolkit streams and sequences to a Python list.
def toList(seq, converter = None):
    if not seq:
        return []
    dt_reset(seq)
    result = []
    while 1:
        element = dt_next(seq)
        if not element:
            return result
        if converter:
            result.append(converter(element))
        else:
            result.append(element)

Listing Six
#  A list-like class for forward iteration through toolkit streams.
class Iterator:
    def __init__(self, handle, converter = None):
        self.handle = handle
        self._i = 0
        self.converter = converter
    def __len__(self):
        return dt_count(self.handle, TYP_ANY)
    __nonzero__ = __len__
    def __getitem__(self, i):
        if i != self._i:
            raise IndexError, "forward iteration only"
        element = dt_next(self.handle)
        if not element:
            raise IndexError, "list index out of range"
        self._i = i + 1
        if self.converter:
             return self.converter(element)
        return element
    def next(self):
        try:
            return self.__getitem__(self._i)
        except IndexError:
            return None





3

