You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Several of our users work with httpx using tools that have stricter type checking than we currently use.
We've had several pull requests and discussions around this, for example pull requests #2435, #2404, #2266.
I'd like us to take an incremental approach to considering stricter typing throughout the package. This will ensure that we have a series of tightly targeted, reviewable pull requests, rather than bundling too many different changes all up into one.
Here are the various flags that mypy --strict enables. I've marked the ones that we currently have enabled...
Note: Updating this list as we go.
--warn-unused-configs
--disallow-any-generics
--disallow-subclassing-any
--disallow-untyped-calls
--disallow-untyped-defs
--disallow-incomplete-defs
--check-untyped-defs
--disallow-untyped-decorators
--no-implicit-optional
--warn-redundant-casts
--warn-unused-ignores
--warn-return-any
--no-implicit-reexport
--strict-equality
--strict-concatenate
I'd suggest that an approach along these lines:
Individually targeted pull requests which turn on a singlemypy flag, and make the required changes to keep the tests passing.
If we get all of these passing, then switching to enabling mypy --strict.
Once we've done that we can assess if there are any remaining issues with pyright's strict mode, and consider if/how we want to address those.
PR #2435 enables the disallow_any_generics = True flag. That looks like it could be a good starting point.
The project is in a pretty good shape, I must say. Out of the box, you can enable these settings already:
warn_unused_configs = True
disallow_subclassing_any = True
check_untyped_defs = True
disallow_untyped_decorators = True
warn_redundant_casts = True
strict_concatenate = True
and scripts/check passes cleanly with no other changes required.
I'd enable warn_unused_ignores = True too, and remove the 10 type: ignore comments that that then highlights. Those comments do nothing, according to mypy. It's a basic code hygiene check, like Flake8 telling you about unused imports.
That leaves you with, in order of error count, with:
strict_equality = True - 2 errors
disallow_incomplete_defs = True - 7 errors
warn_return_any = True - 12 errors
no_implicit_reexport = True - 36 errors
disallow_untyped_calls = True - 92 errors
and, of course
disallow_any_generics = True - 17 errors
for which my PR has the required changes already. Going by those counts should make it easier to prioritise where to focus your efforts.
Note that some of the reported errors are closely related. The changes in my PR also take care of 5 of the 12 errors for warn_return_any, because the type checker can then follow types flow through the code better and so can see that a specific piece of code doesn't return Any but a concrete type, so take the error numbers as a maximum.
Finally, you could split enabling flags between the library itself, and the test suite. Those 92 errors for disallow_untyped_calls may look daunting, but they are all, without exception, in the test suite, so you can enable this flag in the main mypy configuration, and add disallow_untyped_calls = False under [mypy-tests.*] and get a clean bill of health immediately.
The error count here is misleading, actually. It requires just 4 changes to fix these:
--- a/httpx/_compat.py+++ b/httpx/_compat.py@@ -14,10 +14,12 @@ if typing.TYPE_CHECKING: # pragma: no cover
# The C bindings in `brotli` are recommended for CPython.
# The CFFI bindings in `brotlicffi` are recommended for PyPy and everything else.
try:
- import brotlicffi as brotli+ import brotlicffi++ brotli = brotlicffi
except ImportError: # pragma: nocover
try:
- import brotli+ import brotli as brotli
except ImportError:
brotli = None
--- a/tests/conftest.py+++ b/tests/conftest.py@@ -15,7 +15,7 @@ from cryptography.hazmat.primitives.serialization import (
load_pem_private_key,
)
from uvicorn.config import Config
-from uvicorn.main import Server+from uvicorn.server import Server
from httpx import URL
from tests.concurrency import sleep
--- a/httpx/_models.py+++ b/httpx/_models.py@@ -41,7 +41,7 @@ from ._types import (
ResponseExtensions,
SyncByteStream,
)
-from ._urls import URL+from ._urls import URL as URL
from ._utils import (
guess_json_utf,
is_known_encoding,
If you applied my PR and the 4 changes above, and set disallow_untyped_calls = False for the test suite, you only have ~16 errors across 3 mypy flags left to tackle. The project is in a really good shape!
Okay, since #2479 we've now got mypy --strict passing.
On that basis I'm going to close this ticket off.
Here's a couple of optional follow-ups...
We could also tackle getting to --strict on the test cases too. If anyone's keen on tackling that, then I'd suggest opening a ticket similar to this one, enumerating which cases need to pass, and opening PRs on a case-by-case basis in the same way we've done here.
We could take a look at what errors pyright --strict httpx currently raises. Again, if anyone's keen on tackling that, then I'd suggest opening an issue describing the current pyright output.
Activity
mjpieters commentedon Nov 11, 2022
The project is in a pretty good shape, I must say. Out of the box, you can enable these settings already:
warn_unused_configs = Truedisallow_subclassing_any = Truecheck_untyped_defs = Truedisallow_untyped_decorators = Truewarn_redundant_casts = Truestrict_concatenate = Trueand
scripts/checkpasses cleanly with no other changes required.I'd enable
warn_unused_ignores = Truetoo, and remove the 10type: ignorecomments that that then highlights. Those comments do nothing, according to mypy. It's a basic code hygiene check, like Flake8 telling you about unused imports.That leaves you with, in order of error count, with:
strict_equality = True- 2 errorsdisallow_incomplete_defs = True- 7 errorswarn_return_any = True- 12 errorsno_implicit_reexport = True- 36 errorsdisallow_untyped_calls = True- 92 errorsand, of course
disallow_any_generics = True- 17 errorsfor which my PR has the required changes already. Going by those counts should make it easier to prioritise where to focus your efforts.
Note that some of the reported errors are closely related. The changes in my PR also take care of 5 of the 12 errors for
warn_return_any, because the type checker can then follow types flow through the code better and so can see that a specific piece of code doesn't returnAnybut a concrete type, so take the error numbers as a maximum.Finally, you could split enabling flags between the library itself, and the test suite. Those 92 errors for
disallow_untyped_callsmay look daunting, but they are all, without exception, in the test suite, so you can enable this flag in the main mypy configuration, and adddisallow_untyped_calls = Falseunder[mypy-tests.*]and get a clean bill of health immediately.mjpieters commentedon Nov 11, 2022
The error count here is misleading, actually. It requires just 4 changes to fix these:
If you applied my PR and the 4 changes above, and set
disallow_untyped_calls = Falsefor the test suite, you only have ~16 errors across 3 mypy flags left to tackle. The project is in a really good shape!pmav99 commentedon Nov 12, 2022
There is a minor typo in the diff:
s/brotly/brotli/mjpieters commentedon Nov 12, 2022
Well spotted! On my mac, the
import brotli as brotliimport was being used so I didn't notice. Original diff updated.lovelydinosaur commentedon Dec 2, 2022
Okay, since #2479 we've now got
mypy --strictpassing.On that basis I'm going to close this ticket off.
Here's a couple of optional follow-ups...
--stricton the test cases too. If anyone's keen on tackling that, then I'd suggest opening a ticket similar to this one, enumerating which cases need to pass, and opening PRs on a case-by-case basis in the same way we've done here.pyright --strict httpxcurrently raises. Again, if anyone's keen on tackling that, then I'd suggest opening an issue describing the currentpyrightoutput.