Mailing List Archive

Intercepting __getattr__ in a Python 2.3 old-style mixin class?
I have a large Python 2.3 based installation with 200k LOC. As part of a migration project I need to intercept all attribute lookups of all old-style class.

Old legacy code:

class Foo(Bar):
...


My idea is to inject a common mixin class like

class Foo(Bar, Mixin):
...

class Mixin:

def __getattr__(self, k)
print repr(self), k
return Foo.__getattr__(self, k)


However I am running always into a recursion because Foo.__getattr__ resolves to Mixin.__getattr__.
(The migrated code has to run under Python 2.3 as the first step of the migration where will be moving
forward to Python 2.7 later on).

Is there any way to fix the code for Python 2.3 old-style classes?


-aj
Re: Intercepting __getattr__ in a Python 2.3 old-style mixin class? [ In reply to ]
Python 2.7 is different because all classes are automatically new-style classes.
In Python 2.7 you would have to overwrite __getattribute__() but I am on Python 2.3 here.
And I have to stay on Python 2.3 for now…welcome to hell :-)

Andreas
On 7 Nov 2016, at 13:23, Michael Howitz wrote:

> Am 07.11.2016 um 13:01 schrieb Andreas Jung <lists@zopyx.com>:
>> I have a large Python 2.3 based installation with 200k LOC. As part of a migration project I need to intercept all attribute lookups of all old-style class.
>>
>> Old legacy code:
>>
>> class Foo(Bar):
>> ...
>>
>>
>> My idea is to inject a common mixin class like
>>
>> class Foo(Bar, Mixin):
>> ...
>>
>> class Mixin:
>>
>> def __getattr__(self, k)
>> print repr(self), k
>> return Foo.__getattr__(self, k)
>
> Hi Andreas,
>
> I doubt that your approach works. Attributes defined on `Foo` are _not_ resolved using __getattr__.
> See the following example using Python 2.7:
>
> In [1]: 1 class A:
> 2 x = 'foo'
> 3 def __getattr__(self, key):
> 4 return 'bar'
>
> In [2]: a = A()
>
> In [3]: a.x
> Out[3]: 'foo'
>
> In [4]: a.b = 'b'
>
> In [5]: a.b
> Out[5]: 'b'
>
> Maybe I am missing something here.
>
> --
> Mit freundlichen Grüßen
> Michael Howitz
>
Re: Intercepting __getattr__ in a Python 2.3 old-style mixin class? [ In reply to ]
On Mon, Nov 7, 2016, at 14:01, Andreas Jung wrote:
> Python 2.7 is different because all classes are automatically new-style
> classes.
> In Python 2.7 you would have to overwrite __getattribute__() but I am on
> Python 2.3 here.

No :)

In Python 3 all classes are new-style classes. In Python 2 (including
2.7) you have to be explicit and either inherit from object (new-style)
or not (old-style).

In Python 2.7 you get:

>>> class A: pass
>>> class B(object): pass
>>> type(A)
<type 'classobj'>
>>> type(B)
<type 'type'>

In Python 3 both are of type 'type'.

And only new-style classes use '__getattribute__'. '__getattr__' is the
right approach for old-style classes in any Python 2 version, even in
Python 2.7.

Hanno
_______________________________________________
Zope-Dev maillist - Zope-Dev@zope.org
https://mail.zope.org/mailman/listinfo/zope-dev
** No cross posts or HTML encoding! **
(Related lists -
https://mail.zope.org/mailman/listinfo/zope-announce
https://mail.zope.org/mailman/listinfo/zope )
Re: Intercepting __getattr__ in a Python 2.3 old-style mixin class? [ In reply to ]
Hi Andreas,

As Hanno said, you'll still be in old-style-classes-land even in Python 2.7
unless you inherit from `object` directly.

You said:

My idea is to inject a common mixin class like
> class Foo(Bar, Mixin):
> ...
> class Mixin:
> def __getattr__(self, k)
> print repr(self), k
> return Foo.__getattr__(self, k)


Your recursion error is caused by calling `Foo.__getattr__(self, k)` from
`Mixin`, but `Foo` inherits from `Mixin`. Calling a method of a subclass
from the same-named method in the superclass is a recurssion.

Instead `Mixin` needs to call `Bar.__getattr__` since `Bar` is the
superclass to `Foo`.

Alternatively, if you don't want to name any specific superclass inside the
mixin method, (and since you can't use `super()` since you're using
old-style classes), I suggest a different approach. Instead of a mixin
class, use a method factory, like this:

def my__getattr__factory(SuperClass):
> def __getattr__(self, k):
> print repr(self), k
> return SuperClass.__getattr__(self, k)


Which you can then use as:

class Foo(Bar): # <- No mixin


> __getattr__ = my_getattr__generator(Bar) # <- Bar, not Foo


Regards,

Leo

On 7 November 2016 at 12:29, Hanno Schlichting <hanno@hannosch.eu> wrote:

> On Mon, Nov 7, 2016, at 14:01, Andreas Jung wrote:
> > Python 2.7 is different because all classes are automatically new-style
> > classes.
> > In Python 2.7 you would have to overwrite __getattribute__() but I am on
> > Python 2.3 here.
>
> No :)
>
> In Python 3 all classes are new-style classes. In Python 2 (including
> 2.7) you have to be explicit and either inherit from object (new-style)
> or not (old-style).
>
> In Python 2.7 you get:
>
> >>> class A: pass
> >>> class B(object): pass
> >>> type(A)
> <type 'classobj'>
> >>> type(B)
> <type 'type'>
>
> In Python 3 both are of type 'type'.
>
> And only new-style classes use '__getattribute__'. '__getattr__' is the
> right approach for old-style classes in any Python 2 version, even in
> Python 2.7.
>
> Hanno
> _______________________________________________
> Zope-Dev maillist - Zope-Dev@zope.org
> https://mail.zope.org/mailman/listinfo/zope-dev
> ** No cross posts or HTML encoding! **
> (Related lists -
> https://mail.zope.org/mailman/listinfo/zope-announce
> https://mail.zope.org/mailman/listinfo/zope )
>