Welcome, guest | Sign In | My Account | Store | Cart

This recipe allows for calling an unlimited chain of nonexistent attributes - every call is forwarded to a default method with attribute chain as argument.

Python, 24 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MagicObject:

    def __call__(self,*args,**kwargs):
        return MagicObject.__dict__['_stop'](self,self.n,*args,**kwargs)

    def __getattr__(self,name):
        if name in ('__str__','__repr__'): return lambda:'instance of %s at %s' % (str(self.__class__),id(self))
        if not self.__dict__.has_key('n'):self.n=[]
        self.n.append(name)
        return self

    def _stop(self,n,*args,**kwargs):
        self.n=[]
        return self.default(n,*args,**kwargs)

    def default(self,n,*args,**kwargs):
        return 'stop',n,args,kwargs

#############################################################333

>>c=MagicObject()
>>x=c.beubeb.zzzzz(1,2,3,a='bbb')
>>print x
('stop', ['beubeb', 'zzzzz'], (1, 2, 3), {'a': 'bbb'})

I needed this to proxy XMLRPC calls - I wanted to have a local object which I could call as it was a remote object on the server, no matter how deep the attribute chain on the remote object is. I think it can be useful in many other situations, whenever you want to fake or proxy a more complicated object.

This recipe extends the __getattr__ method, so that you can call object.attr.subattr.method() and it all goes to the 'default' method, with a list of attributes in the chain as the first argument.

It works only for method calls - what I don't know is how to handle requests for nonexistent attributes. I just return self - maybe somebody smarter will come up with a solution how to pass them to the handler.

1 comment

Ray Horn 15 years, 3 months ago  # | flag

Give this a try if you want to capture requests for non-extant attributes or function calls:

class MagicObject2(MagicObject):

def __call__(self,*args,**kwargs):
    items = []
    items.append(args)
    items.append(kwargs)
    n = self.n
    if (isinstance(n,list)):
        return {n[0]:{n[-1]:tuple(items)}}
    return {n:tuple(items)}

Notice the difference between this and whatever the original object might return:

c = MagicObject2()
x = c.beubeb.zzzzz(1,2,3,a='bbb')
print x

You may find it easier to interpret the following when it comes time to make sense of what happened with this object at runtime:

{'beubeb': {'zzzzz': ((1, 2, 3), {'a': 'bbb'})}}

You would have to work from the inside outwards when making sense of this stuff but if you need this functionality it would make sense to use it.

Perhaps a few more lines of code might be requires to turn the MagicObject2 into something usable but you probably get the gist of where this is going from what I have posted above.

For more you can look at http://www.pypi.info <-- Might see something useful !

Created by Bartlomiej Górny on Wed, 29 Jun 2005 (PSF)
Python recipes (4591)
Bartlomiej Górny's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks