-
-
Notifications
You must be signed in to change notification settings - Fork 780
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
Type-annotate uvicorn/config.py #1067
Conversation
#992 https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain - `certfile` is a required positional argument when running `SSLContext.load_cert_chain`, so annotating as `Optional` (which allows `None`) would not be ideal. Path-like objects are acceptable, so after `from pathlib import Path`, the annotation is `certfile: Union[Path, str]`. - `if self.is_ssl and self.ssl_certfile` will help ensure that the `self.ssl_certfile` required positional argument is present.
`Dict[str, Any]` can be simplified to `dict`.
Yeah, the Somehow related: #1046 |
EDIT: I attempted to add a type alias for the return type of Mypy and tests are passing on my fork. |
Protocol classes are sometimes used for the app kwarg, such as in tests/test_config.py.
https://mypy.readthedocs.io/en/stable/literal_types.html This commit will retain the use of literals to define ASGI protocol versions, but improve and correct the use of literal types. As described in the mypy docs, `Literal["2.0", "3.0"]` is a simpler way to write `Union[Literal["2.0"], Literal["3.0"]]`.
https://mypy.readthedocs.io/en/stable/type_inference_and_annotations.html This is just one of those times when mypy needs a little help.
PR #978 added logic to convert `reload_dirs` from string to list, as shown in `test_reload_dir_is_set()`, so the annotation on reload_dirs will be updated to accept a string.
Prevents mypy `[has-type]` errors ("Cannot determine type of {object}")
#1046 #1067 The relationship between module imports and calls in uvicorn/config.py was previously unclear. `import_from_string` was annotated as returning a `ModuleType`, but was being used as if it were returning callables. Mypy was raising "module not callable" errors, as reported in #1046. Current use cases of `import_from_string` include: - `uvicorn.Config.HTTP_PROTOCOLS`, `uvicorn.Config.WS_PROTOCOLS`: `Type[asyncio.Protocol]` (these are subclasses of asyncio protocols, so they are annotated with `Type[Class]` as explained in the mypy docs - `uvicorn.Config.LOOP_SETUPS`: `Callable` (each loop is a function) - `uvicorn.Config().loaded_app`: `ASGIApplication` (should be an ASGI application instance, like `Starlette()` or `FastAPI()`) Ideally, the return type of `import_from_string` would reflect these use cases, but the complex typing will be difficult to maintain. For easier maintenance, the return type will be `Any`, and objects returned by `import_from_string` will be annotated directly.
/rant on |
#1067 (comment) Alternative to `pathlib.Path` introduced in Python 3.6.
Hopefully almost done 🤞 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another round of comments, sorry this is a massive PR so I was not able to touch on everything at first sight :)
but we're getting there !
#1067 (comment) #1067 (comment) https://github.com/encode/starlette/blob/b6f3578bb2cf6c60e3efe110143409b47f368d36/starlette/config.py#L16 https://github.com/python/cpython/blob/3e1c7167d86a2a928cdcb659094aa10bb5550c4c/Lib/os.py#L737 https://docs.pytest.org/en/latest/reference/reference.html#pytest.FixtureRequest - Add custom types for exceptions and start response - Use `MutableMapping` for `os.environ`: matches starlette/config.py. - Use `pytest.FixtureRequest` for fixture requests. The `param` attribute is optional, so mypy requires a check for the attribute before indexing into it (`getattr(request, "param")`).
#1067 Fixes `[import]` error: Library stubs not installed for "yaml"
Cool, thanks for your reviews! Good feedback. |
tests/test_config.py
Outdated
@@ -166,8 +188,10 @@ def test_ssl_config_combined(tls_certificate_pem_path): | |||
assert config.is_ssl is True | |||
|
|||
|
|||
def asgi2_app(scope): | |||
async def asgi(receive, send): # pragma: nocover | |||
def asgi2_app(scope: Scope) -> Callable: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def asgi2_app(scope: Scope) -> Callable: | |
def asgi2_app(scope: Scope) -> Callable[[ASGIReceiveCallable, ASGISendCallable], Awaitable[None]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we use the asgiref
types instead?
def asgi2_app(scope: Scope) -> ASGI2Application:
async def asgi(
receive: ASGIReceiveCallable, send: ASGISendCallable
) -> None: # pragma: nocover
pass
return asgi # pragma: nocover
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That doesn't work because the ASGI2Application
is all of it. That part is only the callable
that I've annotated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That doesn't work because the
ASGI2Application
is all of it. That part is only thecallable
that I've annotated.
I see. This is related to #1067 (comment) - if we don't want Config(app)
to accept a callable, we might have to refactor these test methods.
Thanks for the PR @br3ndonland :) |
Thank you for your help! |
#1067 (comment) https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain `certfile` is a required argument for `SSLContext.load_cert_chain`. As it is currently, `is_ssl` could return `True` without `certfile`.
Sure, no problem, I updated and the CI checks should be passing now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok 2 more comments on my side. I'm sorry this takes so long but it's a huge piece of review, hopefully we get to the end :)
hopefully you're back @br3ndonland ;) can we try to finish this, I know it took already a while but I feel we're near the end ! |
I'm back! Yes I think we're almost done. Thanks for sticking with it! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I'm concerned I'm happy with it.
@Kludex can you please take another look and clear if necessary the conversation [here] (https://github.com/encode/uvicorn/pull/1067/files#r648495399) (will merge once I'm sure nothing's left unanswered!)
Thanks a lot for your time and patience with us on this @br3ndonland but this was not an easy one !!
Only the |
Done (2560310). I was thinking about removing In a future PR, I will also look into removing |
Merged in latest |
let's roll, thanks a lot @br3ndonland ! |
Thanks @br3ndonland ! :) |
* add types to wsgi module * Add middleware/wsgi.py to setup.cfg * Smoothen out a few (mypy-related) bugs I don't know how to solve the remaining ones * Small Fix: use HTTPRequestEvent * Apply suggestions from PR review * Remove all mypy issues Taking ideas from: https://github.com/encode/uvicorn/blob/dd85cdacf154529ea1c3d12b5bda7808673979f2/uvicorn/middleware/wsgi.py * Adjusting w.r.t. to #1067 * Adjusting w.r.t. to #1067 * Applied suggestions from PR review * Fixed remaining test_wsgi.py mypy issues * Trying by removing "[typeddict-item]" beside "type: ignore" * Revert previous commit * Final fix ... yaayyy! Co-authored-by: Jaakko Lappalainen <jkk.lapp@gmail.com>
* Add changes from PR #992 This commit will bring in changes to uvicorn/config.py added by @Kludex in PR #992, updating for the latest master branch. * Correct SSL type annotations #992 https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain - `certfile` is a required positional argument when running `SSLContext.load_cert_chain`, so annotating as `Optional` (which allows `None`) would not be ideal. Path-like objects are acceptable, so after `from pathlib import Path`, the annotation is `certfile: Union[Path, str]`. - `if self.is_ssl and self.ssl_certfile` will help ensure that the `self.ssl_certfile` required positional argument is present. * Simplify log_config type annotation `Dict[str, Any]` can be simplified to `dict`. * Add type annotation for app #990 #992 app is sometimes a string, as described in uvicorn/main.py. In other cases, especially in the tests, app is an ASGI application callable. This commit will add a type annotation for the app argument to address these use cases. * Simplify if expression in Config().headers * Allow paths to be used for Config(env_file) * Add asyncio Protocol types to class Config kwargs Protocol classes are sometimes used for the app kwarg, such as in tests/test_config.py. * Improve use of Literal type for ASGI interfaces https://mypy.readthedocs.io/en/stable/literal_types.html This commit will retain the use of literals to define ASGI protocol versions, but improve and correct the use of literal types. As described in the mypy docs, `Literal["2.0", "3.0"]` is a simpler way to write `Union[Literal["2.0"], Literal["3.0"]]`. * Add type annotation to Config kwargs in workers.py https://mypy.readthedocs.io/en/stable/type_inference_and_annotations.html This is just one of those times when mypy needs a little help. * Add type annotations to test_config.py PR #978 added logic to convert `reload_dirs` from string to list, as shown in `test_reload_dir_is_set()`, so the annotation on reload_dirs will be updated to accept a string. * Type-annotate constants in uvicorn/config.py Prevents mypy `[has-type]` errors ("Cannot determine type of {object}") * Correct type annotations in uvicorn/importer.py #1046 #1067 The relationship between module imports and calls in uvicorn/config.py was previously unclear. `import_from_string` was annotated as returning a `ModuleType`, but was being used as if it were returning callables. Mypy was raising "module not callable" errors, as reported in #1046. Current use cases of `import_from_string` include: - `uvicorn.Config.HTTP_PROTOCOLS`, `uvicorn.Config.WS_PROTOCOLS`: `Type[asyncio.Protocol]` (these are subclasses of asyncio protocols, so they are annotated with `Type[Class]` as explained in the mypy docs - `uvicorn.Config.LOOP_SETUPS`: `Callable` (each loop is a function) - `uvicorn.Config().loaded_app`: `ASGIApplication` (should be an ASGI application instance, like `Starlette()` or `FastAPI()`) Ideally, the return type of `import_from_string` would reflect these use cases, but the complex typing will be difficult to maintain. For easier maintenance, the return type will be `Any`, and objects returned by `import_from_string` will be annotated directly. * Use os.PathLike for paths in uvicorn/config.py #1067 (comment) Alternative to `pathlib.Path` introduced in Python 3.6. * Use more specific types in test_config.py #1067 (comment) #1067 (comment) https://github.com/encode/starlette/blob/b6f3578bb2cf6c60e3efe110143409b47f368d36/starlette/config.py#L16 https://github.com/python/cpython/blob/3e1c7167d86a2a928cdcb659094aa10bb5550c4c/Lib/os.py#L737 https://docs.pytest.org/en/latest/reference/reference.html#pytest.FixtureRequest - Add custom types for exceptions and start response - Use `MutableMapping` for `os.environ`: matches starlette/config.py. - Use `pytest.FixtureRequest` for fixture requests. The `param` attribute is optional, so mypy requires a check for the attribute before indexing into it (`getattr(request, "param")`). * Install missing YAML type stubs for mypy #1067 Fixes `[import]` error: Library stubs not installed for "yaml" * Add Environ type to test_config.py #1067 (comment) * Add Literal type aliases for web server config #1067 (comment) * Use suggested casing for Literal type aliases #1067 (comment) #1067 (comment) * Restore test_config.py test_app_factory comment #1067 (comment) Incorrectly modified in c436bba. * Simplify event loop setup in config.py #1067 (comment) * Remove old type comment after merging master #1067 (comment) * Assert that certfile is present for SSL context #1067 (comment) https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain `certfile` is a required argument for `SSLContext.load_cert_chain`. As it is currently, `is_ssl` could return `True` without `certfile`. * Restore support for Config(loop='none') #455 e9e59e9 e60ba66 #1067 (comment) * Move WSGI types to uvicorn/_types.py #1067 (comment) * Remove Awaitable from app type annotation #1067 (comment) Co-authored-by: euri10 <euri10@users.noreply.github.com>
* add types to wsgi module * Add middleware/wsgi.py to setup.cfg * Smoothen out a few (mypy-related) bugs I don't know how to solve the remaining ones * Small Fix: use HTTPRequestEvent * Apply suggestions from PR review * Remove all mypy issues Taking ideas from: https://github.com/encode/uvicorn/blob/dd85cdacf154529ea1c3d12b5bda7808673979f2/uvicorn/middleware/wsgi.py * Adjusting w.r.t. to #1067 * Adjusting w.r.t. to #1067 * Applied suggestions from PR review * Fixed remaining test_wsgi.py mypy issues * Trying by removing "[typeddict-item]" beside "type: ignore" * Revert previous commit * Final fix ... yaayyy! Co-authored-by: Jaakko Lappalainen <jkk.lapp@gmail.com>
* Add changes from PR encode#992 This commit will bring in changes to uvicorn/config.py added by @Kludex in PR encode#992, updating for the latest master branch. * Correct SSL type annotations encode#992 https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain - `certfile` is a required positional argument when running `SSLContext.load_cert_chain`, so annotating as `Optional` (which allows `None`) would not be ideal. Path-like objects are acceptable, so after `from pathlib import Path`, the annotation is `certfile: Union[Path, str]`. - `if self.is_ssl and self.ssl_certfile` will help ensure that the `self.ssl_certfile` required positional argument is present. * Simplify log_config type annotation `Dict[str, Any]` can be simplified to `dict`. * Add type annotation for app encode#990 encode#992 app is sometimes a string, as described in uvicorn/main.py. In other cases, especially in the tests, app is an ASGI application callable. This commit will add a type annotation for the app argument to address these use cases. * Simplify if expression in Config().headers * Allow paths to be used for Config(env_file) * Add asyncio Protocol types to class Config kwargs Protocol classes are sometimes used for the app kwarg, such as in tests/test_config.py. * Improve use of Literal type for ASGI interfaces https://mypy.readthedocs.io/en/stable/literal_types.html This commit will retain the use of literals to define ASGI protocol versions, but improve and correct the use of literal types. As described in the mypy docs, `Literal["2.0", "3.0"]` is a simpler way to write `Union[Literal["2.0"], Literal["3.0"]]`. * Add type annotation to Config kwargs in workers.py https://mypy.readthedocs.io/en/stable/type_inference_and_annotations.html This is just one of those times when mypy needs a little help. * Add type annotations to test_config.py PR encode#978 added logic to convert `reload_dirs` from string to list, as shown in `test_reload_dir_is_set()`, so the annotation on reload_dirs will be updated to accept a string. * Type-annotate constants in uvicorn/config.py Prevents mypy `[has-type]` errors ("Cannot determine type of {object}") * Correct type annotations in uvicorn/importer.py encode#1046 encode#1067 The relationship between module imports and calls in uvicorn/config.py was previously unclear. `import_from_string` was annotated as returning a `ModuleType`, but was being used as if it were returning callables. Mypy was raising "module not callable" errors, as reported in encode#1046. Current use cases of `import_from_string` include: - `uvicorn.Config.HTTP_PROTOCOLS`, `uvicorn.Config.WS_PROTOCOLS`: `Type[asyncio.Protocol]` (these are subclasses of asyncio protocols, so they are annotated with `Type[Class]` as explained in the mypy docs - `uvicorn.Config.LOOP_SETUPS`: `Callable` (each loop is a function) - `uvicorn.Config().loaded_app`: `ASGIApplication` (should be an ASGI application instance, like `Starlette()` or `FastAPI()`) Ideally, the return type of `import_from_string` would reflect these use cases, but the complex typing will be difficult to maintain. For easier maintenance, the return type will be `Any`, and objects returned by `import_from_string` will be annotated directly. * Use os.PathLike for paths in uvicorn/config.py encode#1067 (comment) Alternative to `pathlib.Path` introduced in Python 3.6. * Use more specific types in test_config.py encode#1067 (comment) encode#1067 (comment) https://github.com/encode/starlette/blob/b6f3578bb2cf6c60e3efe110143409b47f368d36/starlette/config.py#L16 https://github.com/python/cpython/blob/3e1c7167d86a2a928cdcb659094aa10bb5550c4c/Lib/os.py#L737 https://docs.pytest.org/en/latest/reference/reference.html#pytest.FixtureRequest - Add custom types for exceptions and start response - Use `MutableMapping` for `os.environ`: matches starlette/config.py. - Use `pytest.FixtureRequest` for fixture requests. The `param` attribute is optional, so mypy requires a check for the attribute before indexing into it (`getattr(request, "param")`). * Install missing YAML type stubs for mypy encode#1067 Fixes `[import]` error: Library stubs not installed for "yaml" * Add Environ type to test_config.py encode#1067 (comment) * Add Literal type aliases for web server config encode#1067 (comment) * Use suggested casing for Literal type aliases encode#1067 (comment) encode#1067 (comment) * Restore test_config.py test_app_factory comment encode#1067 (comment) Incorrectly modified in c436bba. * Simplify event loop setup in config.py encode#1067 (comment) * Remove old type comment after merging master encode#1067 (comment) * Assert that certfile is present for SSL context encode#1067 (comment) https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain `certfile` is a required argument for `SSLContext.load_cert_chain`. As it is currently, `is_ssl` could return `True` without `certfile`. * Restore support for Config(loop='none') encode#455 encode/uvicorn@e9e59e9 encode/uvicorn@e60ba66 encode#1067 (comment) * Move WSGI types to uvicorn/_types.py encode#1067 (comment) * Remove Awaitable from app type annotation encode#1067 (comment) Co-authored-by: euri10 <euri10@users.noreply.github.com>
* add types to wsgi module * Add middleware/wsgi.py to setup.cfg * Smoothen out a few (mypy-related) bugs I don't know how to solve the remaining ones * Small Fix: use HTTPRequestEvent * Apply suggestions from PR review * Remove all mypy issues Taking ideas from: https://github.com/encode/uvicorn/blob/dd85cdacf154529ea1c3d12b5bda7808673979f2/uvicorn/middleware/wsgi.py * Adjusting w.r.t. to encode#1067 * Adjusting w.r.t. to encode#1067 * Applied suggestions from PR review * Fixed remaining test_wsgi.py mypy issues * Trying by removing "[typeddict-item]" beside "type: ignore" * Revert previous commit * Final fix ... yaayyy! Co-authored-by: Jaakko Lappalainen <jkk.lapp@gmail.com>
This commit will enable mypy strict mode, and update code accordingly. Type annotations are not used at runtime. The standard library `typing` module includes a `TYPE_CHECKING` constant that is `False` at runtime, but `True` when conducting static type checking prior to runtime. Type imports will be included under `if TYPE_CHECKING:` conditions. These conditions will be ignored when calculating test coverage. https://docs.python.org/3/library/typing.html The Python standard library `logging.config` module uses type stubs. The typeshed types for the `logging.config` module are used solely for type-checking usage of the `logging.config` module itself. They cannot be imported and used to type annotate other modules. For this reason, dict config types will be vendored into a module in the inboard package. https://github.com/python/typeshed/blob/main/stdlib/logging/config.pyi The ASGI application in `inboard.app.main_base` will be updated to ASGI3 and type-annotated with `asgiref.typing`. Note that, while Uvicorn uses `asgiref.typing`, Starlette does not. The type signature expected by the Starlette/FastAPI `TestClient` therefore does not match `asgiref.typing.ASGIApplication`. A mypy `type: ignore[arg-type]` comment will be used to resolve this difference. https://asgi.readthedocs.io/en/stable/specs/main.html Also note that, while the `asgiref` package was a runtime dependency of Uvicorn 0.17.6, it was later removed from Uvicorn's runtime dependencies in 0.18.0 (encode/uvicorn#1305, encode/uvicorn#1532). However, `asgiref` is still used to type-annotate Uvicorn, so any downstream projects like inboard that type-check Uvicorn objects must also install `asgiref`. Therefore, `asgiref` will be added to inboard's development dependencies to ensure that type checking continues to work as expected. A Uvicorn options type will be added to a new inboard types module. The Uvicorn options type will be a `TypedDict` with fields corresponding to arguments to `uvicorn.run`. This type can be used to check arguments passed to `uvicorn.run`, which is how `inboard.start` runs Uvicorn. Uvicorn 0.17.6 is not fully type-annotated, and Uvicorn does not ship with a `py.typed` marker file until 0.19.0. It would be convenient to generate types dynamically with something like `getattr(uvicorn.run, "__annotations__")` (Python 3.9 or earlier) or `inspect.get_annotations(uvicorn.run)` (Python 3.10 or later). https://docs.python.org/3/howto/annotations.html It could look something like this: ```py UvicornOptions = TypedDict( # type: ignore[misc] "UvicornOptions", inspect.get_annotations(uvicorn.run), total=False, ) ``` Note the `type: ignore[misc]` comment. Mypy raises a `misc` error: `TypedDict() expects a dictionary literal as the second argument`. Unfortunately, `TypedDict` types are not intended to be generated dynamically, because they exist for the benefit of static type checking (python/mypy#3932, python/mypy#4128, python/mypy#13940). Furthermore, prior to Uvicorn 0.18.0, `uvicorn.run()` didn't enumerate keyword arguments, but instead accepted `kwargs` and passed them to `uvicorn.Config.__init__()` (encode/uvicorn#1423). The annotations from `uvicorn.Config.__init__()` would need to be used instead. Even after Uvicorn 0.18.0, the signatures of the two functions are not exactly the same (encode/uvicorn#1545), so it helps to have a static type defined. There will be some other differences from `uvicorn.run()`: - The `app` argument to `uvicorn.run()` accepts an un-parametrized `Callable` because Uvicorn tests use callables (encode/uvicorn#1067). It is not necessary for other packages to accept `Callable`, and it would need to be parametrized to pass mypy strict mode anyway. For these reasons, `Callable` will not be accepted in this type. - The `log_config` argument will use the new inboard dict config type instead of `dict[str, Any]` for stricter type checking.
* Add changes from PR #992 This commit will bring in changes to uvicorn/config.py added by @Kludex in PR #992, updating for the latest master branch. * Correct SSL type annotations encode/uvicorn#992 https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain - `certfile` is a required positional argument when running `SSLContext.load_cert_chain`, so annotating as `Optional` (which allows `None`) would not be ideal. Path-like objects are acceptable, so after `from pathlib import Path`, the annotation is `certfile: Union[Path, str]`. - `if self.is_ssl and self.ssl_certfile` will help ensure that the `self.ssl_certfile` required positional argument is present. * Simplify log_config type annotation `Dict[str, Any]` can be simplified to `dict`. * Add type annotation for app encode/uvicorn#990 encode/uvicorn#992 app is sometimes a string, as described in uvicorn/main.py. In other cases, especially in the tests, app is an ASGI application callable. This commit will add a type annotation for the app argument to address these use cases. * Simplify if expression in Config().headers * Allow paths to be used for Config(env_file) * Add asyncio Protocol types to class Config kwargs Protocol classes are sometimes used for the app kwarg, such as in tests/test_config.py. * Improve use of Literal type for ASGI interfaces https://mypy.readthedocs.io/en/stable/literal_types.html This commit will retain the use of literals to define ASGI protocol versions, but improve and correct the use of literal types. As described in the mypy docs, `Literal["2.0", "3.0"]` is a simpler way to write `Union[Literal["2.0"], Literal["3.0"]]`. * Add type annotation to Config kwargs in workers.py https://mypy.readthedocs.io/en/stable/type_inference_and_annotations.html This is just one of those times when mypy needs a little help. * Add type annotations to test_config.py PR #978 added logic to convert `reload_dirs` from string to list, as shown in `test_reload_dir_is_set()`, so the annotation on reload_dirs will be updated to accept a string. * Type-annotate constants in uvicorn/config.py Prevents mypy `[has-type]` errors ("Cannot determine type of {object}") * Correct type annotations in uvicorn/importer.py encode/uvicorn#1046 encode/uvicorn#1067 The relationship between module imports and calls in uvicorn/config.py was previously unclear. `import_from_string` was annotated as returning a `ModuleType`, but was being used as if it were returning callables. Mypy was raising "module not callable" errors, as reported in #1046. Current use cases of `import_from_string` include: - `uvicorn.Config.HTTP_PROTOCOLS`, `uvicorn.Config.WS_PROTOCOLS`: `Type[asyncio.Protocol]` (these are subclasses of asyncio protocols, so they are annotated with `Type[Class]` as explained in the mypy docs - `uvicorn.Config.LOOP_SETUPS`: `Callable` (each loop is a function) - `uvicorn.Config().loaded_app`: `ASGIApplication` (should be an ASGI application instance, like `Starlette()` or `FastAPI()`) Ideally, the return type of `import_from_string` would reflect these use cases, but the complex typing will be difficult to maintain. For easier maintenance, the return type will be `Any`, and objects returned by `import_from_string` will be annotated directly. * Use os.PathLike for paths in uvicorn/config.py encode/uvicorn#1067 (comment) Alternative to `pathlib.Path` introduced in Python 3.6. * Use more specific types in test_config.py encode/uvicorn#1067 (comment) encode/uvicorn#1067 (comment) https://github.com/encode/starlette/blob/b6f3578bb2cf6c60e3efe110143409b47f368d36/starlette/config.py#L16 https://github.com/python/cpython/blob/3e1c7167d86a2a928cdcb659094aa10bb5550c4c/Lib/os.py#L737 https://docs.pytest.org/en/latest/reference/reference.html#pytest.FixtureRequest - Add custom types for exceptions and start response - Use `MutableMapping` for `os.environ`: matches starlette/config.py. - Use `pytest.FixtureRequest` for fixture requests. The `param` attribute is optional, so mypy requires a check for the attribute before indexing into it (`getattr(request, "param")`). * Install missing YAML type stubs for mypy encode/uvicorn#1067 Fixes `[import]` error: Library stubs not installed for "yaml" * Add Environ type to test_config.py encode/uvicorn#1067 (comment) * Add Literal type aliases for web server config encode/uvicorn#1067 (comment) * Use suggested casing for Literal type aliases encode/uvicorn#1067 (comment) encode/uvicorn#1067 (comment) * Restore test_config.py test_app_factory comment encode/uvicorn#1067 (comment) Incorrectly modified in c436bba. * Simplify event loop setup in config.py encode/uvicorn#1067 (comment) * Remove old type comment after merging master encode/uvicorn#1067 (comment) * Assert that certfile is present for SSL context encode/uvicorn#1067 (comment) https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain `certfile` is a required argument for `SSLContext.load_cert_chain`. As it is currently, `is_ssl` could return `True` without `certfile`. * Restore support for Config(loop='none') encode/uvicorn#455 encode/uvicorn@e9e59e9 encode/uvicorn@e60ba66 encode/uvicorn#1067 (comment) * Move WSGI types to uvicorn/_types.py encode/uvicorn#1067 (comment) * Remove Awaitable from app type annotation encode/uvicorn#1067 (comment) Co-authored-by: euri10 <euri10@users.noreply.github.com>
* Add changes from PR #992 This commit will bring in changes to uvicorn/config.py added by @Kludex in PR #992, updating for the latest master branch. * Correct SSL type annotations encode/uvicorn#992 https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain - `certfile` is a required positional argument when running `SSLContext.load_cert_chain`, so annotating as `Optional` (which allows `None`) would not be ideal. Path-like objects are acceptable, so after `from pathlib import Path`, the annotation is `certfile: Union[Path, str]`. - `if self.is_ssl and self.ssl_certfile` will help ensure that the `self.ssl_certfile` required positional argument is present. * Simplify log_config type annotation `Dict[str, Any]` can be simplified to `dict`. * Add type annotation for app encode/uvicorn#990 encode/uvicorn#992 app is sometimes a string, as described in uvicorn/main.py. In other cases, especially in the tests, app is an ASGI application callable. This commit will add a type annotation for the app argument to address these use cases. * Simplify if expression in Config().headers * Allow paths to be used for Config(env_file) * Add asyncio Protocol types to class Config kwargs Protocol classes are sometimes used for the app kwarg, such as in tests/test_config.py. * Improve use of Literal type for ASGI interfaces https://mypy.readthedocs.io/en/stable/literal_types.html This commit will retain the use of literals to define ASGI protocol versions, but improve and correct the use of literal types. As described in the mypy docs, `Literal["2.0", "3.0"]` is a simpler way to write `Union[Literal["2.0"], Literal["3.0"]]`. * Add type annotation to Config kwargs in workers.py https://mypy.readthedocs.io/en/stable/type_inference_and_annotations.html This is just one of those times when mypy needs a little help. * Add type annotations to test_config.py PR #978 added logic to convert `reload_dirs` from string to list, as shown in `test_reload_dir_is_set()`, so the annotation on reload_dirs will be updated to accept a string. * Type-annotate constants in uvicorn/config.py Prevents mypy `[has-type]` errors ("Cannot determine type of {object}") * Correct type annotations in uvicorn/importer.py encode/uvicorn#1046 encode/uvicorn#1067 The relationship between module imports and calls in uvicorn/config.py was previously unclear. `import_from_string` was annotated as returning a `ModuleType`, but was being used as if it were returning callables. Mypy was raising "module not callable" errors, as reported in #1046. Current use cases of `import_from_string` include: - `uvicorn.Config.HTTP_PROTOCOLS`, `uvicorn.Config.WS_PROTOCOLS`: `Type[asyncio.Protocol]` (these are subclasses of asyncio protocols, so they are annotated with `Type[Class]` as explained in the mypy docs - `uvicorn.Config.LOOP_SETUPS`: `Callable` (each loop is a function) - `uvicorn.Config().loaded_app`: `ASGIApplication` (should be an ASGI application instance, like `Starlette()` or `FastAPI()`) Ideally, the return type of `import_from_string` would reflect these use cases, but the complex typing will be difficult to maintain. For easier maintenance, the return type will be `Any`, and objects returned by `import_from_string` will be annotated directly. * Use os.PathLike for paths in uvicorn/config.py encode/uvicorn#1067 (comment) Alternative to `pathlib.Path` introduced in Python 3.6. * Use more specific types in test_config.py encode/uvicorn#1067 (comment) encode/uvicorn#1067 (comment) https://github.com/encode/starlette/blob/b6f3578bb2cf6c60e3efe110143409b47f368d36/starlette/config.py#L16 https://github.com/python/cpython/blob/3e1c7167d86a2a928cdcb659094aa10bb5550c4c/Lib/os.py#L737 https://docs.pytest.org/en/latest/reference/reference.html#pytest.FixtureRequest - Add custom types for exceptions and start response - Use `MutableMapping` for `os.environ`: matches starlette/config.py. - Use `pytest.FixtureRequest` for fixture requests. The `param` attribute is optional, so mypy requires a check for the attribute before indexing into it (`getattr(request, "param")`). * Install missing YAML type stubs for mypy encode/uvicorn#1067 Fixes `[import]` error: Library stubs not installed for "yaml" * Add Environ type to test_config.py encode/uvicorn#1067 (comment) * Add Literal type aliases for web server config encode/uvicorn#1067 (comment) * Use suggested casing for Literal type aliases encode/uvicorn#1067 (comment) encode/uvicorn#1067 (comment) * Restore test_config.py test_app_factory comment encode/uvicorn#1067 (comment) Incorrectly modified in c436bba. * Simplify event loop setup in config.py encode/uvicorn#1067 (comment) * Remove old type comment after merging master encode/uvicorn#1067 (comment) * Assert that certfile is present for SSL context encode/uvicorn#1067 (comment) https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain `certfile` is a required argument for `SSLContext.load_cert_chain`. As it is currently, `is_ssl` could return `True` without `certfile`. * Restore support for Config(loop='none') encode/uvicorn#455 encode/uvicorn@e9e59e9 encode/uvicorn@e60ba66 encode/uvicorn#1067 (comment) * Move WSGI types to uvicorn/_types.py encode/uvicorn#1067 (comment) * Remove Awaitable from app type annotation encode/uvicorn#1067 (comment) Co-authored-by: euri10 <euri10@users.noreply.github.com>
Description
This PR will add type annotations and mypy type checking to uvicorn/config.py, the associated tests in tests/test_config.py, and the related code that is impacted by the new type annotations.
Supersedes the changes to uvicorn/config.py in #992, and closes #1046.
Changes
uvicorn/config.py
certfile
is a required positional argument when runningSSLContext.load_cert_chain
, so annotating asOptional
(which allowsNone
) would not be ideal. Path-like objects are acceptable, so afterfrom pathlib import Path
, the annotation iscertfile: Union[Path, str]
.if self.is_ssl and self.ssl_certfile
will help ensure that theself.ssl_certfile
required positional argument is present.log_config
type annotation (b2a1548)Config().headers
(b188fd1)Config(env_file)
(d31fddd)Config
kwargs (9c8e70d): Protocol classes are sometimes used for the app kwarg, such as in tests/test_config.py.Literal
type for ASGI interfaces (14cb546)Literal["2.0", "3.0"]
is a simpler way to writeUnion[Literal["2.0"], Literal["3.0"]]
.Config
kwargs in uvicorn/workers.py (459544d)[has-type]
errors ("Cannot determine type of {object}")uvicorn/importer.py
Correct type annotations in uvicorn/importer.py (e9e59e9)
The relationship between module imports and calls in uvicorn/config.py was previously unclear. In #991,
import_from_string
was annotated as returning aModuleType
, but the function is used to return callables and other types. Mypy was raising "module not callable" errors, as reported in #1046.uvicorn/uvicorn/config.py
Lines 317 to 325 in 87da6cf
Current use cases of
import_from_string
include:uvicorn.Config.HTTP_PROTOCOLS
,uvicorn.Config.WS_PROTOCOLS
:Type[asyncio.Protocol]
(these are subclasses of asyncio protocols, so they are annotated withType[Class]
as explained in the mypy docs)uvicorn.Config.LOOP_SETUPS
:Callable
(each of the loops is a function)uvicorn.Config().loaded_app
:ASGIApplication
(should be an ASGI application instance, likeStarlette()
orFastAPI()
)Ideally, the return type of
import_from_string
would reflect these use cases, but the complex typing will be difficult to maintain. For easier maintenance, the return type will beAny
, and objects returned byimport_from_string
will be annotated directly.Related
#990
#991
#992
#998
#1046