Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(WIP) Draft of manual mode (static) and decorators (dynamic) #3

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

ivasio
Copy link

@ivasio ivasio commented May 19, 2019

Manual mode can be tested by running test_static.py



def get_requester_id(requester_id_getter, args, kwargs):
if type(requester_id_getter) == 'str':

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isinstance(requester_id_getter, str)


def setup(zones):
if limiter is None:
limiter = RequestLimiter(zones)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the idea?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Singletone limiter object for entire app. Yes, it can be initialized explicitly, but this function basically helps to avoid double initialization

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, this wouldn't work because limiter is a local variable here. You can force it to work with global keyword but why do you need that singleton?

"argument")
except TypeError:
raise TypeError(f"Value passed in {requester_id_getter} "
"must be hashable")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isinstance(requeser_id, typing.Hashable)



def limit_calls(requester_id_getter, zone, burst=0, delay=float('inf')):
def decorator(function):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See functools.wraps


class Zone:

def __init__(self, size, rate, loop=asyncio.get_event_loop()):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid keeping a loop if you can. And don't create it this way.

delay = limiter.get_request_delay(requester_id, resource)
if delay > 0:
asyncio.sleep(delay)
return function(*args, **kwargs)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so the original function should be awaitable (and probably we need to check it with typing.Awaitable)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and we need to await here of course

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean awaiting asyncio.sleep, don't you?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to await both sleep and function calls

Copy link
Author

@ivasio ivasio May 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, typing.Awaitable check works on coroutine objects, which was actually expected. We need to check if it was a coroutine fucntion before invoking it, so I decided to heck it with asyncio.iscoroutinefunction. It doesn't work with Task's, for instance, so if it is needed, we may consider searching for a workaround

}



Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add pylint call as commit hook (or just don't forget to call it or integrate it with your IDE.

request_times = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]
try_requests(
limiter, [(time, 0, '/login') for time in request_times]
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please keep newlines in the end of the files



def try_requests(limiter, requests):
for i, request in enumerate(requests, 1):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for static version we need to operate on list of tuples (or pandas.DataFrame and have list or dataframe with results back)

limiter = static.RequestLimiter(zones, resources)
request_times = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]
try_requests(
limiter, [(time, 0, '/login') for time in request_times]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is crucial to have different requester_id's and different targets.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course it will be covered with tests, but actually I want to illustrate the correctness of the algorithm itself here, and the working tool is just this algorithm replicated for many resources and clients. Or did you mean anything else by crucial here?

@ivasio ivasio changed the title Draft of manual mode (static) and decorators (dynamic) (WIP) Draft of manual mode (static) and decorators (dynamic) May 19, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants