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

Httpsession requests typing #2699

Merged
merged 20 commits into from
Jun 25, 2024
Merged

Conversation

tdadela
Copy link
Contributor

@tdadela tdadela commented May 4, 2024

The aim of this PR is to provide better typing annotation support for class HttpSession.
This should also fix #2563.

@tdadela
Copy link
Contributor Author

tdadela commented May 5, 2024

This PR is still not finished (in draft). Requires adding similar annotations to FastHttpSession and more testing.
It would be nice and beneficial if you @cyberw could express if you are happy with this direction of changes.

@cyberw
Copy link
Collaborator

cyberw commented May 5, 2024

I’ll have a look!

@cyberw
Copy link
Collaborator

cyberw commented May 5, 2024

Is it possible to put all the override definitions in a if typing_checking-block as well? I'd like to avoid the extra level of indirection at run time.

@tdadela
Copy link
Contributor Author

tdadela commented May 5, 2024

I'm not 100% sure, but according to my current understanding of Python typing ecosystem — no.
I'll do more research about it.

I'd like to avoid the extra level of indirection at run time.

What is the main reason behind it?
Easier debugging / performance — necessity to create extra stack frame for additional function call / something else?

Source code of methods in the base class is very short:

    def get(self, url, **kwargs):
        kwargs.setdefault("allow_redirects", True)
        return self.request("GET", url, **kwargs)

    def options(self, url, **kwargs):
        kwargs.setdefault("allow_redirects", True)
        return self.request("OPTIONS", url, **kwargs)

    def head(self, url, **kwargs):
        kwargs.setdefault("allow_redirects", False)
        return self.request("HEAD", url, **kwargs)

    def post(self, url, data=None, json=None, **kwargs):
        return self.request("POST", url, data=data, json=json, **kwargs)

    def put(self, url, data=None, **kwargs):
        return self.request("PUT", url, data=data, **kwargs)

    def patch(self, url, data=None, **kwargs):
        return self.request("PATCH", url, data=data, **kwargs)

    def delete(self, url, **kwargs):
        return self.request("DELETE", url, **kwargs)

What do you think about following possible implementation in Locust:

    def get(self, url: str | bytes, **kwargs: Unpack[RESTKwargs]):  # type: ignore[override]
         kwargs.setdefault("allow_redirects", True)
        return self.request("GET", url, **kwargs)  # type: ignore[misc]

The number of function call would remain the same.

@cyberw
Copy link
Collaborator

cyberw commented May 5, 2024

Yes, I’m mostly thinking about runtime performance. Why doesnt it work to define the wrappers in an if-block?

@tdadela
Copy link
Contributor Author

tdadela commented May 5, 2024

I think about it as defining types of method that de facto doesn't exist (in contrast to a function with the same name in a base class).
I doubt if type checkers were designed to handle such cases.
But I'm just guessing. I don't know.
I'm going to check it.

@cyberw
Copy link
Collaborator

cyberw commented Jun 4, 2024

hi @tdadela did you have a chance to try it out? Or should I try it?

@tdadela
Copy link
Contributor Author

tdadela commented Jun 6, 2024 via email

@tdadela
Copy link
Contributor Author

tdadela commented Jun 18, 2024

Sorry for the delay.
I tried defining HttpSession with annotations, inside if TYPE_CHECKING. It was ignored by type checkers.

I see 3 options:

  1. Keeping HttpSession as is. (no changes, closing PR)
  2. Trying to define HttpSession with annotations in a .pyc file.
  3. Calling self.requests directly inside HttpSession REST methods (proposition implemented in the last commit). This doesn't increase the number of function calls.

@cyberw
Copy link
Collaborator

cyberw commented Jun 18, 2024

I like the version in the PR now. Have you tried it with vscode/pyright?

Maybe add a constraint to the typing_extensions dependency to only download it on Python versions <3.11?

@0scarB
Copy link
Contributor

0scarB commented Jun 20, 2024

Hi, I'm using pyright 1.1.338 in NeoVim. (With Python version 3.12.3). tdadela's version seems to work great. The catch_response and name kwargs weren't type hinted correctly before, now they are. Some screenshots:
image
image

The only downside is that the method signature becomes less informative in my editor.
Before:
image
After:
image
That's bad default behaviour on the part of the LSP, which should unwrap RESTKwargs IMO. Probably the only way to fix that would be to explicitly type the kwargs for each HTTP method.

Hope this helps. Thanks for the awesome project and great work tdadela!

locust/clients.py Outdated Show resolved Hide resolved
locust/clients.py Outdated Show resolved Hide resolved
locust/clients.py Outdated Show resolved Hide resolved
@tdadela
Copy link
Contributor Author

tdadela commented Jun 23, 2024

@cyberw Do you prefer the last or the last but one version?
(I'll fix the stupid mistake in a minute)

Also, I've realized that # type: ignore[misc] is no longer necessary, probably since calling self.request directly. I'll remove it later. (To not make the impression it's connected with the last commit).

@tdadela
Copy link
Contributor Author

tdadela commented Jun 23, 2024

Have you tried it with vscode/pyright?

Yes. It works well. (I hope I checked everything correctly.)

@tdadela
Copy link
Contributor Author

tdadela commented Jun 23, 2024

@0scarB Thank you very much for your support!
It's especially beneficial since I don't use some tools, e.g., pyright on daily bases (I'm not sure if I test everything correctly).

@0scarB
Copy link
Contributor

0scarB commented Jun 24, 2024

@0scarB Thank you very much for your support! It's especially beneficial since I don't use some tools, e.g., pyright on daily bases (I'm not sure if I test everything correctly).

Tested the latest commit in my project. LGTM! The return types are now correctly shown by pyright. Happy to help

@cyberw
Copy link
Collaborator

cyberw commented Jun 24, 2024

Looks good to me too. @tdadela If you feel it is ready to go, move it out of draft and I'll merge it. Ideally I'd like to use the same typing for FastHttpSession but we can do that later on...

@tdadela
Copy link
Contributor Author

tdadela commented Jun 24, 2024

Today I tried to make return type argument dependent (catch_response) using @overload. Unfortunately, this makes the file super clutter.

Ideally I'd like to use the same typing for FastHttpSession but we can do that later on...

Yup, soon in another PR.

@tdadela tdadela marked this pull request as ready for review June 24, 2024 19:40
@cyberw cyberw merged commit e253453 into locustio:master Jun 25, 2024
14 checks passed
@cyberw
Copy link
Collaborator

cyberw commented Jun 25, 2024

thx!

@tdadela tdadela deleted the httpsession-request-typing branch June 26, 2024 22:42
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.

Pylance errors for catch_response and name parameters in self.client.get()
3 participants