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

Handle rest_framework.throttling.SimpleRateThrottle #459

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

Conversation

JulianMaurin
Copy link
Contributor

@JulianMaurin JulianMaurin commented Jul 19, 2022

Handle rest_framework.throttling.SimpleRateThrottle

According to the issue: #382.

I can confirm that the issue comes from an incompatibility with the DRF throttling view (see: DRF code).

Once the SimpleRateThrottle.timer is patched with fake_time, executing self.timer() (equivalent to timer(self)) is working with time.time but not with fake_time:

How to reproduce

>>> import time
>>> class A:
...     timer = time.time
...     def test(self):
...             print(self.timer())
... 
>>> A().test()
1658255082.1697752
>>>
>>> from freezegun.api import fake_time
>>> class B:
...     timer = fake_time
...     def test(self):
...             print(self.timer())
... 
>>> B().test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in test
TypeError: fake_time() takes 0 positional arguments but 1 was given

About the fix

It was not that simple. Testing an API using the test client will load the module after the patch that is performed by Freezgun. To reproduce the context of late loading I introduce the tests.fake_module_lazy module (In order to add a test demonstrating the error).

Then, to fix the error I changed the signature of the freezegun.api.fake_time function, to accept any args/kwars (and so allow the self.timer usage at the origin of the issue).

I feel the change is not risky at all but I can miss something.

@JulianMaurin
Copy link
Contributor Author

@staticdev, @boxed ,

Any opinion about this PR?

@@ -170,7 +170,7 @@ def get_current_time():
return freeze_factories[-1]()


def fake_time():
def fake_time(*args, **kwargs):
Copy link
Contributor

Choose a reason for hiding this comment

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

Why *args, **kwargs here? That seems very wrong to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hello @boxed ,
Thanks for your review. May I humbly ask you to elaborate?

I think I made an effort to explain the reproduction of the bug, both in the description and in the test.
If I'm off track, can you possibly guide me to a solution that you think is more appropriate?

Copy link
Contributor

Choose a reason for hiding this comment

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

@JulianMaurin this is breaking the API typing on api.pyi, so users that have mypy will have problems. I also think you can add typed arguments to satisfy your needs with default arguments=None so it will also not break current applications and maintain the types well defined.

Copy link
Contributor Author

@JulianMaurin JulianMaurin Aug 11, 2022

Choose a reason for hiding this comment

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

I thought this version does not break the application, in addition, fake_time function is not defined in api.pyi, but maybe I did not understand something.

Would you prefer this version (with a docstring ofc):

Suggested change
def fake_time(*args, **kwargs):
def fake_time(_ : Any):

Copy link
Contributor

Choose a reason for hiding this comment

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

@JulianMaurin putting an argument of type Any makes the method still untyped. Also as @boxed adding *args, **kwargs here does not make sense to me also, since to fix the bug you one need to be able to call time it with self.

You can check in the definition of builtin time.time there is NO argument whatsoever: https://github.com/python/cpython/blob/v3.9.6/Modules/timemodule.c#L86

Have to tried, instead of adding arguments, wrapping fake_time with @classmethod? Maybe it will fix this usage. If not I am sure some kind of wrapper like this will be needed.

@@ -170,7 +170,7 @@ def get_current_time():
return freeze_factories[-1]()


def fake_time():
def fake_time(*args, **kwargs):
Copy link
Contributor

Choose a reason for hiding this comment

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

@JulianMaurin this is breaking the API typing on api.pyi, so users that have mypy will have problems. I also think you can add typed arguments to satisfy your needs with default arguments=None so it will also not break current applications and maintain the types well defined.

@@ -130,6 +131,14 @@ def test_fake_strftime_function():
assert fake_strftime_function() == '2012'


@freeze_time("2022-01-01")
def test_fake_time_function_as_class_attribute():
Copy link
Contributor

Choose a reason for hiding this comment

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

It is great that you added tests, but it is also important to explain on README the new optional usage in English so more people can easily reuse your solution =)

Copy link
Contributor

Choose a reason for hiding this comment

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

You can also add some description of this change on CHANGELOG.

Copy link
Contributor Author

@JulianMaurin JulianMaurin left a comment

Choose a reason for hiding this comment

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

Hey @staticdev ,
Thank you for taking the time to develop.

I will add an entry to the changelog if we continue this PR, thanks for the suggestion 🙇

However I don't see what should be added to the README, can you be more precise?

It would be nice to fix this bug, but if you think this is not the right way at all, tell me directly and I will close the PR and we will all save time.

@@ -170,7 +170,7 @@ def get_current_time():
return freeze_factories[-1]()


def fake_time():
def fake_time(*args, **kwargs):
Copy link
Contributor Author

@JulianMaurin JulianMaurin Aug 11, 2022

Choose a reason for hiding this comment

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

I thought this version does not break the application, in addition, fake_time function is not defined in api.pyi, but maybe I did not understand something.

Would you prefer this version (with a docstring ofc):

Suggested change
def fake_time(*args, **kwargs):
def fake_time(_ : Any):

@staticdev
Copy link
Contributor

staticdev commented Aug 11, 2022

Hey @staticdev , Thank you for taking the time to develop.

I will add an entry to the changelog if we continue this PR, thanks for the suggestion bow

However I don't see what should be added to the README, can you be more precise?

It would be nice to fix this bug, but if you think this is not the right way at all, tell me directly and I will close the PR and we will all save time.

@JulianMaurin I mean, you can add the usage in a class like you described in the description of your pull request.

I did not say you are doing it wrong, you are in the right path.

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.

3 participants