deckar01 / ratelimit

API Rate Limit Decorator
MIT License
20 stars 2 forks source link

User Defined Ratelimit Time Period at Class level #6

Closed johnbumgarner closed 3 years ago

johnbumgarner commented 3 years ago

I have a use case where I would like to preset a base level time period as a Class argument. I want the enduser to be able to change the time if needed. I know that I can set this value in an external file (e.g. constants.py or config.ini), but I want to set it as a Class argument and pass that argument to @limits(calls=5, period=timeout_period)?

I still haven't been able to solve this and I have tried many times. In the code below the timeout period is always ZERO. How can I pass the Class argument rate_limit_timeout_period to @limits(calls=5, period=timeout_period)?

timeout_period = 0

class MyClass(object):

    Global timeout_period

    def __init__(self, search_string='', rate_limit_timeout_period=60):
        """
        self._word = search_string
        timeout_period = rate_limit_timeout_period

    @on_exception(expo, RateLimitException, max_tries=10)
    @limits(calls=5, period=timeout_period)
    def function_to_ratelimit(self):
      # more code here

Thanks in advance for any assistance.

deckar01 commented 3 years ago

Method signatures (and decorators) don't have access to instance properties. They are evaluated at declaration time, so even if you used a global variable, python resolves the value before __init__ is called.

The decorator operator @ is a syntax convenience for declaring method composition with implicit nesting. If you want to use decorators at runtime to wrap an existing function, you can perform the method composition explicitly. It is possible declare a method inside of another method and use the decorator syntax, but it uses more lines and indentation, which defeats the purpose.

class MyClass(object):
    def __init__(self, search_string='', rate_limit_timeout_period=60):
        self._word = search_string
        handler = on_exception(expo, RateLimitException, max_tries=10)
        limiter = limits(calls=5, period=rate_limit_timeout_period)
        self.function_to_ratelimit = handler(limiter(self.function_to_ratelimit))

    def function_to_ratelimit(self):
      # more code here
johnbumgarner commented 3 years ago

@deckar01 Thanks for the coding lesson. The code that you provided works likes a charm. And it added only one extra line of code to my Class.

Is it possible to raise a warning message when the ratelimit is reached? I tried raise_on_limit=True, but that didn't provide any message.

johnbumgarner commented 3 years ago

@deckar01 I was able to figure out the warning message issue that raised.

# variable outside the class 
ratelimit_status = False

# used the on_backoff parameter 
handler = on_exception(expo, RateLimitException, max_time=60, on_backoff=self.backoff_hdlr)

self.ratelimit_status = ratelimit_status

# this is fired when the ratelimit is reached. 
def backoff_hdlr(self, details):
    if self.ratelimit_status is False:
       print('RateLimit reached. Temporary hibernation mode for 60 seconds')
       self.ratelimit_status = True

It seems to work fine, but maybe there is a better way.

Thanks again for solving my original issue.