Skip to content

Instantly share code, notes, and snippets.

@ChrisTM
Created June 21, 2013 21:33
Show Gist options
  • Star 26 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save ChrisTM/5834503 to your computer and use it in GitHub Desktop.
Save ChrisTM/5834503 to your computer and use it in GitHub Desktop.
Python decorator for throttling function calls.
class throttle(object):
"""
Decorator that prevents a function from being called more than once every
time period.
To create a function that cannot be called more than once a minute:
@throttle(minutes=1)
def my_fun():
pass
"""
def __init__(self, seconds=0, minutes=0, hours=0):
self.throttle_period = timedelta(
seconds=seconds, minutes=minutes, hours=hours
)
self.time_of_last_call = datetime.min
def __call__(self, fn):
@wraps(fn)
def wrapper(*args, **kwargs):
now = datetime.now()
time_since_last_call = now - self.time_of_last_call
if time_since_last_call > self.throttle_period:
self.time_of_last_call = now
return fn(*args, **kwargs)
return wrapper
@meawoppl
Copy link

Great little trick. Thanks!

@rachitiit
Copy link

Just in case anyone is wondering about the imports

from datetime import datetime, timedelta
from functools import wraps

@purple4reina
Copy link

I feel like it would be helpful to give some indication to the user that they are trying to call a function too soon. That could either be through some sort of logging or printing call, or raising an exception. I say this because I'd assume that if the caller is asking to execute a method, that they'd expect it to be executed.

@jgysland
Copy link

Alternatively, add a wait argument—if True, wait the difference between time_since_last_call and now (or use a while loop to accommodate multi-threaded use cases), if False, fail verbosely if the condition isn't met.

@yifeikong
Copy link

is this snippet threadsafe?

@philer
Copy link

philer commented Feb 19, 2017

Doesn't need to be a class.

from datetime import datetime, timedelta
from functools import wraps

def throttle(seconds=0, minutes=0, hours=0):
    throttle_period = timedelta(seconds=seconds, minutes=minutes, hours=hours)
    def throttle_decorator(fn):
        time_of_last_call = datetime.min
        @wraps(fn)
        def wrapper(*args, **kwargs):
            now = datetime.now()
            if now - time_of_last_call > throttle_period:
                nonlocal time_of_last_call
                time_of_last_call = now
                return fn(*args, **kwargs)
        return wrapper
    return throttle_decorator

@MikeTam1021
Copy link

philer, yours is much messier - and, to top it off, it doesn't even work. Try running it.

@SergeVL
Copy link

SergeVL commented Nov 14, 2017

MikeTam1021, maybe because nonlocal is from python3

@alexstorer
Copy link

The ratelimit package does this with a few extra features.

@shan23
Copy link

shan23 commented Sep 8, 2020

The ratelimit package does this with a few extra features.

Thanks - that is exactly what I needed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment