views:

63

answers:

1
from functools import wraps

def foo_register(method_name=None):
    """Does stuff."""
    def decorator(method):
        if method_name is None:
            method.gw_method = method.__name__
        else:
            method.gw_method = method_name
        @wraps(method)
        def wrapper(*args, **kwargs):
            method(*args, **kwargs)
        return wrapper
    return decorator

Example: The following decorates my_function with foo_register instead of ever making it to decorator.

@foo_register
def my_function():
    print('hi...')

Example: The following works as expected.

@foo_register('say_hi')
def my_function():
    print('hi...')

If I want it to work correctly in both applications (one using method.__name__ and one passing the name in), I have to check inside of foo_register to see if the first argument is a decorator, and if so, I have to: return decorator(method_name) (instead of return decorator). This sort of "check to see if it's a callable" seems very hackish. Is there a nicer way to create a multi-use decorator like this?

P.S. I already know that I can require the decorator to be called, but that's not a "solution". I want the API to feel natural. My wife loves decorating, and I don't want to ruin that.

+2  A: 

Glenn - I had to do it then. I guess I'm glad that there is not a "magic" way to do it. I hate those.

So, here's my own answer (method names different than above, but same concept):

from functools import wraps

def register_gw_method(method_or_name):
    """Cool!"""
    def decorator(method):
        if callable(method_or_name):
            method.gw_method = method.__name__
        else:
            method.gw_method = method_or_name
        @wraps(method)
        def wrapper(*args, **kwargs):
            method(*args, **kwargs)
        return wrapper
    if callable(method_or_name):
        return decorator(method_or_name)
    return decorator

Example usage (both versions work the same):

@register_gw_method
def my_function():
    print('hi...')

@register_gw_method('say_hi')
def my_function():
    print('hi...')
orokusaki
This is a function with radically different behavior depending on its arguments. That's what I mean by magic: it "figures out what you mean" instead of expecting the user to *say* what he means to begin with.
Glenn Maynard
FWIW, the whole concept of decorators is pretty magical. Not like Lucky Charms magical, but magical nonetheless. I think to make the wife *really* happy, there should be a decorator decorator in this situation that makes a decorator use default arguments if it's invoked with none. Of course this wouldn't work if it's actually passed a callable.
intuited
@intuited - When I'm coding a decorator I feel like I'm watching the van/bridge scene from "Inception". I agree they aren't much fun to maintain, but they sure help to make a library more user friendly (ie, ugly implementation detail).
orokusaki
@Aaron - why did you delete your answer? It was a great answer (which I just up-voted and attempted to comment on). (comment was: "@Aaron - 1+ clean and explicit FTW.")
orokusaki
@orokusaki. I decided that I don't like splitting it because it's not an orthogonal split. The register method still knows about names and unless I can think of a better way to do it, then what you have is cleaner. Thanks for the upvote though.
aaronasterling
@orokusaki: I used to find them really confusing, but getting used to the idea of passing and returning functions, mostly just by doing a lot of it, has helped to make them only kind of confusing.
intuited