Prevent function (or decorator) insertion

I have code in the decorator that I need only once. Many other functions (utility, etc.) will be called later on the line, and I want other functions that this decorator may have to not be accidentally used in the function call socket.

I also want to be able to check at any time whether the current code has been wrapped in a decorator or not.

I wrote this, but I just wanted to find out if anyone else could think of a more elegant solution than checking the unique function name (hopefully!) On the stack.

import inspect def my_special_wrapper(fn): def my_special_wrapper(*args, **kwargs): """ Do some magic, only once! """ # Check we've not done this before for frame in inspect.stack()[1:]: # get stack, ignoring current! if frame[3] == 'my_special_wrapper': raise StandardError('Special wrapper cannot be nested') # Do magic then call fn # ... fn(*args, **kwargs) return my_special_wrapper def within_special_wrapper(): """ Helper to check that the function has been specially wrapped """ for frame in inspect.stack(): if frame[3] == 'my_special_wrapper': return True return False @my_special_wrapper def foo(): print within_special_wrapper() bar() print 'Success!' @my_special_wrapper def bar(): pass foo() 
+4
source share
3 answers

Here is an example of using global for this task - in what I consider relatively safe:

 from contextlib import contextmanager from functools import wraps _within_special_context = False @contextmanager def flag(): global _within_special_context _within_special_context = True try: yield finally: _within_special_context = False #I'd argue this would be best replaced by just checking the variable, but #included for completeness. def within_special_wrapper(): return _within_special_context def my_special_wrapper(f): @wraps(f) def internal(*args, **kwargs): if not _within_special_context: with flag(): ... f(*args, **kwargs) else: raise Exception("No nested calls!") return internal @my_special_wrapper def foo(): print(within_special_wrapper()) bar() print('Success!') @my_special_wrapper def bar(): pass foo() 

Result:

 True Traceback (most recent call last): File "/Users/gareth/Development/so/test.py", line 39, in <module> foo() File "/Users/gareth/Development/so/test.py", line 24, in internal f(*args, **kwargs) File "/Users/gareth/Development/so/test.py", line 32, in foo bar() File "/Users/gareth/Development/so/test.py", line 26, in internal raise Exception("No nested calls!") Exception: No nested calls! 

Using the context manager ensures that the variable is not set. You can simply use try/finally , but if you want to change the behavior for different situations, the context manager can be made flexible and reusable.

+3
source

The obvious solution is to have special_wrapper set the global flag and just skip its magic if the flag is set.

This is about the only good use of a global variable that allows a unit of code to store information that is used only in this code, but which must survive life in this code.

It does not need to be installed in a global area. A function can set a flag both on itself and on any object or class if nothing touches it.

As Lattyware noted in the comments, you'll want to use the try / except context manager, or perhaps even better, to ensure that the variable is not set.

Update. If you need wrapped code to check if it is complete, specify a function that returns the flag value. You might want to combine everything with the class for accuracy.

Update 2: I see that you are doing this for transaction management. There are probably already libraries that do this. I highly recommend you at least look at their code.

+2
source

While my solution works technically, it requires a reset decorator guide, but you can very well change things such that the outermost function is a class (with instances that are wrappers of the decorated functions passed to it in __init__ ) and call reset() in __exit__() , which allows you to use the with statement to create a decorator that will only be used once in the context. Also note that this requires Python 3 because of the nonlocal , but it is easy to adapt to 2.7 using a dict instead of a flag variable.

 def once_usable(decorator): "Apply this decorator function to the decorator you want to be usable only once until it is reset." def outer_wrapper(): flag = False def inner_wrapper(*args, **kwargs): nonlocal flag if not flag: flag = True return decorator(*args, **kwargs) else: print("Decorator currently unusable.") # raising an Error also works def decorator_reset(): nonlocal flag flag = False return (inner_wrapper, decorator_reset) return outer_wrapper() 

Testing:

 >>> def a(aa): return aa*2 >>> def b(bb): def wrapper(*args, **kwargs): print("Decorated.") return bb(*args, **kwargs) return wrapper >>> dec, reset = once_usable(b) >>> aa = dec(a) >>> aa(22) Decorated. 44 >>> aaa = dec(a) Decorator currently unusable. >>> reset() >>> aaa = dec(a) >>> aaa(11) Decorated. 22 
0
source

Source: https://habr.com/ru/post/1484280/


All Articles