Skip to content

Instantly share code, notes, and snippets.

@jsbueno
Created April 3, 2023 21:09
Show Gist options
  • Save jsbueno/1cee4e403c9ad6395b82825a4d0ac2a6 to your computer and use it in GitHub Desktop.
Save jsbueno/1cee4e403c9ad6395b82825a4d0ac2a6 to your computer and use it in GitHub Desktop.
#original code written as an answer to https://stackoverflow.com/questions/75915644/call-decorator-only-once-in-a-nested-class-function/75915738#75915738
from functools import wraps
from threading import RLock
import contextvars
RUNONCE = contextvars.ContextVar("RUNONCE", default=None)
class RunOnceDecorator:
def __init__(self, decorator):
self.decorator = decorator
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
v = RUNONCE.get()
try:
if v is None:
def mid():
# intermediary function needed to set a state on contextvars.context
nonlocal result
RUNONCE.set(True)
result = self.decorator(func,*args, **kwargs)
ctx = contextvars.Context()
ctx.run(mid)
else:
ctx = None
result = func(*args, **kwargs)
finally:
del ctx
return result
return wrapper
@RunOnceDecorator
def my_decorator(func, self, *args, **kwargs):
print("before")
val = func(self, *args, **kwargs)
print("after")
return val
pause = 1
class Foo:
@my_decorator
def bar(self):
global pause
if pause:
pause = 0
time.sleep(1)
print("bar")
return 0
@my_decorator
def baz(self):
print("baz")
self.bar()
return 1
if __name__ == "__main__":
import threading
import time
f1 = Foo()
#f2 = Foo()
t1 = threading.Thread(target=f1.bar)
t2 = threading.Thread(target=f1.baz)
t1.start()
time.sleep(0.2)
t2.start()
t1.join()
t2.join()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment