Skip to content

Regression in requests hooks stubs #7776

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

Closed
inakimalerba opened this issue May 3, 2022 · 8 comments · Fixed by #7871
Closed

Regression in requests hooks stubs #7776

inakimalerba opened this issue May 3, 2022 · 8 comments · Fixed by #7871

Comments

@inakimalerba
Copy link

Since f330e12 it's not possible to add new hooks to requests.Session.

According to the documentation, the recommended way to add a hook to every request is by adding them to requests.Session.hook:

s = requests.Session()
s.hooks['response'].append(print_url)

This snippet now fails with the following error:

error: Unsupported target for indexed assignment ("Mapping[str, Union[Callable[[Response], Any], List[Callable[[Response], Any]]]]")

Example project

source: https://gitlab.com/cki-project/cki-lib/-/blob/main/cki_lib/session.py#L47

    response_hooks: typing.List[typing.Callable[[requests.Response], typing.Any]] = []
    session.hooks['response'] = response_hooks

CI job: https://gitlab.com/cki-project/cki-lib/-/jobs/2407190024

cki_lib/session.py:47: error: Unsupported target for indexed assignment ("Mapping[str, Union[Callable[[Response], Any], List[Callable[[Response], Any]]]]")
@AlexWaygood
Copy link
Member

AlexWaygood commented May 5, 2022

I think the changes to the stub that means this no longer type-check were made slightly further back, in

I'm not familiar with with the requests API to be able to immediately say whether those changes are correct or not.

@brandonchinn178
Copy link

brandonchinn178 commented May 18, 2022

The change is correct; requests allows hooks to either be a list or a function (which it'll turn into a list):
https://github.com/psf/requests/blob/79f2ec3acc4e24fef6e6ce31ad7b1d4e2f77be31/requests/hooks.py#L27-L28

Unfortunately, this makes typing an issue; what if you set a function as a hook and then call .append after?

s.hooks['response'] = lambda ...
s.hooks['response'].append(...)

are perfectly valid uses of the requests API separately, but will fail when used together. This is more of a question for the requests API design, but my two cents are to revert #7094 and say "if you're using requests + typeshed, you MUST operate on the list" (cc @UncleOwen). To completely override all previously set hooks, this would then require the user doing explicitly

s.hooks['response'] = [lambda ...]

which I think is a fine limitation

EDIT: OH! Actually, this is completely broken for sessions, because _Hooks is now an immutable map: #7732. We should probably partially revert it, where _Hooks should be mutable but _HooksInput should stay immutable.

@Akuli
Copy link
Collaborator

Akuli commented May 18, 2022

cki_lib/session.py:47: error: Unsupported target for indexed assignment ("Mapping[...

Indeed, it's an immutable Mapping.

We could make it mutable when accessed through s.hooks but immutable when passed as argument, which is technically wrong, but works fine in practice. If you specify a hooks argument, why would you also mutate it afterwards through the attribute?

@brandonchinn178
Copy link

If you specify a hooks argument, why would you also mutate it afterwards through the attribute?

Because that's how you're supposed to do it for sessions: https://docs.python-requests.org/en/latest/user/advanced/#event-hooks

You can also add hooks to a Session instance. Any hooks you add will then be called on every request made to the session. For example:

>>> s = requests.Session()
>>> s.hooks['response'].append(print_url)
>>> s.get('https://httpbin.org/')
 https://httpbin.org/
 <Response [200]>

A Session can have multiple hooks, which will be called in the order they are added.

Changing _Hooks would work, because all the requests.post() functions use _HooksInput

@Akuli
Copy link
Collaborator

Akuli commented May 18, 2022

That mutates it through the attribute, but doesn't specify it as an argument when constructing the Session. My question is why would you need to do both, which is when mismatching the types would cause problems.

@brandonchinn178
Copy link

brandonchinn178 commented May 18, 2022

@Akuli
Copy link
Collaborator

Akuli commented May 18, 2022

Ah, so the two types are already different. Indeed, we just need to make _Hooks mutable and keep _HooksInput immutable.

Akuli added a commit that referenced this issue May 18, 2022
Fixes #7776

Mutating hooks, as in `session.hooks['response'] = ...`, should work. Reassigning it like `session.hooks = ...` is probably a bad idea, so it will always be a `dict`.
JelleZijlstra pushed a commit that referenced this issue May 19, 2022
Fixes #7776

Mutating hooks, as in `session.hooks['response'] = ...`, should work. Reassigning it like `session.hooks = ...` is probably a bad idea, so it will always be a `dict`.
@inakimalerba
Copy link
Author

@Akuli thank you for the fix ! :)

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 a pull request may close this issue.

4 participants