mirror of https://github.com/encode/starlette.git
type config with `None` default as `str | None` instead of `Any` (#1732)
* fix type annotations for config * wip * fix simple case * wip * isort * remove mypy config change * fix test * fix coverage * use assert_type * format * CR * Update tests/test_config.py Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
This commit is contained in:
parent
643c3f20e3
commit
584f22e355
|
@ -9,6 +9,7 @@ databases[sqlite]==0.5.5
|
|||
flake8==3.9.2
|
||||
isort==5.10.1
|
||||
mypy==0.961
|
||||
typing_extensions==4.2.0
|
||||
types-requests==2.26.3
|
||||
types-contextvars==2.4.7
|
||||
types-PyYAML==6.0.4
|
||||
|
|
|
@ -60,6 +60,12 @@ class Config:
|
|||
if env_file is not None and os.path.isfile(env_file):
|
||||
self.file_values = self._read_file(env_file)
|
||||
|
||||
@typing.overload
|
||||
def __call__(
|
||||
self, key: str, *, default: None
|
||||
) -> typing.Optional[str]: # pragma: no cover
|
||||
...
|
||||
|
||||
@typing.overload
|
||||
def __call__(
|
||||
self, key: str, cast: typing.Type[T], default: T = ...
|
||||
|
|
|
@ -1,12 +1,44 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
|
||||
import pytest
|
||||
from typing_extensions import assert_type
|
||||
|
||||
from starlette.config import Config, Environ, EnvironError
|
||||
from starlette.datastructures import URL, Secret
|
||||
|
||||
|
||||
def test_config_types() -> None:
|
||||
"""
|
||||
We use `assert_type` to test the types returned by Config via mypy.
|
||||
"""
|
||||
config = Config(
|
||||
environ={"STR": "some_str_value", "STR_CAST": "some_str_value", "BOOL": "true"}
|
||||
)
|
||||
|
||||
assert_type(config("STR"), str)
|
||||
assert_type(config("STR_DEFAULT", default=""), str)
|
||||
assert_type(config("STR_CAST", cast=str), str)
|
||||
assert_type(config("STR_NONE", default=None), Optional[str])
|
||||
assert_type(config("STR_CAST_NONE", cast=str, default=None), Optional[str])
|
||||
assert_type(config("STR_CAST_STR", cast=str, default=""), str)
|
||||
|
||||
assert_type(config("BOOL", cast=bool), bool)
|
||||
assert_type(config("BOOL_DEFAULT", cast=bool, default=False), bool)
|
||||
assert_type(config("BOOL_NONE", cast=bool, default=None), Optional[bool])
|
||||
|
||||
def cast_to_int(v: Any) -> int:
|
||||
return int(v)
|
||||
|
||||
# our type annotations allow these `cast` and `default` configurations, but
|
||||
# the code will error at runtime.
|
||||
with pytest.raises(ValueError):
|
||||
config("INT_CAST_DEFAULT_STR", cast=cast_to_int, default="true")
|
||||
with pytest.raises(ValueError):
|
||||
config("INT_DEFAULT_STR", cast=int, default="true")
|
||||
|
||||
|
||||
def test_config(tmpdir, monkeypatch):
|
||||
path = os.path.join(tmpdir, ".env")
|
||||
with open(path, "w") as file:
|
||||
|
@ -27,6 +59,7 @@ def test_config(tmpdir, monkeypatch):
|
|||
DATABASE_URL = config("DATABASE_URL", cast=URL)
|
||||
REQUEST_TIMEOUT = config("REQUEST_TIMEOUT", cast=int, default=10)
|
||||
REQUEST_HOSTNAME = config("REQUEST_HOSTNAME")
|
||||
MAIL_HOSTNAME = config("MAIL_HOSTNAME", default=None)
|
||||
SECRET_KEY = config("SECRET_KEY", cast=Secret)
|
||||
UNSET_SECRET = config("UNSET_SECRET", cast=Secret, default=None)
|
||||
EMPTY_SECRET = config("EMPTY_SECRET", cast=Secret, default="")
|
||||
|
@ -40,6 +73,7 @@ def test_config(tmpdir, monkeypatch):
|
|||
assert DATABASE_URL.username == "user"
|
||||
assert REQUEST_TIMEOUT == 10
|
||||
assert REQUEST_HOSTNAME == "example.com"
|
||||
assert MAIL_HOSTNAME is None
|
||||
assert repr(SECRET_KEY) == "Secret('**********')"
|
||||
assert str(SECRET_KEY) == "12345"
|
||||
assert bool(SECRET_KEY)
|
||||
|
|
Loading…
Reference in New Issue