Skip to content

Update definition of constants in urllib3.util.retry - #6892

Merged
srittau merged 19 commits into
python:masterfrom
feedstock:update-urllib3-stubs
Jan 12, 2022
Merged

srittau merged 19 commits into
python:masterfrom
feedstock:update-urllib3-stubs

Conversation

@bugant

@bugant bugant commented Jan 11, 2022

Copy link
Copy Markdown
Contributor

This PR adds the latest naming for urllib3.util.retry default constants.

@bugant
bugant force-pushed the update-urllib3-stubs branch from 72c4d6c to dc5908e Compare January 11, 2022 12:56
Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
class Retry:
DEFAULT_ALLOWED_METHODS: frozenset[str]
RETRY_AFTER_STATUS_CODES: frozenset[int]
DEFAULT_REMOVE_HEADERS_ON_REDIRECT: frozenset[list[str]]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes good point @AlexWaygood! My bad. I will fix it now. Thank you

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
log: Any

class Retry:
DEFAULT_ALLOWED_METHODS: frozenset[str]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
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)

@AlexWaygood

Copy link
Copy Markdown
Member

Wait, do the deprecated constants in the stub even exist anymore? I can't see them in the source code anywhere...

@bugant

bugant commented Jan 11, 2022

Copy link
Copy Markdown
Contributor Author

do the deprecated constants in the stub even exist anymore? I can't see them in the source

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 ?

@AlexWaygood

Copy link
Copy Markdown
Member

do the deprecated constants in the stub even exist anymore? I can't see them in the source

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

@bugant

bugant commented Jan 11, 2022

Copy link
Copy Markdown
Contributor Author

do the deprecated constants in the stub even exist anymore? I can't see them in the source

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 ?

Those constants have been renamed here urllib3/urllib3@382ab32

@bugant

bugant commented Jan 11, 2022

Copy link
Copy Markdown
Contributor Author

do the deprecated constants in the stub even exist anymore? I can't see them in the source

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

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).

@AlexWaygood

AlexWaygood commented Jan 11, 2022 •

Copy link
Copy Markdown
Member

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. allowed_methods is Optional[Collection[str]] in the source code, so could be Collection[str] | None here — is there a reason for it not to be?

@bugant

bugant commented Jan 11, 2022

Copy link
Copy Markdown
Contributor Author

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. allowed_methods is Optional[Collection[str]] in the source code, so could be Collection[str] | None here — is there a reason for it not to be?

My bad, I looked at an old version of the code, will fix it now. Thanks.

@AlexWaygood

AlexWaygood commented Jan 11, 2022 •

Copy link
Copy Markdown
Member

While we're cleaning up this file, could you also possibly remove the is_forced_retry method? It doesn't appear to exist at runtime; I'm not sure when it was removed, but I can't find any reference in the source code going back as far as 2018. Removing it would allow us to get rid of several entries from the stubtest allowlist for urllib3 (stubtest checks for inconsistencies between the stub and the runtime)

@bugant

bugant commented Jan 11, 2022

Copy link
Copy Markdown
Contributor Author

While we're cleaning up this file, could you also possibly remove the is_forced_retry method? It doesn't appear to exist at runtime; I'm not sure when it was removed, but I can't find any reference in the source code going back as far as 2018. Removing it would allow us to get rid of several entries from the stubtest allowlist for urllib3 (stubtest checks for inconsistencies between the stub and the runtime)

Yes @AlexWaygood , I did update all the methods as well. I'm not sure how to update the stubtest bit. Any hint?

@bugant

bugant commented Jan 11, 2022

Copy link
Copy Markdown
Contributor Author

While we're cleaning up this file, could you also possibly remove the is_forced_retry method? It doesn't appear to exist at runtime; I'm not sure when it was removed, but I can't find any reference in the source code going back as far as 2018. Removing it would allow us to get rid of several entries from the stubtest allowlist for urllib3 (stubtest checks for inconsistencies between the stub and the runtime)

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.

@AlexWaygood

Copy link
Copy Markdown
Member

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):

  • urllib3.Retry.is_forced_retry
  • urllib3.connectionpool.Retry.is_forced_retry
  • urllib3.util.Retry.is_forced_retry
  • urllib3.util.retry.Retry.is_forced_retry

There are some other stubtest failures as well, which I'll help you out with.

@AlexWaygood

Copy link
Copy Markdown
Member

I don't know why black is failing FWIW; the bot should have the power to push to your branch and autoformat your PR for you.

@JelleZijlstra

Copy link
Copy Markdown
Member

the bot should have the power to push to your branch

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.

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
@classmethod
def from_int(cls, retries, redirect=..., default=...): ...
def get_backoff_time(self): ...
def from_int(cls, retries, redirect=..., default=...) -> "Retry": ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No need to have string-literal annotations in stubs; from __future__ import annotations behaviour is enabled by default 🙂 same applies for the increment method

Suggested change
def from_int(cls, retries, redirect=..., default=...) -> "Retry": ...
def from_int(cls, retries, redirect=..., default=...) -> Retry: ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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:

Suggested change
def from_int(cls, retries, redirect=..., default=...) -> "Retry": ...
def from_int(cls: type[Self], retries, redirect=..., default=...) -> Self: ...

@AlexWaygood

Copy link
Copy Markdown
Member

the bot should have the power to push to your branch

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.

Thought that was probably it, but wasn't sure. Thanks!

@bugant

bugant commented Jan 11, 2022

Copy link
Copy Markdown
Contributor Author

Not sure which checkbox I have to tick

@JelleZijlstra

Copy link
Copy Markdown
Member

Screen Shot 2022-01-11 at 6 26 04 AM

It shows up when you create the PR ("Allow edits and access to secrets by maintainers"). Not sure if there's a way to change it later, so you may just have to run Black manually.

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
backoff_factor: float
raise_on_redirect: bool
raise_on_status: bool
history: tuple[Any] | None

@AlexWaygood AlexWaygood Jan 11, 2022 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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)

Suggested change
history: tuple[Any] | None
history: tuple[()] | tuple[RequestHistory, ...]

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
status: int | None
other: int | None
allowed_methods: Collection[str] | None
status_forcelist: Collection[int] | None

@AlexWaygood AlexWaygood Jan 11, 2022 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This also can never be None at runtime; it is set as an empty set if no value is provided in __init__.

Suggested change
status_forcelist: Collection[int] | None
status_forcelist: Collection[int]

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
raise_on_status: bool
history: tuple[Any] | None
respect_retry_after_header: bool
remove_headers_on_redirect: Collection[str]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Suggested change
remove_headers_on_redirect: Collection[str]
remove_headers_on_redirect: frozenset[str]

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
total: bool | int | None
connect: int | None
read: int | None
redirect: bool | int | None

@AlexWaygood AlexWaygood Jan 11, 2022 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It appears that redirect can never be False, only int, None or True. You'll need to import Literal from typing_extensions.

Suggested change
redirect: bool | int | None
redirect: Literal[True] | int | None

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
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): ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
def parse_retry_after(self, retry_after: str): ...
def parse_retry_after(self, retry_after: str) -> float: ...

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
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): ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
def get_retry_after(self, response: HTTPResponse): ...
def get_retry_after(self, response: HTTPResponse) -> float | None: ...

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
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 = ...): ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
def sleep_for_retry(self, response: HTTPResponse | None = ...): ...
def sleep_for_retry(self, response: HTTPResponse | None = ...) -> bool: ...

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
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 = ...): ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
def sleep(self, response: HTTPResponse | None = ...): ...
def sleep(self, response: HTTPResponse | None = ...) -> None: ...

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
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 = ...): ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
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 AlexWaygood left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.)

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
backoff_factor: float
raise_on_redirect: bool
raise_on_status: bool
history: tuple[RequestHistory, ...] | tuple[()]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do we need the empty tuple called out here? The first union member is an arbitrary-length tuple, which includes the empty tuple.

@AlexWaygood AlexWaygood Jan 11, 2022 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh that's my fault; I suggested that annotation, and didn't think that one through. My bad; you're quite right.

@bugant

bugant commented Jan 11, 2022 •

Copy link
Copy Markdown
Contributor Author

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.)

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.

@AlexWaygood

AlexWaygood commented Jan 11, 2022 •

Copy link
Copy Markdown
Member

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.)

Thanks @AlexWaygood! What else is left there?

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!

  • None of the parameters in Retry.__init__, Retry.from_int, or Retry.increment have annotations yet.
  • Retry.new is lacking annotations for its parameters and its return type.
  • Retry.get_backoff_time is missing a return annotation.
  • We can probably do better than Any for the global log variable?

@AlexWaygood

Copy link
Copy Markdown
Member

The log variable is actually pretty obviously a logging.Logger object: https://github.com/urllib3/urllib3/blob/b1f60e44d43b13e5272d5b6003f125af9c25c8ad/src/urllib3/util/retry.py#L22

@bugant

bugant commented Jan 12, 2022

Copy link
Copy Markdown
Contributor Author

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.)

Thanks @AlexWaygood! What else is left there?

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!

* None of the parameters in `Retry.__init__`, `Retry.from_int`, or `Retry.increment` have annotations yet.

* `Retry.new` is lacking annotations for its parameters and its return type.

* `Retry.get_backoff_time` is missing a return annotation.

* We can probably do better than `Any` for the global `log` variable?

Thanks @AlexWaygood , I added the missing annotatiions as you suggested.

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
method_whitelist: Collection[str] | None = ...,
) -> None: ...
def new(self, **kw): ...
def new(self, **kw: Any) -> Retry: ...

@AlexWaygood AlexWaygood Jan 12, 2022 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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).

Suggested change
def new(self, **kw: Any) -> Retry: ...
def new(self: Self, **kw: Any) -> Self: ...

@AlexWaygood

Copy link
Copy Markdown
Member

Thanks @AlexWaygood , I added the missing annotatiions as you suggested.

Thank you!!

Comment thread stubs/urllib3/urllib3/util/retry.pyi Outdated
method_whitelist: Collection[str] | None = ...,
) -> None: ...
def new(self, **kw): ...
def new(self, **kw: Any) -> Self: ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
def new(self, **kw: Any) -> Self: ...
def new(self: Self, **kw: Any) -> Self: ...

@srittau srittau left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks!

@srittau
srittau merged commit 6907d7f into python:master Jan 12, 2022
@bugant

bugant commented Jan 12, 2022

Copy link
Copy Markdown
Contributor Author

@AlexWaygood do you know if it is possible to install types-urllib3 from the master branch? I cannot understand how to do it

@srittau

srittau commented Jan 12, 2022

Copy link
Copy Markdown
Collaborator

@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.

@srittau

srittau commented Jan 12, 2022

Copy link
Copy Markdown
Collaborator

Actually, I just manually forced urllib3 to update, types-urllib3 1.26.6 should have these changes.

@bugant

bugant commented Jan 12, 2022

Copy link
Copy Markdown
Contributor Author

Actually, I just manually forced urllib3 to update, types-urllib3 1.26.6 should have these changes.

Fabulous! Thank you very much @srittau!

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.

4 participants