-
Notifications
You must be signed in to change notification settings - Fork 43
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
Refactor the ratelimiting behavior out of the request handlers. #226
Conversation
e0daa7b
to
41f966d
Compare
This one needs a bit of review; I'll read it when I have some spare time :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO a cleaner approach could be a per method decorator, as I think it makes more sense for ratelimits to be per method. Then you would have something like this:
class Lexer(Base):
@ratelimit_endpoint(area="read")
async def get(self) -> None:
self.write(utility.list_languages())
I also think that if the change is made here it should be applied to website.py
for consistency. Not sure about api_curl
or api_deprecated
, they could probably be left as is.
@wookie184 Good point, I'll make it a decorator instead of a mixin. |
9abf110
to
9005e19
Compare
Redid the commits to use a decorator instead of a mixin.
I ended up applying it all over. |
63c1bfb
to
a16af3b
Compare
src/pinnwand/defensive.py
Outdated
@wraps(func) | ||
def inner(*args, **kwargs): | ||
# We transform the args into a dict because ChainMap works differently starting python 3.9 | ||
# In previous versions, it expects all maps to be dictionaries, whereas starting py3.9 | ||
# It transforms list/tuples into a dict whose keys are the different elements. | ||
arguments = ChainMap(dict.fromkeys(args), kwargs.values()) | ||
request_handler = None | ||
for arg in arguments: | ||
if isinstance(arg, RequestHandler): | ||
request_handler = arg | ||
break | ||
|
||
if should_be_ratelimited(request_handler.request, area): | ||
raise error.RatelimitError() | ||
return func(*args, **kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it's guaranteed that the first argument will always be self which is the request handler, does this work?
@wraps(func) | |
def inner(*args, **kwargs): | |
# We transform the args into a dict because ChainMap works differently starting python 3.9 | |
# In previous versions, it expects all maps to be dictionaries, whereas starting py3.9 | |
# It transforms list/tuples into a dict whose keys are the different elements. | |
arguments = ChainMap(dict.fromkeys(args), kwargs.values()) | |
request_handler = None | |
for arg in arguments: | |
if isinstance(arg, RequestHandler): | |
request_handler = arg | |
break | |
if should_be_ratelimited(request_handler.request, area): | |
raise error.RatelimitError() | |
return func(*args, **kwargs) | |
@wraps(func) | |
def inner(self, *args, **kwargs): | |
if should_be_ratelimited(self.request, area): | |
raise error.RatelimitError() | |
return func(self, *args, **kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I first had it like that, by precising that the first argument is instance of RequestHandler, but then some tests failed because it got other unexpected arguments, so i just i thought I'd make it generic.
I prefer the explicitness of the parameter as well, so a combination like the one you're suggesting should work.
I'll try that out
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in the forcepush 002b21f
a16af3b
to
e88f69e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, one thing.
e88f69e
to
989960c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested and works nicely. Seems like a good improvement to me
Awesome stuff. Let's get the conflicts resolved and merge :) |
The ratelimit_endpoint decorator will wrap any request handler's function to handle the ratelimit for it. This avoids having the ratelimit checks being done in the handler's callback function's body
This is to improve the decorator's api better.
Rebased the commits, so this is done :D |
@shtlrs Awesome!! |
As I was browsing the code, I noticed that all
get
andpost
handlers had to check for ratelimits before handling the request.This had been ported over to a
ratelimit
decorator that we use to wrap any http verb's callback function with the ratelimiting behavior.I initially implemented this as a Mixin, but wookie suggested that a per-handler decorator would be better, which makes sense, so it has been changed to that.