Skip to content

Commit 93d7b7b

Browse files
authored
Fixes #452 - Adding support for populate_by_name (#454)
1 parent e212052 commit 93d7b7b

File tree

2 files changed

+116
-4
lines changed

2 files changed

+116
-4
lines changed

pydantic_settings/sources.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -445,10 +445,12 @@ def _extract_field_info(self, field: FieldInfo, field_name: str) -> list[tuple[s
445445
)
446446
else: # string validation alias
447447
field_info.append((v_alias, self._apply_case_sensitive(v_alias), False))
448-
elif origin_is_union(get_origin(field.annotation)) and _union_is_complex(field.annotation, field.metadata):
449-
field_info.append((field_name, self._apply_case_sensitive(self.env_prefix + field_name), True))
450-
else:
451-
field_info.append((field_name, self._apply_case_sensitive(self.env_prefix + field_name), False))
448+
449+
if not v_alias or self.config.get('populate_by_name', False):
450+
if origin_is_union(get_origin(field.annotation)) and _union_is_complex(field.annotation, field.metadata):
451+
field_info.append((field_name, self._apply_case_sensitive(self.env_prefix + field_name), True))
452+
else:
453+
field_info.append((field_name, self._apply_case_sensitive(self.env_prefix + field_name), False))
452454

453455
return field_info
454456

tests/test_settings.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ class SettingWithIgnoreEmpty(BaseSettings):
6464
model_config = SettingsConfigDict(env_ignore_empty=True)
6565

6666

67+
class SettingWithPopulateByName(BaseSettings):
68+
apple: str = Field('default', alias='pomo')
69+
70+
model_config = SettingsConfigDict(populate_by_name=True)
71+
72+
6773
def test_sub_env(env):
6874
env.set('apple', 'hello')
6975
s = SimpleSettings()
@@ -127,6 +133,110 @@ class Settings(BaseSettings):
127133
assert s.a == 'b'
128134

129135

136+
def test_populate_by_name_when_using_alias(env):
137+
env.set('pomo', 'bongusta')
138+
s = SettingWithPopulateByName()
139+
assert s.apple == 'bongusta'
140+
141+
142+
def test_populate_by_name_when_using_name(env):
143+
env.set('apple', 'honeycrisp')
144+
s = SettingWithPopulateByName()
145+
assert s.apple == 'honeycrisp'
146+
147+
148+
def test_populate_by_name_when_using_both(env):
149+
env.set('apple', 'honeycrisp')
150+
env.set('pomo', 'bongusta')
151+
s = SettingWithPopulateByName()
152+
assert s.apple == 'bongusta', 'Expected alias value to be prioritized.'
153+
154+
155+
def test_populate_by_name_with_alias_path_when_using_alias(env):
156+
env.set('fruits', '["empire", "honeycrisp"]')
157+
158+
class Settings(BaseSettings):
159+
apple: str = Field('default', validation_alias=AliasPath('fruits', 0))
160+
model_config = SettingsConfigDict(populate_by_name=True)
161+
162+
s = Settings()
163+
assert s.apple == 'empire'
164+
165+
166+
def test_populate_by_name_with_alias_path_when_using_name(env):
167+
env.set('apple', 'jonathan gold')
168+
169+
class Settings(BaseSettings):
170+
apple: str = Field('default', validation_alias=AliasPath('fruits', 0))
171+
model_config = SettingsConfigDict(populate_by_name=True)
172+
173+
s = Settings()
174+
assert s.apple == 'jonathan gold'
175+
176+
177+
@pytest.mark.parametrize(
178+
'env_vars, expected_value',
179+
[
180+
pytest.param({'pomo': 'pomo-chosen'}, 'pomo-chosen', id='pomo'),
181+
pytest.param({'pomme': 'pomme-chosen'}, 'pomme-chosen', id='pomme'),
182+
pytest.param({'manzano': 'manzano-chosen'}, 'manzano-chosen', id='manzano'),
183+
pytest.param(
184+
{'pomo': 'pomo-chosen', 'pomme': 'pomme-chosen', 'manzano': 'manzano-chosen'},
185+
'pomo-chosen',
186+
id='pomo-priority',
187+
),
188+
pytest.param({'pomme': 'pomme-chosen', 'manzano': 'manzano-chosen'}, 'pomme-chosen', id='pomme-priority'),
189+
],
190+
)
191+
def test_populate_by_name_with_alias_choices_when_using_alias(env, env_vars: Dict[str, str], expected_value: str):
192+
for k, v in env_vars.items():
193+
env.set(k, v)
194+
195+
class Settings(BaseSettings):
196+
apple: str = Field('default', validation_alias=AliasChoices('pomo', 'pomme', 'manzano'))
197+
model_config = SettingsConfigDict(populate_by_name=True)
198+
199+
s = Settings()
200+
assert s.apple == expected_value
201+
202+
203+
def test_populate_by_name_with_dotenv_when_using_alias(tmp_path):
204+
p = tmp_path / '.env'
205+
p.write_text('pomo=bongusta')
206+
207+
class Settings(BaseSettings):
208+
apple: str = Field('default', alias='pomo')
209+
model_config = SettingsConfigDict(env_file=p, populate_by_name=True)
210+
211+
s = Settings()
212+
assert s.apple == 'bongusta'
213+
214+
215+
def test_populate_by_name_with_dotenv_when_using_name(tmp_path):
216+
p = tmp_path / '.env'
217+
p.write_text('apple=honeycrisp')
218+
219+
class Settings(BaseSettings):
220+
apple: str = Field('default', alias='pomo')
221+
model_config = SettingsConfigDict(env_file=p, populate_by_name=True)
222+
223+
s = Settings()
224+
assert s.apple == 'honeycrisp'
225+
226+
227+
def test_populate_by_name_with_dotenv_when_using_both(tmp_path):
228+
p = tmp_path / '.env'
229+
p.write_text('apple=honeycrisp')
230+
p.write_text('pomo=bongusta')
231+
232+
class Settings(BaseSettings):
233+
apple: str = Field('default', alias='pomo')
234+
model_config = SettingsConfigDict(env_file=p, populate_by_name=True)
235+
236+
s = Settings()
237+
assert s.apple == 'bongusta', 'Expected alias value to be prioritized.'
238+
239+
130240
def test_with_prefix(env):
131241
class Settings(BaseSettings):
132242
apple: str

0 commit comments

Comments
 (0)