From b84917b932498218270f7b9dd48a665b136b8d12 Mon Sep 17 00:00:00 2001 From: "alex.oleshkevich" Date: Thu, 29 Dec 2022 21:52:31 +0300 Subject: [PATCH 1/5] add Config.env_prefix option --- docs/config.md | 27 +++++++++++++++++++++++---- starlette/config.py | 3 +++ tests/test_config.py | 10 ++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/docs/config.md b/docs/config.md index a879374dc..ea5c23f54 100644 --- a/docs/config.md +++ b/docs/config.md @@ -40,9 +40,9 @@ ALLOWED_HOSTS=127.0.0.1, localhost The order in which configuration values are read is: -* From an environment variable. -* From the ".env" file. -* The default value given in `config`. +- From an environment variable. +- From the ".env" file. +- The default value given in `config`. If none of those match, then `config(...)` will raise an error. @@ -100,7 +100,7 @@ keys in the environment. Rather than reading or writing from `os.environ`, you should use Starlette's `environ` instance. This instance is a mapping onto the standard `os.environ` that additionally protects you by raising an error if any environment variable -is set *after* the point that it has already been read by the configuration. +is set _after_ the point that it has already been read by the configuration. If you're using `pytest`, then you can setup any initial environment in `tests/conftest.py`. @@ -113,6 +113,25 @@ from starlette.config import environ environ['TESTING'] = 'TRUE' ``` +## Reading prefixed environment variables + +You can namespace the environment variables by setting `env_prefix` argument. + +**myproject/settings.py**: + +```python +import os +from starlette.config import Config + +os.environ['APP_DEBUG'] = 'yes' +os.environ['ENVIRONMENT'] = 'dev' + +config = Config(env_prefix='APP_') + +DEBUG = config('DEBUG') # lookups APP_DEBUG, returns "yes" +DEBUG = config('ENVIRONMENT') # lookups APP_ENVIRONMENT, raises KeyError as variable is not defined +``` + ## A full example Structuring large applications can be complex. You need proper separation of diff --git a/starlette/config.py b/starlette/config.py index 8c58b2738..795232cf6 100644 --- a/starlette/config.py +++ b/starlette/config.py @@ -54,8 +54,10 @@ def __init__( self, env_file: typing.Optional[typing.Union[str, Path]] = None, environ: typing.Mapping[str, str] = environ, + env_prefix: str = "", ) -> None: self.environ = environ + self.env_prefix = env_prefix self.file_values: typing.Dict[str, str] = {} if env_file is not None and os.path.isfile(env_file): self.file_values = self._read_file(env_file) @@ -103,6 +105,7 @@ def get( cast: typing.Optional[typing.Callable] = None, default: typing.Any = undefined, ) -> typing.Any: + key = self.env_prefix + key if key in self.environ: value = self.environ[key] return self._perform_cast(key, value, cast) diff --git a/tests/test_config.py b/tests/test_config.py index d33000389..dabf1997a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -127,3 +127,13 @@ def test_environ(): environ = Environ() assert list(iter(environ)) == list(iter(os.environ)) assert len(environ) == len(os.environ) + + +def test_config_with_env_prefix(tmpdir, monkeypatch): + config = Config( + environ={"APP_DEBUG": "value", "ENVIRONMENT": "dev"}, env_prefix="APP_" + ) + assert config.get("DEBUG") == "value" + + with pytest.raises(KeyError): + assert config.get("ENVIRONMENT") == "value" From 85227048f36b646b9b02586b09cdcd5717707ca8 Mon Sep 17 00:00:00 2001 From: "alex.oleshkevich" Date: Thu, 29 Dec 2022 21:53:42 +0300 Subject: [PATCH 2/5] fix variable name in docs --- docs/config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config.md b/docs/config.md index ea5c23f54..301216680 100644 --- a/docs/config.md +++ b/docs/config.md @@ -129,7 +129,7 @@ os.environ['ENVIRONMENT'] = 'dev' config = Config(env_prefix='APP_') DEBUG = config('DEBUG') # lookups APP_DEBUG, returns "yes" -DEBUG = config('ENVIRONMENT') # lookups APP_ENVIRONMENT, raises KeyError as variable is not defined +ENVIRONMENT = config('ENVIRONMENT') # lookups APP_ENVIRONMENT, raises KeyError as variable is not defined ``` ## A full example From 81a641ced80819775e174ecb08f13061eb18c935 Mon Sep 17 00:00:00 2001 From: "alex.oleshkevich" Date: Thu, 29 Dec 2022 21:54:44 +0300 Subject: [PATCH 3/5] simplify test case --- tests/test_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_config.py b/tests/test_config.py index dabf1997a..70b0a4812 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -136,4 +136,4 @@ def test_config_with_env_prefix(tmpdir, monkeypatch): assert config.get("DEBUG") == "value" with pytest.raises(KeyError): - assert config.get("ENVIRONMENT") == "value" + config.get("ENVIRONMENT") From 9a6cf900a21d252f9c172d5004980fc3e5030c1b Mon Sep 17 00:00:00 2001 From: "alex.oleshkevich" Date: Thu, 29 Dec 2022 21:56:36 +0300 Subject: [PATCH 4/5] rollback markdown formatting --- docs/config.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/config.md b/docs/config.md index 301216680..c31e94ca4 100644 --- a/docs/config.md +++ b/docs/config.md @@ -40,9 +40,9 @@ ALLOWED_HOSTS=127.0.0.1, localhost The order in which configuration values are read is: -- From an environment variable. -- From the ".env" file. -- The default value given in `config`. +* From an environment variable. +* From the ".env" file. +* The default value given in `config`. If none of those match, then `config(...)` will raise an error. @@ -100,7 +100,7 @@ keys in the environment. Rather than reading or writing from `os.environ`, you should use Starlette's `environ` instance. This instance is a mapping onto the standard `os.environ` that additionally protects you by raising an error if any environment variable -is set _after_ the point that it has already been read by the configuration. +is set *after* the point that it has already been read by the configuration. If you're using `pytest`, then you can setup any initial environment in `tests/conftest.py`. From c989bd469af70963f814a5b58a74ad82f94a0401 Mon Sep 17 00:00:00 2001 From: Alex Oleshkevich Date: Sun, 22 Jan 2023 18:06:39 +0300 Subject: [PATCH 5/5] Update docs/config.md Co-authored-by: Marcelo Trylesinski --- docs/config.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/config.md b/docs/config.md index c31e94ca4..853dff3d4 100644 --- a/docs/config.md +++ b/docs/config.md @@ -117,9 +117,7 @@ environ['TESTING'] = 'TRUE' You can namespace the environment variables by setting `env_prefix` argument. -**myproject/settings.py**: - -```python +```python title="myproject/settings.py" import os from starlette.config import Config