From 2e79d0f5fc789a496921f44768ac854b8a7da757 Mon Sep 17 00:00:00 2001 From: chbndrhnns Date: Mon, 10 Nov 2025 22:01:07 +0100 Subject: [PATCH] fix: Deterministic alias selection when using validate_by_name --- pydantic_settings/main.py | 4 +++- pydantic_settings/sources/base.py | 14 +++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/pydantic_settings/main.py b/pydantic_settings/main.py index 6085b909..29a09997 100644 --- a/pydantic_settings/main.py +++ b/pydantic_settings/main.py @@ -466,7 +466,9 @@ def _settings_restore_init_kwarg_names( for field_name, field_info in settings_cls.model_fields.items(): alias_names, *_ = _get_alias_names(field_name, field_info) matchable_names = set(alias_names) - include_name = settings_cls.model_config.get('populate_by_name', False) + include_name = settings_cls.model_config.get( + 'populate_by_name', False + ) or settings_cls.model_config.get('validate_by_name', False) if include_name: matchable_names.add(field_name) init_kwarg_name = init_kwarg_names & matchable_names diff --git a/pydantic_settings/sources/base.py b/pydantic_settings/sources/base.py index 2a0f872b..69138d0f 100644 --- a/pydantic_settings/sources/base.py +++ b/pydantic_settings/sources/base.py @@ -267,7 +267,9 @@ def __init__( # When populate_by_name is True, allow using the field name as an input key, # but normalize to the preferred alias to keep keys consistent across sources. matchable_names = set(alias_names) - include_name = settings_cls.model_config.get('populate_by_name', False) + include_name = settings_cls.model_config.get('populate_by_name', False) or settings_cls.model_config.get( + 'validate_by_name', False + ) if include_name: matchable_names.add(field_name) init_kwarg_name = init_kwarg_names & matchable_names @@ -369,7 +371,7 @@ def _extract_field_info(self, field: FieldInfo, field_name: str) -> list[tuple[s else: # string validation alias field_info.append((v_alias, self._apply_case_sensitive(v_alias), False)) - if not v_alias or self.config.get('populate_by_name', False): + if not v_alias or self.config.get('populate_by_name', False) or self.config.get('validate_by_name', False): annotation = field.annotation if typing_objects.is_typealiastype(annotation) or typing_objects.is_typealiastype(get_origin(annotation)): annotation = _strip_annotated(annotation.__value__) # type: ignore[union-attr] @@ -489,7 +491,13 @@ def _get_resolved_field_value(self, field: FieldInfo, field_name: str) -> tuple[ A tuple that contains the value, preferred key and a flag to determine whether value is complex. """ field_value, field_key, value_is_complex = self.get_field_value(field, field_name) - if not (value_is_complex or (self.config.get('populate_by_name', False) and (field_key == field_name))): + if not ( + value_is_complex + or ( + (self.config.get('populate_by_name', False) or self.config.get('validate_by_name', False)) + and (field_key == field_name) + ) + ): field_infos = self._extract_field_info(field, field_name) preferred_key, *_ = field_infos[0] return field_value, preferred_key, value_is_complex