Skip to content

Commit dba60fe

Browse files
authored
Add enhancements to the index.md (#73)
1 parent d4a7201 commit dba60fe

File tree

3 files changed

+131
-82
lines changed

3 files changed

+131
-82
lines changed

docs/index.md

Lines changed: 129 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,13 @@ from pydantic import (
1818
AliasChoices,
1919
AmqpDsn,
2020
BaseModel,
21-
ConfigDict,
2221
Field,
2322
ImportString,
2423
PostgresDsn,
2524
RedisDsn,
2625
)
2726

28-
from pydantic_settings import BaseSettings
27+
from pydantic_settings import BaseSettings, SettingsConfigDict
2928

3029

3130
class SubModel(BaseModel):
@@ -34,17 +33,16 @@ class SubModel(BaseModel):
3433

3534

3635
class Settings(BaseSettings):
37-
auth_key: str = Field(validation_alias='my_auth_key')
38-
api_key: str = Field(validation_alias='my_api_key')
36+
auth_key: str = Field(validation_alias='my_auth_key') # (1)!
3937

4038
redis_dsn: RedisDsn = Field(
4139
'redis://user:pass@localhost:6379/1',
42-
validation_alias=AliasChoices('service_redis_dsn', 'redis_url'),
40+
validation_alias=AliasChoices('service_redis_dsn', 'redis_url'), # (2)!
4341
)
4442
pg_dsn: PostgresDsn = 'postgres://user:pass@localhost:5432/foobar'
4543
amqp_dsn: AmqpDsn = 'amqp://user:pass@localhost:5672/'
4644

47-
special_function: ImportString[Callable[[Any], Any]] = 'math.cos'
45+
special_function: ImportString[Callable[[Any], Any]] = 'math.cos' # (3)!
4846

4947
# to override domains:
5048
# export my_prefix_domains='["foo.com", "bar.com"]'
@@ -54,14 +52,13 @@ class Settings(BaseSettings):
5452
# export my_prefix_more_settings='{"foo": "x", "apple": 1}'
5553
more_settings: SubModel = SubModel()
5654

57-
model_config = ConfigDict(env_prefix='my_prefix_') # defaults to no prefix, i.e. ""
55+
model_config = SettingsConfigDict(env_prefix='my_prefix_') # (4)!
5856

5957

6058
print(Settings().model_dump())
6159
"""
6260
{
6361
'auth_key': 'xxx',
64-
'api_key': 'xxx',
6562
'redis_dsn': Url('redis://user:pass@localhost:6379/1'),
6663
'pg_dsn': Url('postgres://user:pass@localhost:5432/foobar'),
6764
'amqp_dsn': Url('amqp://user:pass@localhost:5672/'),
@@ -72,68 +69,133 @@ print(Settings().model_dump())
7269
"""
7370
```
7471

75-
## Environment variable names
72+
1. The environment variable name is overridden using `validation_alias`. In this case, the environment variable
73+
`my_auth_key` will be read instead of `auth_key`.
74+
75+
Check the [`Field` documentation](/usage/fields/) for more information.
76+
77+
2. The `AliasChoices` class allows to have multiple environment variable names for a single field.
78+
The first environment variable that is found will be used.
79+
80+
Check the [`AliasChoices`](/usage/fields/#aliaspath-and-aliaschoices) for more information.
7681

77-
The following rules are used to determine which environment variable(s) are read for a given field:
82+
3. The `ImportString` class allows to import an object from a string.
83+
In this case, the environment variable `special_function` will be read and the function `math.cos` will be imported.
7884

79-
* By default, the environment variable name is built by concatenating the prefix and field name.
80-
* For example, to override `special_function` above, you could use:
85+
4. The `env_prefix` config setting allows to set a prefix for all environment variables.
8186

82-
export my_prefix_special_function='foo.bar'
87+
Check the [Environment variable names documentation](#environment-variable-names) for more information.
8388

84-
* Note : The default prefix is an empty string.
89+
## Environment variable names
8590

86-
* Custom environment variable names can be set like:
87-
* `Field(validation_alias=...)` (see `api_key` and `redis_dsn` above)
88-
* When specifying custom environment variable names, either a string, `AliasChoices`, `AliasPath` my be provided.
89-
* `env_prefix` is not considered.
90-
* When specifying a `AliasChoices`, order matters: the first detected value is used.
91-
* For example, for `redis_dsn` above, `service_redis_dsn` would take precedence over `redis_url`.
91+
By default, the environment variable name is the same as the field name.
9292

93-
Case-sensitivity can be turned on through the `model_config`:
93+
You can change the prefix for all environment variables by setting the `env_prefix` config setting:
9494

9595
```py
96-
from pydantic import ConfigDict
96+
from pydantic_settings import BaseSettings, SettingsConfigDict
97+
98+
99+
class Settings(BaseSettings):
100+
model_config = SettingsConfigDict(env_prefix='my_prefix_')
101+
102+
auth_key: str = 'xxx' # will be read from `my_prefix_auth_key`
103+
```
104+
105+
!!! note
106+
The default `env_prefix` is `''` (empty string).
107+
108+
If you want to change the environment variable name for a single field, you can use an alias.
109+
110+
There are two ways to do this:
111+
112+
* Using `Field(alias=...)` (see `api_key` above)
113+
* Using `Field(validation_alias=...)` (see `auth_key` above)
114+
115+
Check the [`Field` aliases documentation](/usage/fields#field-aliases) for more information about aliases.
97116

98-
from pydantic_settings import BaseSettings
117+
### Case-sensitivity
118+
119+
By default, environment variable names are case-insensitive.
120+
121+
If you want to make environment variable names case-sensitive, you can set the `case_sensitive` config setting:
122+
123+
```py
124+
from pydantic_settings import BaseSettings, SettingsConfigDict
99125

100126

101127
class Settings(BaseSettings):
102-
model_config = ConfigDict(case_sensitive=True)
128+
model_config = SettingsConfigDict(case_sensitive=True)
103129

104130
redis_host: str = 'localhost'
105131
```
106132

107133
When `case_sensitive` is `True`, the environment variable names must match field names (optionally with a prefix),
108-
so in this example
109-
`redis_host` could only be modified via `export redis_host`. If you want to name environment variables
134+
so in this example `redis_host` could only be modified via `export redis_host`. If you want to name environment variables
110135
all upper-case, you should name attribute all upper-case too. You can still name environment variables anything
111136
you like through `Field(validation_alias=...)`.
112137

113-
In Pydantic **v1** `case_sensitive` is `False` by default and all variable names are converted to lower-case internally.
114-
If you want to define upper-case variable names on nested models like `SubModel` you have to
115-
set `case_sensitive=True` to disable this behaviour.
138+
In case of nested models, the `case_sensitive` setting will be applied to all nested models.
139+
140+
```py
141+
import os
142+
143+
from pydantic import ValidationError
144+
145+
from pydantic_settings import BaseSettings, SettingsConfigDict
146+
147+
148+
class RedisSettings(BaseSettings):
149+
host: str
150+
port: int
151+
152+
153+
class Settings(BaseSettings):
154+
model_config = SettingsConfigDict(case_sensitive=True)
155+
156+
redis: RedisSettings
157+
158+
159+
os.environ['redis'] = '{"host": "localhost", "port": 6379}'
160+
print(Settings().model_dump())
161+
#> {'redis': {'host': 'localhost', 'port': 6379}}
162+
os.environ['redis'] = '{"HOST": "localhost", "port": 6379}' # (1)!
163+
try:
164+
Settings()
165+
except ValidationError as e:
166+
print(e)
167+
"""
168+
2 validation errors for RedisSettings
169+
host
170+
Field required [type=missing, input_value={'HOST': 'localhost', 'port': 6379}, input_type=dict]
171+
For further information visit https://errors.pydantic.dev/2/v/missing
172+
HOST
173+
Extra inputs are not permitted [type=extra_forbidden, input_value='localhost', input_type=str]
174+
For further information visit https://errors.pydantic.dev/2/v/extra_forbidden
175+
"""
176+
```
177+
178+
1. Note that the `host` field is not found because the environment variable name is `HOST` (all upper-case).
116179

117180
!!! note
118181
On Windows, Python's `os` module always treats environment variables as case-insensitive, so the
119182
`case_sensitive` config setting will have no effect - settings will always be updated ignoring case.
120183

121184
## Parsing environment variable values
122185

123-
For most simple field types (such as `int`, `float`, `str`, etc.),
124-
the environment variable value is parsed the same way it would
125-
be if passed directly to the initialiser (as a string).
186+
For most simple field types (such as `int`, `float`, `str`, etc.), the environment variable value is parsed
187+
the same way it would be if passed directly to the initialiser (as a string).
126188

127-
Complex types like `list`, `set`, `dict`, and sub-models are populated from the environment
128-
by treating the environment variable's value as a JSON-encoded string.
189+
Complex types like `list`, `set`, `dict`, and sub-models are populated from the environment by treating the
190+
environment variable's value as a JSON-encoded string.
129191

130192
Another way to populate nested complex variables is to configure your model with the `env_nested_delimiter`
131-
config setting, then use an env variable with a name pointing to the nested module fields.
193+
config setting, then use an environment variable with a name pointing to the nested module fields.
132194
What it does is simply explodes your variable into nested models or dicts.
133195
So if you define a variable `FOO__BAR__BAZ=123` it will convert it into `FOO={'BAR': {'BAZ': 123}}`
134196
If you have multiple variables with the same structure they will be merged.
135197

136-
With the following environment variables:
198+
As an example, given the following environment variables:
137199
```bash
138200
# your environment
139201
export V0=0
@@ -143,12 +205,12 @@ export SUB_MODEL__V3=3
143205
export SUB_MODEL__DEEP__V4=v4
144206
```
145207

146-
You could load a settings module thus:
208+
You could load them into the following settings model:
147209

148210
```py
149-
from pydantic import BaseModel, ConfigDict
211+
from pydantic import BaseModel
150212

151-
from pydantic_settings import BaseSettings
213+
from pydantic_settings import BaseSettings, SettingsConfigDict
152214

153215

154216
class DeepSubModel(BaseModel):
@@ -163,7 +225,7 @@ class SubModel(BaseModel):
163225

164226

165227
class Settings(BaseSettings):
166-
model_config = ConfigDict(env_nested_delimiter='__')
228+
model_config = SettingsConfigDict(env_nested_delimiter='__')
167229

168230
v0: str
169231
sub_model: SubModel
@@ -234,17 +296,12 @@ print(Settings().model_dump())
234296

235297
## Dotenv (.env) support
236298

237-
!!! note
238-
dotenv file parsing requires [python-dotenv](https://pypi.org/project/python-dotenv/) to be installed.
239-
This can be done with either `pip install python-dotenv` or `pip install pydantic[dotenv]`.
240-
241299
Dotenv files (generally named `.env`) are a common pattern that make it easy to use environment variables in a
242300
platform-independent manner.
243301

244-
A dotenv file follows the same general principles of all environment variables,
245-
and looks something like:
302+
A dotenv file follows the same general principles of all environment variables, and it looks like this:
246303

247-
```bash
304+
```bash title=".env"
248305
# ignore comment
249306
ENVIRONMENT="production"
250307
REDIS_ADDRESS=localhost:6379
@@ -254,18 +311,15 @@ MY_VAR='Hello world'
254311

255312
Once you have your `.env` file filled with variables, *pydantic* supports loading it in two ways:
256313

257-
**1.** setting `env_file` (and `env_file_encoding` if you don't want the default encoding of your OS) on `model_config`
258-
in a `BaseSettings` class:
314+
1. Setting the `env_file` (and `env_file_encoding` if you don't want the default encoding of your OS) on `model_config`
315+
in the `BaseSettings` class:
259316

260317
```py test="skip" lint="skip"
261318
class Settings(BaseSettings):
262-
model_config = ConfigDict(env_file='.env', env_file_encoding = 'utf-8')
263-
264-
...
265-
319+
model_config = SettingsConfigDict(env_file='.env', env_file_encoding = 'utf-8')
266320
```
267321

268-
**2.** instantiating a `BaseSettings` derived class with the `_env_file` keyword argument
322+
2. Instantiating the `BaseSettings` derived class with the `_env_file` keyword argument
269323
(and the `_env_file_encoding` if needed):
270324

271325
```py test="skip" lint="skip"
@@ -287,23 +341,15 @@ Passing a file path via the `_env_file` keyword argument on instantiation (metho
287341
the value (if any) set on the `model_config` class. If the above snippets were used in conjunction, `prod.env` would be loaded
288342
while `.env` would be ignored.
289343

290-
If you need to load multiple dotenv files, you can pass the file paths as a `list` or `tuple`.
291-
292-
Later files in the list/tuple will take priority over earlier files.
293-
294-
```py
295-
from pydantic import ConfigDict
296-
297-
from pydantic_settings import BaseSettings
298-
344+
If you need to load multiple dotenv files, you can pass multiple file paths as a tuple or list. The files will be
345+
loaded in order, with each file overriding the previous one.
299346

347+
```py test="skip" lint="skip"
300348
class Settings(BaseSettings):
301-
model_config = ConfigDict(
349+
model_config = SettingsConfigDict(
302350
# `.env.prod` takes priority over `.env`
303351
env_file=('.env', '.env.prod')
304352
)
305-
306-
...
307353
```
308354

309355
You can also use the keyword argument override to tell Pydantic not to load any file at all (even if one is set in
@@ -317,32 +363,29 @@ Pydantic settings consider `extra` config in case of dotenv file. It means if yo
317363
on `model_config` and your dotenv file contains an entry for a field that is not defined in settings model,
318364
it will raise `ValidationError` in settings construction.
319365

320-
## Secret Support
366+
## Secrets
321367

322368
Placing secret values in files is a common pattern to provide sensitive configuration to an application.
323369

324370
A secret file follows the same principal as a dotenv file except it only contains a single value and the file name
325371
is used as the key. A secret file will look like the following:
326372

327-
`/var/run/database_password`:
328-
```
373+
``` title="/var/run/database_password"
329374
super_secret_database_password
330375
```
331376

332377
Once you have your secret files, *pydantic* supports loading it in two ways:
333378

334-
**1.** setting `secrets_dir` on `model_config` in a `BaseSettings` class to the directory where your secret files are stored:
379+
1. Setting the `secrets_dir` on `model_config` in a `BaseSettings` class to the directory where your secret files are stored.
335380

336381
```py test="skip" lint="skip"
337382
class Settings(BaseSettings):
338-
model_config = ConfigDict(secrets_dir='/var/run')
383+
model_config = SettingsConfigDict(secrets_dir='/var/run')
339384

340-
...
341385
database_password: str
342-
343386
```
344387

345-
**2.** instantiating a `BaseSettings` derived class with the `_secrets_dir` keyword argument:
388+
2. Instantiating the `BaseSettings` derived class with the `_secrets_dir` keyword argument:
346389

347390
```py test="skip" lint="skip"
348391
settings = Settings(_secrets_dir='/var/run')
@@ -365,16 +408,18 @@ To use these secrets in a *pydantic* application the process is simple. More inf
365408
and using secrets in Docker see the official
366409
[Docker documentation](https://docs.docker.com/engine/reference/commandline/secret/).
367410

368-
First, define your Settings
411+
First, define your `Settings` class with a `SettingsConfigDict` that specifies the secrets directory.
412+
369413
```py test="skip" lint="skip"
370414
class Settings(BaseSettings):
371-
model_config = ConfigDict(secrets_dir='/run/secrets')
415+
model_config = SettingsConfigDict(secrets_dir='/run/secrets')
372416

373417
my_secret_data: str
374418
```
419+
375420
!!! note
376-
By default Docker uses `/run/secrets` as the target mount point. If you want to use a different location, change
377-
`Config.secrets_dir` accordingly.
421+
By default [Docker uses `/run/secrets`](https://docs.docker.com/engine/swarm/secrets/#how-docker-manages-secrets)
422+
as the target mount point. If you want to use a different location, change `Config.secrets_dir` accordingly.
378423

379424
Then, create your secret via the Docker CLI
380425
```bash
@@ -450,10 +495,13 @@ import json
450495
from pathlib import Path
451496
from typing import Any, Dict, Tuple, Type
452497

453-
from pydantic import ConfigDict
454498
from pydantic.fields import FieldInfo
455499

456-
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource
500+
from pydantic_settings import (
501+
BaseSettings,
502+
PydanticBaseSettingsSource,
503+
SettingsConfigDict,
504+
)
457505

458506

459507
class JsonConfigSettingsSource(PydanticBaseSettingsSource):
@@ -497,7 +545,7 @@ class JsonConfigSettingsSource(PydanticBaseSettingsSource):
497545

498546

499547
class Settings(BaseSettings):
500-
model_config = ConfigDict(env_file_encoding='utf-8')
548+
model_config = SettingsConfigDict(env_file_encoding='utf-8')
501549

502550
foobar: str
503551

0 commit comments

Comments
 (0)