RazerM / ratelimiter

Simple Python module providing rate limiting
Apache License 2.0
122 stars 29 forks source link

Remove exec for async support #5

Closed pmav99 closed 6 years ago

pmav99 commented 6 years ago

Not that exec() is a huge problem in this context, but perhaps a more elegant way to support Python < 3.5 would be to move __aenter__ into a different module and import the module on runtime. E.g.

import sys

if sys.version_info < (3, 5):
    raise ImportError("Only support python versions >= 3.5")

import time
import asyncio

async def __aenter__(self):
    if self._alock is None:
        self._init_async_lock()

    with await self._alock:
        # We want to ensure that no more than max_calls were run in the allowed
        # period. For this, we store the last timestamps of each call and run
        # the rate verification upon each __enter__ call.
        if len(self.calls) >= self.max_calls:
            until = time.time() + self.period - self._timespan
            if self.callback:
                asyncio.ensure_future(self.callback(until))
            sleeptime = until - time.time()
            if sleeptime > 0:
                await asyncio.sleep(sleeptime)
        return self

and

class RateLimiter(object):
    """Provides rate limiting for an operation with a configurable number of
    requests for a time period.
    """
    # snip

    if PY35:
        import _async
        __aenter__ = _async.__aenter__
        __aexit__ = asyncio.coroutine(__exit__)
RazerM commented 6 years ago

I agree. I'll accept a PR if you change the import to from . import _async

pmav99 commented 6 years ago

Thanks @RazerM, Just a note though, if we use explicit relative imports we need to convert the single module to a proper python package. E.g.:

$ tree ratelimiter
ratelimiter
├── _async.py
├── __init__.py
└── ratelimiter.py

Are you OK with that?

merwok commented 6 years ago

Yes, making this a package would be needed, otherwise you’d have to install two top-level modules e.g. ratelimiter and _ratelimiter_async, which is a bit inelegant.

(I would put the main code in ratelimiter/__init__.py though, I don’t see why many projects have an empty __init__ and hide the code in a submodule that gets imported anyway)

RazerM commented 6 years ago

Yes, a package should be used. The code can be in __init__.py or core.py if you'd prefer.