Search code examples
pythonpython-decoratorsdispatch

Python: Decorator Dispatcher: Catch-22


I'm trying to build a decorator-based dispatcher, such as you find used by Flask or Pyramid. I got something that works, but ran into a bit of a catch-22. The following code works, but only because foo() gets executed and sets the .mq_path-attribute. When starting the application and building a list of the dispatchable functions no attributes are thus set yet. I want to execute foo() driven by events.

I could "manually" prepare a list of functions ahead and updated as I add functions, but I enjoy the way Flask works, by just adding a decorator to a function that handles an URL (or in this case a MQ path).

list_of_paths = []
path_dispatcher = {}

def handle_mq(path):
    def decorator(fn):
        def decorated(*args,**kwargs):
            decorated.mq_path = path
            print "Hello from the handle_mq() decorator, your path is: {0}".format(path)
            return fn(*args,**kwargs)
        return decorated
    return decorator

@handle_mq('/some/path')
def foo():
    print "foo!"

foo() # <- this code only works if I first call the decorated function

for k, v in globals().items():
    if hasattr(v, 'mq_path'):
        list_of_paths.append(v.mq_path)
        path_dispatcher[v.mq_path] = v

print list_of_paths
print path_dispatcher
path_dispatcher['/some/path']()

So basically the question is, how to gather a list of the decorated functions before they are first executed?

I'm on Python 2.7.


Solution

  • I found the answer!

    list_of_paths = []
    path_dispatcher = {}
    
    def handle_mq(path):
        def decorator(fn):
            list_of_paths.append(path)
            path_dispatcher[path] = fn
            def decorated(*args,**kwargs):
                print "Hello from handl_mq decorator, your path is: {0}".format(path)
                return fn(*args,**kwargs)
            return decorated
        return decorator
    
    @handle_mq('/some/path')
    def foo():
        print "foo!"
    
    print list_of_paths
    print path_dispatcher
    path_dispatcher['/some/path']()