[issue29758] Previously-working SWIG code fails in Python 3.6

Tristan Croll report at bugs.python.org
Thu Mar 9 05:10:32 EST 2017


Tristan Croll added the comment:

OK, a further clue. First, a little more detail on how my project is arranged (to re-iterate, this works without complaint in Python 3.5):

Rather than use my SWIG output directly, I've created a further wrapper layer in Python to add functions/syntactic sugar that would be difficult to achieve in SWIG, add a bit of extra documentation, and bury some classes that may be occasionally useful but don't really need to be in the primary API. The SWIG output Python library I name clipper_core, and I use the following class decorators for classes in the front-end API to reduce the amount of code (Python and SWIG) needed:

def mappedclass(old_cls):
    '''
    Ensures that objects returned from functions in the clipper_core
    library are instantiated in Python as the derived class with the
    extra functionality. 
    '''
    def decorator(cls):
        def __newnew__(thiscls, *args, **kwargs):
            if thiscls == old_cls:
                return object.__new__(cls)
            return object.__new__(thiscls)
        old_cls.__new__ = __newnew__
        
        return cls
    return decorator
 
def getters_to_properties(*funcs):
    '''
    Class decorator. Add the names of any getter functions with void 
    arguments (e.g. Coord_grid.u()) to convert them to properties. If
    you want the property name to be different from the function name,
    add the desired name and the function name as a tuple 
    (e.g. ('uvw', '_get_uvw'))
    '''
    def property_factory(func):
        def getter(self):
            return getattr(super(self.__class__, self), func)()
        prop = property(getter)
        return prop

    def decorator(cls):
        for func in funcs:
            if type(func) == tuple:
                setattr(cls, func[0], property_factory(func[1]))
            else:
                setattr(cls, func, property_factory(func)) 
        return cls
    return decorator

def format_to_string(cls):
    '''
    Class decorator to redirect the Clipper format() function to __str__,
    to provide pretty printing of the object.
    '''
    def format(self):
        return super(self.__class__,self).format()
    def __str__(self):
        return self.format
    setattr(cls, 'format', property(format))
    setattr(cls, '__str__', __str__)
    return cls

Experimenting this morning with the following two classes:

@format_to_string    
@mappedclass(clipper_core.Cell_descr)
class Cell_descr(clipper_core.Cell_descr):
    def __init__(self, abc, angles):
        '''
        __init__(self, abc, angles) -> Cell_descr
        
        Args:
            abc ([float*3]): cell dimensions in Angstroms
            angles ([float*3]): alpha, beta and gamma angles in degrees
        '''
        clipper_core.Cell_descr.__init__(self, *abc, *angles)
    
@format_to_string
@getters_to_properties('cell_descr', 'matrix_frac', 'matrix_orth', 
                       'metric_real', 'metric_reci', 'volume')
@mappedclass(clipper_core.Cell)
class Cell(clipper_core.Cell):
    '''
    Define a crystallographic unit cell using the lengths of the three
    sides a, b, c (in Angstroms) and the three angles alpha, beta, gamma
    (in degrees). 
    '''
    def __init__(self, abc, angles):
        '''
        __init__(self, abc, angles) -> Cell
        
        Args:
            abc ([float*3]): cell dimensions in Angstroms
            angles ([float*3]): alpha, beta and gamma angles in degrees
        '''
        cell_descr = Cell_descr(abc, angles)
        #cell_descr = clipper_core.Cell_descr(*abc, *angles)
        clipper_core.Cell.__init__(self, cell_descr)
    
    def __eq__(self, other):
        return self.equals(other)

Then:
import clipper
cell = clipper.cell([100,100,100],[90,90,90])
cell.cell_descr

SystemError                               Traceback (most recent call last)
<ipython-input-4-c5072ce3ae97> in <module>()
----> 1 c.cell_descr

/home/tic20/apps/chimerax/lib/python3.6/site-packages/chimerax/clipper/clipper_decorators.py in getter(self)
     87     def property_factory(func):
     88         def getter(self):
---> 89             return getattr(super(self.__class__, self), func)()
     90         prop = property(getter)
     91         return prop

/home/tic20/apps/chimerax/lib/python3.6/site-packages/chimerax/clipper/lib/clipper_python_core/clipper.py in cell_descr(self)
   8249 
   8250         """
-> 8251         return _clipper.Cell_cell_descr(self)
   8252 
   8253     __swig_destroy__ = _clipper.delete_Cell

SystemError: Objects/tupleobject.c:81: bad argument to internal function

... but if I comment out the derived Cell_descr class and switch to the alternate cell_descr constructor in Cell.__init__(), then it works as expected. I have tried commenting out the other class decorators, with no effect - so it would seem it's what's happening in the @mappedclass decorator that is causing my troubles.

----------

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue29758>
_______________________________________


More information about the Python-bugs-list mailing list