feat: add support for case insensitive env names (#313)

* feat: add support for case insensitive env names

Closes #277

* feedback: just alias os.environ

* doc: update history

* doc: mention case_insensitive option

* refactor: feedback if-else expression assignment

* fix: formatting

* chore: encode black configuration in file to support IDEs

* docs: fix example

* feedback: no suppport for IDEs in this PR

* feedback: style
This commit is contained in:
Jason Kuhrt
2018-11-26 09:57:09 -05:00
committed by Samuel Colvin
parent 94706bc834
commit a0aa9e78cd
5 changed files with 50 additions and 7 deletions
+1
View File
@@ -8,6 +8,7 @@ v0.16.0 (2018-XX-XX)
* refactor schema generation to be compatible with JSON Schema and OpenAPI specs, #308 by @tiangolo
* add ``schema`` to ``schema`` module to generate top-level schemas from base models, #308 by @tiangolo
* add ``case_insensitive`` option to ``BaseSettings`` ``Config``, #277 by @jasonkuhrt
v0.15.0 (2018-11-18)
....................
@@ -0,0 +1,8 @@
from pydantic import BaseSettings
class Settings(BaseSettings):
redis_host = 'localhost'
class Config:
case_insensitive = True
+7 -1
View File
@@ -231,7 +231,7 @@ Outputs:
The generated schemas are compliant with the specifications:
`JSON Schema Core <https://json-schema.org/latest/json-schema-core.html>`__,
`JSON Schema Validation <https://json-schema.org/latest/json-schema-validation.html>`__ and
`JSON Schema Validation <https://json-schema.org/latest/json-schema-validation.html>`__ and
`OpenAPI <https://github.com/OAI/OpenAPI-Specification>`__.
``BaseModel.schema`` will return a dict of the schema, while ``BaseModel.schema_json`` will return a JSON string
@@ -459,6 +459,12 @@ Here ``redis_port`` could be modified via ``export MY_PREFIX_REDIS_PORT=6380`` o
Complex types like ``list``, ``set``, ``dict`` and submodels can be set by using JSON environment variables.
Environment variables can be read in a case insensitive manner:
.. literalinclude:: examples/settings_case_insensitive.py
Here ``redis_port`` could be modified via ``export APP_REDIS_HOST``, ``export app_redis_host``, ``export app_REDIS_host``, etc.
Dynamic model creation
......................
+19 -6
View File
@@ -21,7 +21,9 @@ class BaseSettings(BaseModel):
"""
Base class for settings, allowing values to be overridden by environment variables.
Environment variables must be upper case. Eg. to override foobar, `export APP_FOOBAR="whatever"`.
Environment variables must be upper case and prefixed by APP_ by default. Eg. to override foobar,
`export APP_FOOBAR="whatever"`. To change this behaviour set Config options case_insensitive and
env_prefix.
This is useful in production for secrets you do not wish to save in code, it places nicely with docker(-compose),
Heroku and any 12 factor app design.
@@ -36,23 +38,34 @@ class BaseSettings(BaseModel):
Substitute environment variables into values.
"""
d = {}
if self.__config__.case_insensitive:
env_vars = {k.lower(): v for (k, v) in os.environ.items()}
else:
env_vars = os.environ
for field in self.__fields__.values():
if field.has_alias:
env_name = field.alias
else:
env_name = self.__config__.env_prefix + field.name.upper()
env_var = os.getenv(env_name, None)
if env_var:
env_name_ = env_name.lower() if self.__config__.case_insensitive else env_name
env_val = env_vars.get(env_name_, None)
if env_val:
if _complex_field(field):
try:
env_var = json.loads(env_var)
env_val = json.loads(env_val)
except ValueError as e:
raise SettingsError(f'error parsing JSON for "{env_name}"') from e
d[field.alias] = env_var
d[field.alias] = env_val
return d
class Config:
env_prefix = 'APP_'
env_prefix = "APP_"
validate_all = True
ignore_extra = False
arbitrary_types_allowed = True
case_insensitive = False
+15
View File
@@ -110,3 +110,18 @@ def test_alias_matches_name(env):
env.set('foobar', 'xxx')
s = Settings()
assert s.foobar == 'xxx'
def test_case_insensitive(env):
class Settings(BaseSettings):
foo: str
bAR: str
class Config:
case_insensitive = True
env.set('apP_foO', 'foo')
env.set('app_bar', 'bar')
s = Settings()
assert s.foo == 'foo'
assert s.bAR == 'bar'