I’m creating a function tagging system, to enable or disable functions based on tags:
def do_nothing(*args, **kwargs): pass
class Selector(set):
def tag(self, tag):
def decorator(func):
if tag in self:
return func
else:
return do_nothing
return decorator
selector = Selector(['a'])
@selector.tag('a')
def foo1():
print "I am called"
@selector.tag('b')
def foo2():
print "I am not called"
@selector.tag('a')
@selector.tag('b')
def foo3():
print "I want to be called, but I won't be"
foo1() #Prints "I am called"
foo2() #Does nothing
foo3() #Does nothing, even though it is tagged with 'a'
My question is about the last function, foo3. I understand why it isn’t being called. I was wondering if there’s a way to make it so that it is called if any of the tags are present in the selector. Ideally, the solution makes it so the tags are only checked once, not every time the function is called.
A side note: I’m doing this to select tests to run based on environment variables in unittest unit tests. My actual implementation uses unittest.skip.
EDIT: Added the decorator return.
The issue is that if you decorate it twice, one returns the function, one returns nothing.
This means, in whatever order, you will always get nothing. What you need to do is keep a set of tags on each object, and check that whole set at once. We can do this nicely without polluting namespaces with function attributes:
This gives some bonuses – it’s possible to inspect all the tags the function has, and it’s possible to tag with multiple decorators or by giving many tags in a single decorator.
The use of
functools.wraps()also means the function keeps it’s original ‘identity’ (docstrings, name, etc…).Edit: If you wanted to do some wrapper elimination: