ActiveState Code

Recipe 413701: ObserverProxy


Create an proxy that forwards methods to a group of observers

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def ObserverProxy(method_names):
    class Proxy:
        def __init__(self):
            self._observers = []
        def add_observer(self, observer):
            self._observers.append(observer)
        def remove_observer(self, observer):
            self._observers.remove(observer)

    def create_method_proxy(method_name):
        def method_proxy(self, *args, **kwargs):
            for observer in self._observers:
                getattr(observer, method_name)(*args, **kwargs)
        return method_proxy

    for method_name in method_names:
        setattr(Proxy, method_name, create_method_proxy(method_name))

    return Proxy()

if __name__ == "__main__":
    # usage example

    output_proxy = ObserverProxy(["write", "close"])

    import sys
    output_proxy.add_observer(sys.stdout)
    output_proxy.add_observer(sys.stderr)
    output_proxy.add_observer(file("somefile", "w"))

    print >>output_proxy, "This goes to all observers"
    output_proxy.close()

Discussion

I found myself repeatedly creating proxying observers for different purposes, for example in unittest test suites where I wanted multiple result objects, so I made this recipe.

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52289 is a similar recipe with an advantage - it doesn't require defining the interface in advance.

Comments

  1. 1. At 1:09 a.m. on 10 may 2005, Zoran Isailovski said:

    Nice closure application, but you should also mention similar / related recipes ... ... for ex.:

    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107027

    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52289

  2. 2. At 2:23 a.m. on 10 may 2005, Ori Peleg (the author) said:

    Thanks, I wasn't aware of them. The second recipe, http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52289, is really cool - it seems to wrap each method when called, so it's unnecessary to predefine the interface. There are a lot of new instances created, on each call, but hey, no need to optimize prematurely :)

    The first recipe is conceptually different in that the proxy doesn't provide the same interface, so it can't be plugged in.

  3. 3. At 11:50 a.m. on 10 may 2005, Zoran Isailovski said:

    Another complement. I didn't think of it the first time, but there are also parallels with my events recipe at http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410686

    It fasciliates mapping compatible protocols (methods of same signature) onto each other, independent of their name, for ex.:

    output_proxy = Events()
    output_proxy.write += sys.stdout.write
    output_proxy.write += sys.stderr.write
    output_proxy.write += mylog.add_string # different name here
    ...
    output_proxy.write("Hi")
    

Sign in to comment