reporting proxy porting problem

Robin Becker robin at reportlab.com
Thu Nov 28 06:12:10 EST 2013


I am in the process of porting reportlab to python3.3, one of the contributions 
is a module that implements a reporting proxy with a canvas that records all 
access calls and attribute settings etc etc. This fails under python3 because of 
differences between old and new style classes.


I find that I don't understand exactly how the original works so well, but here 
is a cut down version

##########################################################################
class Canvas:
     def __init__(self,*args,**kwds):
         self._fontname = 'Helvetica'

class PDFAction :
     """Base class to fake method calls or attributes on Canvas"""
     def __init__(self, parent, action) :
         """Saves a pointer to the parent object, and the method name."""
         self._parent = parent
         self._action = action

     def __getattr__(self, name) :
         """Probably a method call on an attribute, returns the real one."""
         print('PDFAction.__getattr__(%s)' % name)
         return getattr(getattr(self._parent._underlying, self._action), name)

     def __call__(self, *args, **kwargs) :
         """The fake method is called, print it then call the real one."""
         if not self._parent._parent._in :
             self._precomment()
             self._postcomment()
         self._parent._parent._in += 1
         meth = getattr(self._parent._underlying, self._action)
         retcode = meth(*args,**kwargs)
         self._parent._parent._in -= 1
         return retcode

     def __hash__(self) :
         return hash(getattr(self._parent._underlying, self._action))

     def _precomment(self) :
         print('%s(__dict__=%s)._precomment()' %
                 (self.__class__.__name__,repr(self.__dict__)))

     def _postcomment(self) :
         print('%s(__dict__=%s)._postcomment()' %
                 (self.__class__.__name__,repr(self.__dict__)))

class PyCanvas:
     _name = "c"

     def __init__(self, *args, **kwargs) :
         self._in = 0
         self._parent = self     # nice trick, isn't it ?
         self._underlying = Canvas(*args,**kwargs)

     def __bool__(self) :
         """This is needed by platypus' tables."""
         return 1

     def __str__(self) :
         return 'PyCanvas.__str__()'

     def __getattr__(self, name) :
         return PDFAction(self, name)

if __name__=='__main__':
     c = PyCanvas('filepath.pdf')
     print('c._fontname=%s' % c._fontname)
     print('is it a string? %r type=%s' %
             (isinstance(c._fontname,str),type(c._fontname))))
##########################################################################

when run under python27 I see this

C:\code\hg-repos\reportlab>\python27\python.exe z.py
PDFAction.__getattr__(__str__)
c._fontname=Helvetica
is it a string? False type=<type 'instance'>

and under python33 I see this
C:\code\hg-repos\reportlab>\python33\python.exe z.py
c._fontname=<__main__.PDFAction object at 0x00BF8830>
is it a string? False type=<class '__main__.PDFAction'>

clearly something different is happening and this leads to failure in the real 
pycanvas module testing. Is there a way to recover the old behaviour(s)?
-- 
Robin Becker




More information about the Python-list mailing list