Path: csiph.com!usenet.pasdenom.info!weretis.net!feeder1.news.weretis.net!feeder.erje.net!eu.feeder.erje.net!newsfeed.xs4all.nl!newsfeed3.news.xs4all.nl!xs4all!newsgate.cistron.nl!newsgate.news.xs4all.nl!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'string.': 0.05; 'attribute': 0.07; 'canvas': 0.07; 'debug': 0.07; 'method.': 0.07; 'python3': 0.07; 'ugly': 0.07; 'upgraded': 0.07; 'string': 0.09; '"c"': 0.09; '*args,': 0.09; 'attributes': 0.09; 'classes.': 0.09; 'exist,': 0.09; 'implements': 0.09; 'inherited': 0.09; 'received:80.91': 0.09; 'received:80.91.229': 0.09; 'received:gmane.org': 0.09; 'received:list': 0.09; 'useless': 0.09; 'def': 0.12; 'jan': 0.12; '"""base': 0.16; '**kwargs)': 0.16; 'attribute,': 0.16; 'called,': 0.16; 'highlight': 0.16; 'invokes': 0.16; 'name)': 0.16; 'new-style': 0.16; 'object),': 0.16; 'old-style': 0.16; 'parts.': 0.16; 'porting': 0.16; 'presume': 0.16; 'received:80.91.229.3': 0.16; 'received:plane.gmane.org': 0.16; 'reedy': 0.16; 'sign.': 0.16; 'subject:proxy': 0.16; 'user-defined': 0.16; 'fix': 0.17; 'wrote:': 0.18; 'all,': 0.19; 'module': 0.19; 'skip:g 40': 0.19; 'not,': 0.20; 'seems': 0.21; 'settings': 0.22; 'hack': 0.22; 'header:User-Agent:1': 0.23; '2.1': 0.24; '2.x': 0.24; 'example.': 0.24; 'issue,': 0.24; 'pointer': 0.24; 'proxy': 0.24; 'subject:problem': 0.24; 'class.': 0.26; 'updating': 0.26; 'second': 0.26; 'pass': 0.26; 'skip:_ 20': 0.27; 'header:X -Complaints-To:1': 0.27; 'header:In-Reply-To:1': 0.27; 'skip:p 30': 0.29; 'am,': 0.29; 'reporting': 0.29; 'skip:( 40': 0.30; 'code': 0.31; '3.x': 0.31; 'prints': 0.31; 'class': 0.32; 'run': 0.32; 'style': 0.33; 'skip:_ 10': 0.34; 'maybe': 0.34; 'classes': 0.35; 'etc': 0.35; 'test': 0.35; 'but': 0.35; 'there': 0.35; 'version': 0.36; 'false': 0.36; 'instances': 0.36; 'object,': 0.36; 'transition': 0.36; 'done': 0.36; 'method': 0.36; 'shows': 0.36; 'should': 0.36; 'changing': 0.37; 'being': 0.38; 'depends': 0.38; 'same.': 0.38; 'to:addr:python-list': 0.38; 'issue': 0.38; 'fact': 0.38; 'explain': 0.39; 'does': 0.39; 'to:addr:python.org': 0.39; 'skip:p 20': 0.39; 'received:org': 0.40; 'how': 0.40; 'new': 0.61; 'received:173': 0.61; 'strictly': 0.61; 'first': 0.61; 'you.': 0.62; 'real': 0.63; 'more': 0.64; 'different': 0.65; 'here': 0.66; 'between': 0.67; 'default': 0.69; 'below.': 0.71; 'records': 0.73; 'cut': 0.74; 'guides': 0.74; 'special': 0.74; 'behavior': 0.77; '"".': 0.84; '2.2.': 0.84; 'abandoning': 0.84; 'difference.': 0.84; 'irrelevant': 0.84; 'received:fios.verizon.net': 0.84; 'trick,': 0.84; 'cutting': 0.91; 'magical': 0.91; 'differences': 0.93 X-Injected-Via-Gmane: http://gmane.org/ To: python-list@python.org From: Terry Reedy Subject: Re: reporting proxy porting problem Date: Thu, 28 Nov 2013 17:01:25 -0500 References: <5297250A.80709@chamonix.reportlab.co.uk> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Gmane-NNTP-Posting-Host: pool-173-75-254-207.phlapa.fios.verizon.net User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Thunderbird/24.1.1 In-Reply-To: <5297250A.80709@chamonix.reportlab.co.uk> X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 128 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1385676111 news.xs4all.nl 16001 [2001:888:2000:d::a6]:41281 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:60735 On 11/28/2013 6:12 AM, Robin Becker wrote: > 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. All the transition guides I have seen recommend first updating 2.x code (and its tests) to work in 2.x with *all* classes being new-style classes. I presume one of the code checker programs will check this for you. To some extent, the upgrade can be done by changing one class at a time. Yes, this means abandoning support of 2.1 ;-). It also means giving up magical hacks that only work with old-style classes. > I find that I don't understand exactly how the original works so well, To me, not being comprehensible is not a good sign. I explain some of the behavior below. > but here is a cut down version Much more should be cut to highlight the important parts. > ########################################################################## > class Canvas: > def __init__(self,*args,**kwds): > self._fontname = 'Helvetica' This seems pretty useless, but maybe that is a result of cutting down. > 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.""" What if it is not a 'method call on an attribute'? > print('PDFAction.__getattr__(%s)' % name) > return getattr(getattr(self._parent._underlying, self._action), name) I snipped several irrelevant methods. The important part is that there is no __str__ method! > class PyCanvas: > _name = "c" > > def __init__(self, *args, **kwargs) : > self._in = 0 > self._parent = self # nice trick, isn't it ? I call this an ugly code stink. But this is not directly an issue here. > self._underlying = Canvas(*args,**kwargs) Snip irrelevant __bool__ > def __str__(self) : > return 'PyCanvas.__str__()' Also irrelevant for the example. > 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 > C:\code\hg-repos\reportlab>\python27\python.exe z.py > PDFAction.__getattr__(__str__) > c._fontname=Helvetica > is it a string? False type= When Canvas and PyCanvas are upgraded, but PDFAction is not, the result remains the same. This fact shows where the old-new difference makes a difference. > 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= With 2.7, and PDFAction also upgraded (subclassed from object), the result is the same. So this is not a 3.x issue at all, but strictly an old -- new class issue, which has existed since 2.2. The first difference is that print('c._fontname=%s' % c._fontname) produces, with old-style PDFAction, PDFAction.__getattr__(__str__) c._fontname=Helvetica but produces, with new-style PDFAction, c._fontname=<__main__.PDFAction object at 0x00BF8830> The reason is that c._fontname invokes PyCanvas.__getattr__, which returns PDFAction(c, '_fontname') (call this p). The % string interpolation calls p.__str__. For old p, that method does not exist, so p.__getattr__('__str__) is called, and that prints a debug line and returns 'Helvetica', which is interpolated and printed in the second line. For new p, p.__str__ is inherited from object, and we see the familiar default 'object at' string. The hack is depending of the absence of a special method. The immediate fix is to give PDFAction a .__str__ method that returns the same string as the current .__attr__. The second different is that type(c._fontname) is 'instance' versus "". This is because the type of all instances of all user-defined old classes is 'instance'. This is pretty useless. Any test that depends on this is equally useless and should be upgraded to only pass with an instance of the intended (new-style) class. -- Terry Jan Reedy