1
1
from __future__ import annotations as _annotations
2
2
3
- import warnings
4
- from typing import AbstractSet , Any , ClassVar , Dict , List , Optional , Tuple , Type , Union
3
+ from pathlib import Path
4
+ from typing import Any , Dict , Optional , Tuple , Type , Union
5
5
6
- from pydantic . config import BaseConfig , Extra
7
- from pydantic .fields import ModelField
6
+ from pydantic import ConfigDict
7
+ from pydantic ._internal . _utils import deep_update
8
8
from pydantic .main import BaseModel
9
- from pydantic .typing import StrPath , display_as_type
10
- from pydantic .utils import deep_update , sequence_like
11
9
12
10
from .sources import (
13
11
DotEnvSettingsSource ,
18
16
SecretsSettingsSource ,
19
17
)
20
18
21
- env_file_sentinel = str (object ())
19
+ env_file_sentinel : DotenvType = Path ('' )
20
+
21
+
22
+ class SettingsConfigDict (ConfigDict ):
23
+ case_sensitive : bool
24
+ env_prefix : str
25
+ env_file : Optional [DotenvType ]
26
+ env_file_encoding : Optional [str ]
27
+ env_nested_delimiter : Optional [str ]
28
+ secrets_dir : Optional [Union [str , Path ]]
22
29
23
30
24
31
class BaseSettings (BaseModel ):
@@ -34,12 +41,12 @@ def __init__(
34
41
_env_file : Optional [DotenvType ] = env_file_sentinel ,
35
42
_env_file_encoding : Optional [str ] = None ,
36
43
_env_nested_delimiter : Optional [str ] = None ,
37
- _secrets_dir : Optional [StrPath ] = None ,
44
+ _secrets_dir : Optional [Union [ str , Path ] ] = None ,
38
45
** values : Any ,
39
46
) -> None :
40
47
# Uses something other than `self` the first arg to allow "self" as a settable attribute
41
48
super ().__init__ (
42
- ** __pydantic_self__ ._build_values (
49
+ ** __pydantic_self__ ._settings_build_values (
43
50
values ,
44
51
_env_file = _env_file ,
45
52
_env_file_encoding = _env_file_encoding ,
@@ -48,40 +55,55 @@ def __init__(
48
55
)
49
56
)
50
57
51
- def _build_values (
58
+ @classmethod
59
+ def settings_customise_sources (
60
+ cls ,
61
+ settings_cls : Type [BaseSettings ],
62
+ init_settings : PydanticBaseSettingsSource ,
63
+ env_settings : PydanticBaseSettingsSource ,
64
+ dotenv_settings : PydanticBaseSettingsSource ,
65
+ file_secret_settings : PydanticBaseSettingsSource ,
66
+ ) -> Tuple [PydanticBaseSettingsSource , ...]:
67
+ return init_settings , env_settings , dotenv_settings , file_secret_settings
68
+
69
+ def _settings_build_values (
52
70
self ,
53
71
init_kwargs : Dict [str , Any ],
54
72
_env_file : Optional [DotenvType ] = None ,
55
73
_env_file_encoding : Optional [str ] = None ,
56
74
_env_nested_delimiter : Optional [str ] = None ,
57
- _secrets_dir : Optional [StrPath ] = None ,
75
+ _secrets_dir : Optional [Union [ str , Path ] ] = None ,
58
76
) -> Dict [str , Any ]:
59
77
# Configure built-in sources
60
78
init_settings = InitSettingsSource (self .__class__ , init_kwargs = init_kwargs )
61
79
env_settings = EnvSettingsSource (
62
80
self .__class__ ,
63
81
env_nested_delimiter = (
64
- _env_nested_delimiter if _env_nested_delimiter is not None else self .__config__ .env_nested_delimiter
82
+ _env_nested_delimiter
83
+ if _env_nested_delimiter is not None
84
+ else self .model_config .get ('env_nested_delimiter' )
65
85
),
66
- env_prefix_len = len (self .__config__ . env_prefix ),
86
+ env_prefix_len = len (self .model_config . get ( ' env_prefix' , '' ) ),
67
87
)
68
88
dotenv_settings = DotEnvSettingsSource (
69
89
self .__class__ ,
70
- env_file = (_env_file if _env_file != env_file_sentinel else self .__config__ . env_file ),
90
+ env_file = (_env_file if _env_file != env_file_sentinel else self .model_config . get ( ' env_file' ) ),
71
91
env_file_encoding = (
72
- _env_file_encoding if _env_file_encoding is not None else self .__config__ . env_file_encoding
92
+ _env_file_encoding if _env_file_encoding is not None else self .model_config . get ( ' env_file_encoding' )
73
93
),
74
94
env_nested_delimiter = (
75
- _env_nested_delimiter if _env_nested_delimiter is not None else self .__config__ .env_nested_delimiter
95
+ _env_nested_delimiter
96
+ if _env_nested_delimiter is not None
97
+ else self .model_config .get ('env_nested_delimiter' )
76
98
),
77
- env_prefix_len = len (self .__config__ . env_prefix ),
99
+ env_prefix_len = len (self .model_config . get ( ' env_prefix' , '' ) ),
78
100
)
79
101
80
102
file_secret_settings = SecretsSettingsSource (
81
- self .__class__ , secrets_dir = _secrets_dir or self .__config__ . secrets_dir
103
+ self .__class__ , secrets_dir = _secrets_dir or self .model_config . get ( ' secrets_dir' )
82
104
)
83
105
# Provide a hook to set built-in sources priority and add / remove sources
84
- sources = self .__config__ . customise_sources (
106
+ sources = self .settings_customise_sources (
85
107
self .__class__ ,
86
108
init_settings = init_settings ,
87
109
env_settings = env_settings ,
@@ -95,59 +117,14 @@ def _build_values(
95
117
# to an informative error and much better than a confusing error
96
118
return {}
97
119
98
- class Config (BaseConfig ):
99
- env_prefix : str = ''
100
- env_file : Optional [DotenvType ] = None
101
- env_file_encoding : Optional [str ] = None
102
- env_nested_delimiter : Optional [str ] = None
103
- secrets_dir : Optional [StrPath ] = None
104
- validate_all : bool = True
105
- extra : Extra = Extra .forbid
106
- arbitrary_types_allowed : bool = True
107
- case_sensitive : bool = False
108
-
109
- @classmethod
110
- def prepare_field (cls , field : ModelField ) -> None :
111
- env_names : Union [List [str ], AbstractSet [str ]]
112
- field_info_from_config = cls .get_field_info (field .name )
113
-
114
- env = field_info_from_config .get ('env' ) or field .field_info .extra .get ('env' )
115
- if env is None :
116
- if field .has_alias :
117
- warnings .warn (
118
- 'aliases are no longer used by BaseSettings to define which environment variables to read. '
119
- 'Instead use the "env" field setting. '
120
- 'See https://pydantic-docs.helpmanual.io/usage/settings/#environment-variable-names' ,
121
- FutureWarning ,
122
- )
123
- env_names = {cls .env_prefix + field .name }
124
- elif isinstance (env , str ):
125
- env_names = {env }
126
- elif isinstance (env , (set , frozenset )):
127
- env_names = env
128
- elif sequence_like (env ):
129
- env_names = list (env )
130
- else :
131
- raise TypeError (f'invalid field env: { env !r} ({ display_as_type (env )} ); should be string, list or set' )
132
-
133
- if not cls .case_sensitive :
134
- env_names = env_names .__class__ (n .lower () for n in env_names )
135
- field .field_info .extra ['env_names' ] = env_names
136
-
137
- @classmethod
138
- def customise_sources (
139
- cls ,
140
- settings_cls : Type [BaseSettings ],
141
- init_settings : PydanticBaseSettingsSource ,
142
- env_settings : PydanticBaseSettingsSource ,
143
- dotenv_settings : PydanticBaseSettingsSource ,
144
- file_secret_settings : PydanticBaseSettingsSource ,
145
- ) -> Tuple [PydanticBaseSettingsSource , ...]:
146
- return init_settings , env_settings , dotenv_settings , file_secret_settings
147
-
148
- @classmethod
149
- def parse_env_var (cls , field_name : str , raw_val : str ) -> Any :
150
- return cls .json_loads (raw_val )
151
-
152
- # populated by the metaclass using the Config class defined above, annotated here to help IDEs only
153
- __config__ : ClassVar [Type [Config ]]
120
+ model_config = SettingsConfigDict (
121
+ extra = 'forbid' ,
122
+ arbitrary_types_allowed = True ,
123
+ validate_default = True ,
124
+ case_sensitive = False ,
125
+ env_prefix = '' ,
126
+ env_file = None ,
127
+ env_file_encoding = None ,
128
+ env_nested_delimiter = None ,
129
+ secrets_dir = None ,
130
+ )
0 commit comments