I have the below decorator function for printing response times. Is there a way to pass description of the decorator when decorating another function with this decorator? For eg: I want to print "calling service xyz" instead of function name "call_service_xyz"
def timer(func):
"""Print the runtime of the decorated function"""
#functools.wraps(func)
def wrapper_timer(*args, **kwargs):
start_time = time.perf_counter() # 1
value = func(*args, **kwargs)
end_time = time.perf_counter() # 2
run_time = end_time - start_time # 3
logger.info(f"Finished {func.__name__!r} in {run_time:.4f} secs")
return value
return wrapper_timer
#timer("call service xyz")
def call_service_xyz():
You could print the docstring to act as the description of the function:
def timer(func):
"""Print the runtime of the decorated function"""
#functools.wraps(func)
def wrapper_timer(*args, **kwargs):
start_time = time.perf_counter() # 1
value = func(*args, **kwargs)
end_time = time.perf_counter() # 2
run_time = end_time - start_time # 3
logger.info(f"{desc} Finished in {run_time:.4f} secs")
return value
return wrapper_timer
#timer
def call_service_xyz():
"""Call service xyz."""
# ^^ this is the docstring which is
# assigned to .__doc__
Alternatively, if you wish to pass the description into timer you need to create another layer to the decorator which accepts arguments:
def timer(desc):
def decorator(func):
#functools.wraps(func)
def wrapper_timer(*args, **kw):
start_time = time.perf_counter()
value = func(*args, **kw)
end_time = time.perf_counter()
run_time = end_time - start_time
logger.info(f"{func.__doc__!r} Finished in {run_time:.4f} secs")
return value
return wrapper_timer
return decorator
Now use as you describe in your question:
#timer('call service xyz')
def xyz():
...
Related
I have written a generator as follows:
def my_generator():
i = 0
while i < 1000000:
i += 1
yield i
Assuming the generator cannot be executed in a second and in the test function I use a timeout decorator to guarantee the function should not run more than 1 second.
#timeout(1)
def test():
for i in my_generator:
print(i)
Unfortunately, the timeout don't work as I wanted, the function print all the number from 1 to 1000000 with more than 1 second.
In the decorator, I have tried gevent and KThread, but none of them can work.
Decorator using KThread:
class KThread(threading.Thread):
"""Subclass of threading.Thread, with a kill() method."""
def __init__(self, *args, **kwargs):
threading.Thread.__init__(self, *args, **kwargs)
self.killed = False
def start(self):
"""Start the thread."""
self.__run_backup = self.run
"""Force the Thread to install our trace."""
self.run = self.__run
threading.Thread.start(self)
def __run(self):
"""Hacked run function, which installs the trace."""
sys.settrace(self.globaltrace)
self.__run_backup()
self.run = self.__run_backup
def globaltrace(self, frame, why, arg):
if why == 'call':
return self.localtrace
else:
return None
def localtrace(self, frame, why, arg):
if self.killed:
if why == 'line':
raise SystemExit()
return self.localtrace
def kill(self):
self.killed = True
def timeout(seconds):
def timeout_decorator(func):
def _new_func(oldfunc, result, oldfunc_args, oldfunc_kwargs):
result.append(oldfunc(*oldfunc_args, **oldfunc_kwargs))
def _(*args, **kwargs):
result = []
'''create new args for _new_funcbecause
we want to get the func return val to result list
'''
new_kwargs = {
'oldfunc': func,
'result': result,
'oldfunc_args': args,
'oldfunc_kwargs': kwargs
}
thd = KThread(target=_new_func, args=(), kwargs=new_kwargs)
thd.start()
thd.join(seconds)
alive = thd.isAlive()
'''kill the child thread'''
thd.kill()
if alive:
alert_exce = u'function timeout for [%d s].' % seconds
raise Timeout(alert_exce)
else:
return result[0]
_.__name__ = func.__name__
_.__doc__ = func.__doc__
return _
return timeout_decorator
Decorator using gevent:
def g_timer(timeout_seconds=None, timeout_exception=TimeoutError, exception_message=None, module_name=None):
import gevent
from gevent import monkey
monkey.patch_all()
def decorate(func):
def wrapper(*args, **kwargs):
try:
t0 = time.time()
gevent.with_timeout(timeout_seconds, func, *args, **kwargs)
elapsed = time.time() - t0
except gevent.timeout.Timeout as e:
print("exception")
return wrapper
return decorate
I tried to write a decorator to compute the time of the computation for methods in a class, but I also have a lot of properties.
I tried to write a decorator for a property as follows:
def timer(method):
import time
#property
def wrapper(*args, **kw):
start = time.time()
result = method
end = time.time()
print('Elapsed time for: {} is: {}s'.format(method.__name__,(end-start)*1000))
return result
return wrapper
I can't get the name of the property as written, moreover perhaps you would suggest to write it in another way?
You would need to stack decorators:
def timer(method):
import time
def wrapper(*args, **kwargs):
start = time.time()
result = method(*args, **kwargs) # note the function call!
end = time.time()
print('Elapsed time for: {} is: {}s'.format(method.__name__,(end-start)*1000))
return result
return wrapper
class X:
#property
#timer
def some_method(self):
# timed code
return 0
>>> x = X()
>>> x.some_method
Elapsed time for: some_method is: 0.0050067901611328125s
0
Could you please let me know if there is a way for a decorated function to keep its metadata?
This would be the code for the decorator:
def timer(func):
"""prints how long a function takes to run."""
def wrapper(*args, **kwargs):
t_start = time.time()
result = functionalists(*args, **kwargs)
t_total = time.time() - t_start
print('{} took {}s'.format(functionalists.__name__, t_total))
return result
return wrapper
The following would be the decorated function.
#timer
def sleep_n_seconds(n=10):
"""pause processing for n seconds.
Args:
n (int): The number of seconds to pause for.
"""
time.sleep(n)
When I try to print the docstrings with the following code, the metadata is not returned.
print(sleep_n_seconds.__doc__)
Please let me know if I need to provide further details.
Thank you
Use the wraps function from functools module to retain the signature. :
from functools import wraps
def timer(func):
#wraps(func)
"""prints how long a function takes to run."""
def wrapper(*args, **kwargs):
t_start = time.time()
result = functionalists(*args, **kwargs)
t_total = time.time() - t_start
print('{} took {}s'.format(functionalists.__name__, t_total))
return result
return wrapper
For tracing race conditions in the code I need to display the caller name and how long the operations take.
The code below outputs:
2018-02-06 19:00:11.418800: get_data() called by **wrappe**r() (0:00:00.001010)
test result: PASS
How can I make it output the caller name instead of the decorator function name e.g.:
2018-02-06 19:05:47.617679: get_data() called by **test_get_data**() (0:00:00.034116)
test result: PASS
The code:
from datetime import datetime
import time, inspect
class Timer():
def __init__(self):
self.time_start = datetime.now()
def stop(self):
return (datetime.now() - self.time_start)
class Test2Decorators():
def caller(callee):
def timer(callee):
def wrapper(*args, **kwargs):
get_data_timer = Timer()
result = callee(*args, **kwargs)
print ' (%s)' % get_data_timer.stop()
return result
return wrapper
#timer
def wrapper(*args, **kwargs):
curframe = inspect.currentframe()
calframe = inspect.getouterframes(curframe, 2)
print '%s: %s() called by %s()' % (datetime.now(), callee.__name__, calframe[1][3]),
return callee(*args, **kwargs)
return wrapper
#caller
def get_data(self, product_id = None, product_name = None):
return 'test result: PASS'
def test_get_data():
print class_inst.get_data('beer', '32445256')
time.sleep(1)
class_inst = Test2Decorators()
test_get_data()
I just figured this out this way:
from datetime import datetime
import inspect
import time
class Timer():
def __init__(self):
self.time_start = datetime.now()
def stop(self):
return (datetime.now() - self.time_start)
class Test2Decorators():
def caller(callee):
def timer(callee):
def wrapper(*args, **kwargs):
get_data_timer = Timer()
result = callee(*args, **kwargs)
print '%s()' % inspect.stack()[1][3],
print ' (%s)' % get_data_timer.stop()
return result
return wrapper
#timer
def wrapper(*args, **kwargs):
print '%s: %s() called by' % (datetime.now(), callee.__name__),
return callee(*args, **kwargs)
return wrapper
#caller
def get_data(self, product_id = None, product_name = None):
return 'test result: PASS'
def test_get_data():
while True:
print class_inst.get_data('beer', '32445256')
time.sleep(1)
class_inst = Test2Decorators()
test_get_data()
This question already has answers here:
Read/Write Python Closures
(8 answers)
Closed 8 years ago.
I'm trying to write a decorator in Python to limit the number of times a function is called in an amount of time. I anticipate using it like this:
#ratelimit(seconds=15)
def foo():
print 'hello'
start = time.time()
while time.time() - start < 10:
foo()
> 'hello'
> 'hello'
So the decorated function can be called a maximum of once every seconds. In terms of implementing it I have this, but it doesn't work as I'm not sure the correct way to persist the last_call between subsequent calls:
import time
def ratelimit(seconds=10):
last_call = None # Never call decorated function
def decorator(func):
def wrapper(*args, **kwargs):
if last_call is None or time.time() - last_call > seconds:
result = func(*args, **kwargs)
last_call = time.time()
return result
return wraps(func)(wrapper)
return decorator
The code below worked fine for me in python 2.7.
import time
from functools import wraps
last_called = dict() # When last called, and with what result
def ratelimit(seconds=10, timer=time.time):
def decorator(func):
last_called[func] = None
#wraps(func)
def wrapper(*args, **kwargs):
now = timer()
call_data = last_called.get(func, None)
if call_data is None or now - call_data[0] >= seconds:
result = func(*args, **kwargs)
last_called[func] = (now, result)
else:
result = call_data[1] # Replay rate-limited result
return result
return wrapper
return decorator