Repository navigation
Update definition of constants in urllib3.util.retry - #6892
Conversation
72c4d6c to
dc5908e
Compare
| class Retry: | ||
| DEFAULT_ALLOWED_METHODS: frozenset[str] | ||
| RETRY_AFTER_STATUS_CODES: frozenset[int] | ||
| DEFAULT_REMOVE_HEADERS_ON_REDIRECT: frozenset[list[str]] |
There was a problem hiding this comment.
| DEFAULT_REMOVE_HEADERS_ON_REDIRECT: frozenset[list[str]] | |
| DEFAULT_REMOVE_HEADERS_ON_REDIRECT: frozenset[str] |
Looks like a frozenset with a single element to me, the string "Authorization" 🙂 (It's actually impossible to have a frozenset of lists, since lists aren't hashable!) https://github.com/urllib3/urllib3/blob/33afb5c96c435ab7118b5f41e26f96c290fef55b/src/urllib3/util/retry.py#L192
There was a problem hiding this comment.
Yes good point @AlexWaygood! My bad. I will fix it now. Thank you
| log: Any | ||
|
|
||
| class Retry: | ||
| DEFAULT_ALLOWED_METHODS: frozenset[str] |
There was a problem hiding this comment.
| DEFAULT_ALLOWED_METHODS: frozenset[str] | |
| DEFAULT_ALLOWED_METHODS: ClassVar[frozenset[str]] |
Since these are class-level constants, it might be good to have them all as ClassVars (should apply similar changes to the other constants as well, if so)
|
Wait, do the deprecated constants in the stub even exist anymore? I can't see them in the source code anywhere... |
They don't exists anymore... I'm not sure how to deal with those to be honest. I mean, probably some people using older version can still have those as valid default constants. What do you suggest to do @AlexWaygood ? |
Typeshed generally only ships stubs for the most recent version of a package, so I think they should probably be deleted. Looks like they were removed from urllib3 in November 2020! urllib3/urllib3@c67c094#diff-53a96dd57c993a39101318e9df47f8aab6610e8c2c1064f8109f66943dcbbd78 |
Those constants have been renamed here urllib3/urllib3@382ab32 |
I removed deprecated constants and also used more precise types for the rest of the attributes (adding missing ones, and re-naming some of them). |
Looks great! Is there a reason why you've annotated some of the instance attributes differently to how they're annotated in the source code? E.g. |
My bad, I looked at an old version of the code, will fix it now. Thanks. |
|
While we're cleaning up this file, could you also possibly remove the |
Yes @AlexWaygood , I did update all the methods as well. I'm not sure how to update the stubtest bit. Any hint? |
Ah the CI is reporting those, I think. I'm fixing that. |
|
You'll need to remove the following entries from the stubtest allowlist as part of your PR (the test fails if there are unused entries):
There are some other stubtest failures as well, which I'll help you out with. |
|
I don't know why |
That's not necessarily true; there's a checkbox somewhere to allow edits by maintainers and it looks like @bugant didn't turn it on. |
| @classmethod | ||
| def from_int(cls, retries, redirect=..., default=...): ... | ||
| def get_backoff_time(self): ... | ||
| def from_int(cls, retries, redirect=..., default=...) -> "Retry": ... |
There was a problem hiding this comment.
No need to have string-literal annotations in stubs; from __future__ import annotations behaviour is enabled by default 🙂 same applies for the increment method
| def from_int(cls, retries, redirect=..., default=...) -> "Retry": ... | |
| def from_int(cls, retries, redirect=..., default=...) -> Retry: ... |
There was a problem hiding this comment.
BUT, on second thought, the annotation in the source code is wrong. You should annotate the method like this: add from _typeshed import Self at the top of the file, and then do:
| def from_int(cls, retries, redirect=..., default=...) -> "Retry": ... | |
| def from_int(cls: type[Self], retries, redirect=..., default=...) -> Self: ... |
Thought that was probably it, but wasn't sure. Thanks! |
|
Not sure which checkbox I have to tick |
| backoff_factor: float | ||
| raise_on_redirect: bool | ||
| raise_on_status: bool | ||
| history: tuple[Any] | None |
There was a problem hiding this comment.
The attribute can never be None at runtime; if the None default in the __init__ method is used, then the attribute is set to an empty tuple.
You'll need to add back the RequestHistory NamedTuple you had in one of your previous commits (it's a collections.namedtuple in the 1.26.x branch, but it can be a typing.NamedTuple in the stub, like it is in the main branch; type-checkers don't care about the different kinds of NamedTuples)
| history: tuple[Any] | None | |
| history: tuple[()] | tuple[RequestHistory, ...] |
| status: int | None | ||
| other: int | None | ||
| allowed_methods: Collection[str] | None | ||
| status_forcelist: Collection[int] | None |
There was a problem hiding this comment.
This also can never be None at runtime; it is set as an empty set if no value is provided in __init__.
| status_forcelist: Collection[int] | None | |
| status_forcelist: Collection[int] |
| raise_on_status: bool | ||
| history: tuple[Any] | None | ||
| respect_retry_after_header: bool | ||
| remove_headers_on_redirect: Collection[str] |
There was a problem hiding this comment.
Any Collection can be passed into __init__, but it is changed to be a frozenset when it is set as an attribute on the class.
| remove_headers_on_redirect: Collection[str] | |
| remove_headers_on_redirect: frozenset[str] |
| total: bool | int | None | ||
| connect: int | None | ||
| read: int | None | ||
| redirect: bool | int | None |
There was a problem hiding this comment.
It appears that redirect can never be False, only int, None or True. You'll need to import Literal from typing_extensions.
| redirect: bool | int | None | |
| redirect: Literal[True] | int | None |
| def get_backoff_time(self): ... | ||
| def sleep(self, response: HTTPResponse | None = ...) -> None: ... | ||
| def is_forced_retry(self, method, status_code): ... | ||
| def parse_retry_after(self, retry_after: str): ... |
There was a problem hiding this comment.
| def parse_retry_after(self, retry_after: str): ... | |
| def parse_retry_after(self, retry_after: str) -> float: ... |
| def sleep(self, response: HTTPResponse | None = ...) -> None: ... | ||
| def is_forced_retry(self, method, status_code): ... | ||
| def parse_retry_after(self, retry_after: str): ... | ||
| def get_retry_after(self, response: HTTPResponse): ... |
There was a problem hiding this comment.
| def get_retry_after(self, response: HTTPResponse): ... | |
| def get_retry_after(self, response: HTTPResponse) -> float | None: ... |
| def is_forced_retry(self, method, status_code): ... | ||
| def parse_retry_after(self, retry_after: str): ... | ||
| def get_retry_after(self, response: HTTPResponse): ... | ||
| def sleep_for_retry(self, response: HTTPResponse | None = ...): ... |
There was a problem hiding this comment.
| def sleep_for_retry(self, response: HTTPResponse | None = ...): ... | |
| def sleep_for_retry(self, response: HTTPResponse | None = ...) -> bool: ... |
| def parse_retry_after(self, retry_after: str): ... | ||
| def get_retry_after(self, response: HTTPResponse): ... | ||
| def sleep_for_retry(self, response: HTTPResponse | None = ...): ... | ||
| def sleep(self, response: HTTPResponse | None = ...): ... |
There was a problem hiding this comment.
| def sleep(self, response: HTTPResponse | None = ...): ... | |
| def sleep(self, response: HTTPResponse | None = ...) -> None: ... |
| def get_retry_after(self, response: HTTPResponse): ... | ||
| def sleep_for_retry(self, response: HTTPResponse | None = ...): ... | ||
| def sleep(self, response: HTTPResponse | None = ...): ... | ||
| def is_retry(self, method: str, status_code: int, has_retry_after: bool = ...): ... |
There was a problem hiding this comment.
| def is_retry(self, method: str, status_code: int, has_retry_after: bool = ...): ... | |
| def is_retry(self, method: str, status_code: int, has_retry_after: bool = ...) -> bool: ... |
AlexWaygood
left a comment
There was a problem hiding this comment.
There's still more work to be done to finish annotating many of the methods in this file, but the changes you've made LGTM! (I don't have the power to merge, I'm just a contributor, not a maintainer.)
| backoff_factor: float | ||
| raise_on_redirect: bool | ||
| raise_on_status: bool | ||
| history: tuple[RequestHistory, ...] | tuple[()] |
There was a problem hiding this comment.
Why do we need the empty tuple called out here? The first union member is an arbitrary-length tuple, which includes the empty tuple.
There was a problem hiding this comment.
Oh that's my fault; I suggested that annotation, and didn't think that one through. My bad; you're quite right.
Thanks @AlexWaygood! What else is left there? I intentionally left out methods whose name starts with an _, thinking that those are kind of "private"/internal. |
The remaining changes can be made in other PRs! But this is what's still left to do -- feel free to add it in this PR if you like!
|
|
The |
Thanks @AlexWaygood , I added the missing annotatiions as you suggested. |
| method_whitelist: Collection[str] | None = ..., | ||
| ) -> None: ... | ||
| def new(self, **kw): ... | ||
| def new(self, **kw: Any) -> Retry: ... |
There was a problem hiding this comment.
At runtime this will return an instance of a subclass if it's being called on a subclass of Retry, so we should use a TypeVar to annotate the return-type instead of hardcoding it. (This is different to how they've annotated their code on main, but I think their annotation is wrong.) typeshed's established style is to use the _typeshed.Self TypeVar for these situations (you'll need to do from _typeshed import Self at the top of the file).
| def new(self, **kw: Any) -> Retry: ... | |
| def new(self: Self, **kw: Any) -> Self: ... |
Thank you!! |
| method_whitelist: Collection[str] | None = ..., | ||
| ) -> None: ... | ||
| def new(self, **kw): ... | ||
| def new(self, **kw: Any) -> Self: ... |
There was a problem hiding this comment.
| def new(self, **kw: Any) -> Self: ... | |
| def new(self: Self, **kw: Any) -> Self: ... |
|
@AlexWaygood do you know if it is possible to install |
|
@bugant: New versions of the stubs are released automatically every three hours, starting midnight UTC. So the next version of the package should be released in a bit over two hours. I unfortunately just missed the last release by about five minutes. |
|
Actually, I just manually forced urllib3 to update, types-urllib3 1.26.6 should have these changes. |
Fabulous! Thank you very much @srittau! |

This PR adds the latest naming for
urllib3.util.retrydefault constants.