You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* ✨ Update settings examples to use lru_cache
* 📝 Update docs for Settings, using @lru_cache
* 🎨 Update lru_cache colors to show difference in stored values
These environment variables can only handle text strings, as they are external to Python and have to be compatible with other programs and the rest of the system (and even with different operating systems, as Linux, Windows, macOS).
121
121
122
-
That means that any value read in Python from an environment variable will be a `str`, and any conversion to a different type or validation has be done in code.
122
+
That means that any value read in Python from an environment variable will be a `str`, and any conversion to a different type or validation has to be done in code.
123
123
124
124
## Pydantic `Settings`
125
125
@@ -137,6 +137,9 @@ You can use all the same validation features and tools you use for Pydantic mode
137
137
{!../../../docs_src/settings/tutorial001.py!}
138
138
```
139
139
140
+
!!! tip
141
+
If you want something quick to copy and paste, don't use this example, use the last one below.
142
+
140
143
Then, when you create an instance of that `Settings` class (in this case, in the `settings` object), Pydantic will read the environment variables in a case-insensitive way, so, an upper-case variable `APP_NAME` will still be read for the attribute `app_name`.
141
144
142
145
Next it will convert and validate the data. So, when you use that `settings` object, you will have data of the types you declared (e.g. `items_per_user` will be an `int`).
@@ -151,7 +154,7 @@ Then you can use the new `settings` object in your application:
151
154
152
155
### Run the server
153
156
154
-
Then you would run the server passing the configurations as environment variables, for example you could set an `ADMIN_EMAIL` and `APP_NAME` with:
157
+
Next, you would run the server passing the configurations as environment variables, for example you could set an `ADMIN_EMAIL` and `APP_NAME` with:
155
158
156
159
<divclass="termy">
157
160
@@ -174,7 +177,7 @@ And the `items_per_user` would keep its default value of `50`.
174
177
175
178
## Settings in another module
176
179
177
-
You could put those settings in another module file as you saw in [Bigger Applications - Multiple Files](bigger-applications.md){.internal-link target=_blank}.
180
+
You could put those settings in another module file as you saw in [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}.
178
181
179
182
For example, you could have a file `config.py` with:
180
183
@@ -189,7 +192,7 @@ And then use it in a file `main.py`:
189
192
```
190
193
191
194
!!! tip
192
-
You would also need a file `__init__.py` as you saw on [Bigger Applications - Multiple Files](bigger-applications.md){.internal-link target=_blank}.
195
+
You would also need a file `__init__.py` as you saw on [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}.
193
196
194
197
## Settings in a dependency
195
198
@@ -207,18 +210,19 @@ Coming from the previous example, your `config.py` file could look like:
207
210
208
211
Notice that now we don't create a default instance `settings = Settings()`.
209
212
210
-
Instead we declare its type as `Settings`, but the value as `None`.
211
-
212
213
### The main app file
213
214
214
-
Now we create a dependency that returns the `settings` object if we already created it.
215
-
216
-
Otherwise we create a new one, assign it to `config.settings` and then return it from the dependency.
215
+
Now we create a dependency that returns a new `config.Settings()`.
217
216
218
-
```Python hl_lines="8 9 10 11 12"
217
+
```Python hl_lines="5 11 12"
219
218
{!../../../docs_src/settings/app02/main.py!}
220
219
```
221
220
221
+
!!! tip
222
+
We'll discuss the `@lru_cache()` in a bit.
223
+
224
+
For now you can assume `get_settings()` is a normal function.
225
+
222
226
And then we can require it from the *path operation function* as a dependency and use it anywhere we need it.
223
227
224
228
```Python hl_lines="16 18 19 20"
@@ -275,20 +279,102 @@ Here we create a class `Config` inside of your Pydantic `Settings` class, and se
275
279
!!! tip
276
280
The `Config` class is used just for Pydantic configuration. You can read more at <ahref="https://pydantic-docs.helpmanual.io/usage/model_config/"class="external-link"target="_blank">Pydantic Model Config</a>
277
281
278
-
### Creating the settings object
282
+
### Creating the `Settings` only once with `lru_cache`
283
+
284
+
Reading a file from disk is normally a costly (slow) operation, so you probably want to do it only once and then re-use the same settings object, instead of reading it for each request.
285
+
286
+
But every time we do:
287
+
288
+
```Python
289
+
config.Settings()
290
+
```
291
+
292
+
a new `Settings` object would be created, and at creation it would read the `.env` file again.
293
+
294
+
If the dependency function was just like:
295
+
296
+
```Python
297
+
defget_settings():
298
+
return config.Settings()
299
+
```
279
300
280
-
Reading a file from disk is normally a costly (slow) operation, so you probably want to do it only once and then re-use the same settings, instead of reading it for each request.
301
+
we would create that object for each request, and we would be reading the `.env` file for each request. ⚠️
281
302
282
-
Because of that, in the dependency function, we first check if we already have a `settings` object, and create a new one (that could read from disk) only if it's still `None`, so, it would happen only the first time:
303
+
But as we are using the `@lru_cache()` decorator on top, the `Settings` object will be created only once, the first time it's called. ✔️
283
304
284
-
```Python hl_lines="9 10 11 12"
305
+
```Python hl_lines="1 10"
285
306
{!../../../docs_src/settings/app03/main.py!}
286
307
```
287
308
309
+
Then for any subsequent calls of `get_settings()` in the dependencies for the next requests, instead of executing the internal code of `get_settings()` and creating a new `Settings` object, it will return the same object that was returned on the first call, again and again.
310
+
311
+
#### `lru_cache` Technical Details
312
+
313
+
`@lru_cache()` modifies the function it decorates to return the same value that was returned the first time, instead of computing it again, executing the code of the function every time.
314
+
315
+
So, the function below it will be executed once for each combination of arguments. And then the values returned by each of those combinations of arguments will be used again and again whenever the function is called with exactly the same combination of arguments.
In the case of our dependency `get_settings()`, the function doesn't even take any arguments, so it always returns the same value.
369
+
370
+
That way, it behaves almost as if it was just a global variable. But as it uses a dependency function, then we can override it easily for testing.
371
+
372
+
`@lru_cache()` is part of `functools` which is part of Python's standard library, you can read more about it in the <ahref="https://docs.python.org/3/library/functools.html#functools.lru_cache"class="external-link"target="_blank">Python docs for `@lru_cache()`</a>.
373
+
288
374
## Recap
289
375
290
376
You can use Pydantic Settings to handle the settings or configurations for your application, with all the power of Pydantic models.
291
377
292
378
* By using a dependency you can simplify testing.
293
379
* You can use `.env` files with it.
294
-
*Saving the settings in a variable lets you avoid reading the dotenv file again and again for each request.
380
+
*Using `@lru_cache()`lets you avoid reading the dotenv file again and again for each request, while allowing you to override it during testing.
0 commit comments