Proxy objects in IronPython

I am trying to create a proxy object in IronPython that is supposed to dynamically represent the underlying structure. The proxy itself shouldn't have any functions and properties, I'm trying to catch all the calls at runtime. Capturing function calls is easy, I just need to define a getattr () function for my object and check if the corresponding function exists in the base layer and return some function-like object.

I'm having problems with properties - I don't know how to distinguish the calling context, this is my property called an lvalue or rvalue:

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

or

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

I have looked through the list of special functions in Python, but I have not found anything suitable.

I also tried to make a property in an object on the fly, with a combination of exec () and builtin property (), but I found that IronPython 1.1.2 is missing the whole "new" module (which is present in the IronPython 2.x beta, but I would rather use IP 1.x because of the .NET 2.0 framework).

Any ideas?

+1


source to share


2 answers


A typical implementation of what you want in python would be:

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 is done in CPython without any assertions throwing an exception. IronPython must be equivalent, otherwise it will crash and this test should be added to its unit test package.

+2


source


Try the following:



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")

      

+2


source







All Articles