views:

280

answers:

3

I'm trying to make a proxy object in IronPython, which should dynamically present underlying structure. The proxy itself shouldn't have any functions and properties, I'm trying to catch all the calls in the runtime. Catching the function calls is easy, I just need to define getattr() function for my object, and check does appropriate function exists in the underlying layer, and return some function-like object.

I have problems with properties - I don't know how to distinguish the calling context, is my property called as a lvalue or rvalue:

o = myproxy.myproperty # *I need to call underlying.myproperty_get()*

or

myproxy.myproperty = o # *I need to call underlying.myproperty_set(o)*

I looked at the list of special functions in Python, but I didn't found anything appropriate.

I also tried to make property in the object on the fly, with combination of exec() and builtin property() function, but I found that IronPython 1.1.2 lacks of entire 'new' module (which is present in IronPython 2.x beta, but I'll rather use IP 1.x, because of .NET 2.0 framework).

Any ideas?

+1  A: 

The usual implementation of what you want in python would be this:

class CallProxy(object):
    'this class wraps a callable in an object'
    def __init__(self, fun):
        self.fun = fun

    def __call__(self, *args, **kwargs):
        return self.fun(*args, **kwargs)

class ObjProxy(object):
    ''' a proxy object intercepting attribute access
    '''
    def __init__(self, obj):
        self.__dict__['_ObjProxy__obj'] = obj

    def __getattr__(self, name):
        attr = getattr(self.__obj, name)
        if callable(attr):
            return CallProxy(attr)
        else:
            return attr

    def __setattr__(self, name, value):
        setattr(self.__obj, name, value)

I wrote a test to prove that this behaves as expected:

#keep a list of calls to the TestObj for verification
call_log = list()
class TestObj(object):
    ''' test object on which to prove
        that the proxy implementation is correct
    '''
    def __init__(self):
        #example attribute
        self.a = 1
        self._c = 3

    def b(self):
        'example method'
        call_log.append('b')
        return 2

    def get_c(self):
        call_log.append('get_c')
        return self._c
    def set_c(self, value):
        call_log.append('set_c')
        self._c = value
    c = property(get_c, set_c, 'example property')

def verify(obj, a_val, b_val, c_val):
    'testing of the usual object semantics'
    assert obj.a == a_val
    obj.a = a_val + 1
    assert obj.a == a_val + 1
    assert obj.b() == b_val
    assert call_log[-1] == 'b'
    assert obj.c == c_val
    assert call_log[-1] == 'get_c'
    obj.c = c_val + 1
    assert call_log[-1] == 'set_c'
    assert obj.c == c_val + 1

def test():
    test = TestObj()
    proxy = ObjProxy(test)
    #check validity of the test
    verify(test, 1, 2, 3)
    #check proxy equivalent behavior
    verify(proxy, 2, 2, 4)
    #check that change is in the original object
    verify(test, 3, 2, 5)

if __name__ == '__main__':
    test()

This executes on CPython without any assert throwing an exception. IronPython should be equivalent, otherwise it's broken and this test should be added to its unit test suite.

Florian Bösch
+1  A: 

Try this:

class Test(object):
    _test = 0

    def test():
        def fget(self):
            return self._test
        def fset(self, value):
            self._test = value
        return locals()
    test = property(**test())

    def greet(self, name):
        print "hello", name


class Proxy(object):
    def __init__(self, obj):
        self._obj = obj

    def __getattribute__(self, key):
        obj = object.__getattribute__(self, "_obj")
        return getattr(obj, key)

    def __setattr__(self, name, value):
        if name == "_obj":
            object.__setattr__(self, name, value)
        else:
            obj = object.__getattribute__(self, "_obj")
            setattr(obj, name, value)


t = Test()
p = Proxy(t)
p.test = 1
assert t.test == p.test
p.greet("world")
Toni Ruža
A: 

Guys, thank you very much!

apetrovic