1

I'm trying to get a proxy class working, but it does not really want to work. The class that I want to proxy is the following:

import zmq


class ZmqPublish():
    def __init__(self):
        self.context = None
        self.pub_socket = None

    def setup(self, addr):
        self.context = zmq.Context()
        if isinstance(addr, str):
            addr = addr.split(':')
        host, port = addr if len(addr) == 2 else (addr[0], None)
        self.pub_socket = self.context.socket(zmq.PUB)
        self.pub_socket.bind('tcp://%s:%s' % (host, port))

    def send(self, msg_type, msg):
        self.pub_socket.send_multipart([msg_type, msg])

This simply starts a publisher and has the ability to send messages. Then I have another class that takes care of storing data and sending messages as well as a kind of reply:

from multiprocessing import Manager
from multiprocessing.managers import BaseManager

import zmq_publish


class publishManager(BaseManager):
    pass


publishManager.register('zmq_publish.ZmqPublish', zmq_publish.ZmqPublish)


class Storage:
    def __init__(self):
        self.manager = Manager()
        self.dict = self.manager.dict()
        # Additional variables
        self.host = '127.0.0.1'
        self.port = 5555
        self.bind_addr = (self.host, self.port)
        self.publishManager = Storage.publishManager()
        self.publishManager.start()
        self.publishManager.setup(self.bind_addr)

    def setup(self):
        # Set up zmq subscriber with callbacks

    def add(self, msg):
        self.dict[msg] = 0
        self.publishManager.send('type', msg)

For example the add function is given to a zmq process as a callback and it adds some information. The idea is that it can also send a message back as a response. The problem is, that the message is never send and I believe it is due to the fact that the callback process is another one than the one who created the publisher instance. Thus I am trying to proxy this class and make it available through a manager, but so far it does not work. Can somebody help me or give me some hints?

Kind regards Patrick

1
  • When you have multithreaded code, always debug it in the single-threaded case first. Commented Apr 28, 2017 at 16:03

1 Answer 1

2

I haven't understood your question totally. I'll provide you with a hint that hopefully will assist in achieving the task. You can proxy objects by overloading __getattr__ for undefined attributes accesses and/or __getattribute__ for all attribute accesses.

class Proxy:
    def __init__(self, Klass): 
        self.Klass = Klass                    # Save/embed original class/object
    def __getattr__(self, name):              # On undefined attr fetches only
        ...
        return getattr(self.Klass, name)      # Delegate to Klass/ could be an obj too
    ....

Now any attribute that's not defined at Proxy will be intercepted by __getattr__ and delegated to Klass. Klass here is a class object so we proxy the class itself, Klass can also be an actual instance of a class. Basically, Proxy delegates only undefined attribute fetches to klass. Hence, when you access an attribute that's not available for Proxy, doesn't exist in Proxy or its instances, __getattr__ gets triggered automatically and will delegate your request to the wrapped/embedded object klass. For intercepting all attribute fetches you would use __getattribute__ instead of __getattr__ and in addition to that you might need to use object.__getattribute__ to avoid recursion inside __getattribute__, because any self.attr inside your proxy methods will trigger your class's __getattribute__.

Edit: How to use the Proxy class:

If you would like to to proxy an object of another class, one way to achieve this:

class Proxy:
    # Proxy objects of other classes 
    def __init__(self, obj): 
        self.obj = obj

    def __getattr__(self, attr): 
        return getattr(self.obj, attr)  # Run only on undefined attribute accesses 

class Test:
    data = 'some data'
    def __init__(self, x, y):
        self.x = x 
        self.y = y 
    def add(self):
        return self.x + self.y 
    def mul(self): 
        return self.x * self.y 

test_obj = Test(3, 4)        
proxied_obj = Proxy(test_obj)    # Delegate some operations to test_obj

proxied_obj.add()                # Calls Proxy.__getattr__
print(proxied_obj.mul())         # Calls Proxy.__getattr__

As you can see, add and mul method doesn't actually exist in Proxy class which proxies other classes' instances. proxied_obj is an instance of Proxy class with a wrapped Test obj. *Notice, __getattr__ is like a fallback option, only when attributes are not found, __getattr__ gets called. That is, if any attribute was looked up in the Proxy instance itself or its class or superclasses was not found, then __getattr__ gets triggered.

You could add other methods in Proxy to augment the the interface of the proxied object. In 3.X and new-style classes, if your proxied object may require operator overloading methods: __add__ for + or __getitem__ for indexing, you need to redefine all the overloading methods inside Proxy or in a superclass again so builtin operations will succeed:

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

    def __getattr__(self, attr): 
        return getattr(self.obj, attr)

    def   __add__  (...): ... 
    def __getitem__(...): ...

If you're still confused: OOP and Delegation: “Wrapper” Proxy Objects

I really encourage you to open Python interpreter and try to experiment with code, just small examples. One line of code might be better than 100 spoken words and vice versa.

Sign up to request clarification or add additional context in comments.

2 Comments

I tried it out and I ran into some errors, which are not entirely clear. If I use __getattr__, I get TypeError: unbound method setup() must be called with ZmqPublish instance as first argument (got tuple instance instead), which seems strange. I thought __getattr__ takes care of the instance? If I use __getattribute__ like that def __getattribute__(self, name): return getattr(self.Klass, name), it gives me AttributeError: Proxy instance has no attribute 'setup'. Where is my mistake?If I can make my question clearer, please let me know what's missing.
It seems you're trying to delegate some operations to an object of ZmqPublish then pass Proxy class an instance of ZmqPublish, the not class itself. The way you're using __getattribute__ isn't correct, __getattribute__ runs automatically for all attribute fetches and overloading it will have consequences on your code. No problem, I'll update my question to show how to use the Proxy class.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.