Question

I'm building a rate-limiting decorator in flask using redis stores that will recognize different limits on different endpoints. (I realize there are a number of rate-limiting decorators out there, but my use case is different enough that it made sense to roll my own.)

Basically the issue I'm having is ensuring that the keys I store in redis are class-specific. I'm using the blueprint pattern in flask, which basically works like this:

class SomeEndpoint(MethodView):
    def get(self):
        # Respond to get request
    def post(self):
        # Respond to post request

The issue here is that I want to be able to rate limit the post method of these classes without adding any additional naming conventions. In my mind the best way to do this would be something like this:

class SomeEndpoint(MethodView):

    @RateLimit  # Access SomeEndpoint class name
    def post(self):
        # Some response

but within the decorator, only the post function is in scope. How would I get back to the SomeEndpoint class given the post function? This is the basic layout of the decorator. That might be confusing, so here's a more concrete example of the decorator.

class RateLimit(object):
"""
The base decorator for app-specific rate-limiting.
"""
def __call__(self, f):
    def endpoint(*args, **kwargs):
        print class_backtrack(f)  # Should print SomeEnpoint
        return f(*args, **kwargs)
    return endpoint

basically looking for what that class_backtrack function looks like. I've looked through the inspect module, but I haven't found anything that seems to accomplish this.

Was it helpful?

Solution

You can decorate the entire class instead of just the methods:

def wrap(Class, method):
    def wrapper(self, *args, **kwargs):
        print Class
        return method(self, *args, **kwargs)
    return method.__class__(wrapper, None, Class)

def rate_limit(*methods):
    def decorator(Class):
        for method_name in methods:
            method = getattr(Class, method_name)
            setattr(Class, method_name, wrap(Class, method))
        return Class
    return decorator

@rate_limit('post')
class SomeEndpoint(object):

    def post(self):
        pass

class Subclass(SomeEndpoint):
    pass

a = Subclass()
a.post()
# prints <class 'SomeEndpoint'>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top