github pydantic/pydantic-settings v2.16.0

3 hours ago

Highlights

Breaking changes and behavior changes

  • Python 3.10 is no longer supported (#1003). pydantic-settings now requires Python 3.11+, since 3.10 reaches end of life this month. The tomli fallback is gone in favor of stdlib tomllib; the toml extra is kept as an empty extra so pip install pydantic-settings[toml] keeps working.
  • python-dotenv>=1.2.2 is now required (#971) to pick up the fix for CVE-2026-28684.
  • Aliases are ignored in every source when validate_by_alias=False (#1033). Env vars, secrets, CLI flags and defaults now use only field names, matching pydantic. An env var named after the alias is now ignored instead of raising extra_forbidden, and the CLI exposes --apple rather than --pomo.
  • validation_alias takes precedence over alias (#1038). When the two differ, values from JSON/YAML/TOML files and the CLI were silently ignored; they now load. CLI help lists the validation_alias flag first, and CliApp.serialize emits it.
  • CLI case-insensitivity only lowercases registered options (#1036). With case_sensitive=False, args after -- and those captured by CliUnknownArgs keep their case (-Xmx4G no longer becomes -xmx4g). Unrecognized options are now reported as typed (--NOPE instead of --nope).
  • JSON and YAML config files are read as UTF-8 by default (#955) instead of the locale encoding, completing the secrets-file fix from 2.15.0. json_file_encoding/yaml_file_encoding still override it.
  • Discriminated union defaults are partially updated again (#997). This reverts the 2.15.0 regression with nested_model_default_partial_update=True: the default is only dropped when the incoming value selects a different union member.

New features

  • Python 3.15 and pydantic 2.14 support (#1026, #1029, #1022). pydantic 2.14 broke CliApp.run and validate_by_alias=False handling; both are fixed.
  • Cached settings instances via Settings.settings_cached() and Settings.settings_clear_cache() (#952). These give you a process-local singleton per settings class without hand-rolling lru_cache wrappers. Calling the class directly still builds a fresh instance.
  • AWS Systems Manager Parameter Store source — AWSSystemsManagerSettingsSource (#941). It recursively reads every parameter under an ssm_path, with / mapping onto nested models. Install it with the new aws-systems-manager extra.
  • env_file_depth (#940) lets a relative env_file be found in parent directories, mirroring pyproject_toml_depth. This is useful when scripts run from a subdirectory of a monorepo. It defaults to 0, which keeps the current behavior.
  • CliVariadicArg for named variadic options (#949, #958). It accepts --param a b c in addition to repeated --param a --param b. Required fields need at least one value.

Bug fixes

CLI

  • CliApp.serialize output now round-trips through CliApp.run for enums, tuples, dates and Decimals (#988), explicit None values (#981), empty containers (#1008), CliVariadicArg values (#976) and populated CliUnknownArgs fields, which previously raised IndexError (#992).
  • Empty JSON arrays (--items []) now give an empty list instead of [''] (#994).
  • Tuples, frozenset, MutableSequence and other collection subtypes are now treated as repeatable/variadic options (#972), and so are named collection type aliases such as TypeAliasType('Addrs', list[IPvAnyAddress]) (#1032).
  • cli_exit_on_error=False is now honored in CliSubCommand parsers, which raise SettingsError instead of exiting (#977).
  • CliSubCommand aliases keep their case with case_sensitive=False (#989).
  • Each CliUnknownArgs field now receives only its own subcommand's leftover args (#1004).
  • CliApp.format_help no longer crashes when cli_prefix is set in model_config (#945).

Type aliases and parsing

  • Named union type aliases (TypeAliasType('V', list[int] | str)) now decode like their inline equivalents in env and dotenv sources, including under Optional, Annotated and custom validation aliases (#1006, #1030, #1041).
  • Resolving a named Annotated type alias no longer mutates shared FieldInfo metadata, which made validators run twice on subclasses or after model_rebuild() (#979).
  • A bare single-segment AliasPath('SOME_KEY') no longer tries to JSON-decode plain string values (#980).
  • collections.abc.Set/MutableSet/typing.AbstractSet fields now JSON-decode env values (#973).
  • A bare typing.Dict field with env_nested_delimiter no longer raises IndexError, which was a regression in 2.13.0 (#1009).
  • Case-insensitive matching now works for nested pydantic dataclasses (#1010).

Dotenv, secrets and config files

  • With extra='allow', a non-lowercase env_prefix is now stripped from dotenv extras (#959, #968).
  • secrets_case_sensitive=True is now honored on Windows (#1001).
  • An empty JSON config file is treated as an empty mapping instead of crashing (#947), matching YAML and TOML.
  • TOML table headers: ~ paths are now expanded when checking that the file exists (#1034), and an explicit toml_table_header=() now overrides the model config to read the file root (#1027).

Cloud sources

  • Azure Key Vault and GCP Secret Manager now honor env_parse_none_str (#1019, #1040).
  • Azure Key Vault: dash_to_underscore=True now works for nested model fields (#1025).
  • NestedSecretsSettingsSource now validates its specific constructor arguments (#975).
  • DotenvFiltering is exported from pydantic_settings.sources.types (#944).

Documentation

  • Document that runtime validation arguments (strict, extra, context) passed to model_validate() are not applied to BaseSettings, and give a workaround (#1017).
  • Explain why extra='forbid' doesn't reject misspelled top-level env vars (#986).
  • Clarify alias plus nested model env lookup with populate_by_name (#933), and add a nested model example for env_prefix_target (#953).
  • Fix assorted typos (#942, #964).
All changes (including dependency bumps and internal maintenance)

What's Changed

  • Bump boto3 from 1.43.60 to 1.43.66 in the python-packages group by @dependabot[bot] in #936
  • docs: clarify alias + nested model env lookup with populate_by_name by @pranjalm37 in #933
  • lint: enable flake8-bugbear and fix the issues it found by @hramezani in #931
  • Bump the python-packages group with 6 updates by @dependabot[bot] in #943
  • docs: improve ENV_FILE_SENTINEL comment phrasing in sources/types.py by @aoright in #942
  • fix(types): include DotenvFiltering in all in sources.types by @aoright in #944
  • Add env_file_depth for parent directory env file lookup by @dmi03 in #940
  • Add an AWS Systems Manager Parameter Store settings source by @ChrisJr404 in #941
  • fix: use self.cli_prefix instead of cli_prefix in CliSettingsSource constructor by @RomainL972 in #945
  • Bump the python-packages group with 6 updates by @dependabot[bot] in #948
  • fix: treat empty JSON config file as empty mapping instead of crashing by @sneha4175 in #947
  • Expand ruff rule selection by @hramezani in #950
  • Enable ruff's flake8-return (RET) rules by @hramezani in #951
  • docs: add nested model alias example for env_prefix_target by @MohammedAlkindi in #953
  • Add cached settings instances by @tiangolo in #952
  • Bump the github-actions group with 3 updates by @dependabot[bot] in #954
  • Bump the python-packages group with 4 updates by @dependabot[bot] in #957
  • Add CliVariadicArg for named variadic CLI options by @gyanu2507 in #949
  • Fix CliVariadicArg empty-value handling for named options by @hramezani in #958
  • fix: read JSON and YAML config as UTF-8, not the locale encoding by @MohammedAlkindi in #955
  • Reach 100% test coverage by @hramezani in #963
  • docs: fix three small wording/typo errors by @simpleqt in #964
  • fix: strip a case-normalized env_prefix from dotenv extras by @MohammedAlkindi in #959
  • fix: slice the case-normalized prefix in dotenv match_prefix by @hramezani in #968
  • Bump the python-packages group with 3 updates by @dependabot[bot] in #966
  • fix: address CVE-2026-28684 in python-dotenv dependency by @jmartens in #971
  • build(deps): bump the python-packages group with 3 updates by @dependabot[bot] in #978
  • Verify NestedSecretsSettingsSource specific arguments by @makukha in #975
  • Fix abstract Set annotations not being treated as complex by @mmdverse in #973
  • Fix CLI append-action detection for collection subtypes by @mmdverse in #972
  • Honor cli_exit_on_error=False in CliSubCommand parsers by @glaziermag in #977
  • Preserve CliVariadicArg values in CliApp.serialize argparse/env styles by @glaziermag in #976
  • Avoid mutating field metadata when resolving type aliases by @jakezwang in #979
  • fix: bare AliasPath complexity keyed off name length, not path length by @ishan-1010 in #980
  • docs: clarify extra validation for environment variables by @hulkbig in #986
  • build(deps): bump the python-packages group with 2 updates by @dependabot[bot] in #990
  • Preserve explicit None values in CLI serialization by @mtcd001-tech in #981
  • Make CliApp.serialize output parseable for enum and non-JSON values by @SulimanAbdulrazzaq in #988
  • Preserve CliSubCommand alias case with case_sensitive=False by @glaziermag in #989
  • Fix Path.is_mount type-checking error on win32 platform by @truecallerabreham in #982
  • build(deps): bump astral-sh/setup-uv from 10.0.1 to 10.2.0 in the github-actions group by @dependabot[bot] in #998
  • build(deps): bump the python-packages group with 3 updates by @dependabot[bot] in #999
  • Fix CliApp.serialize for models with CliUnknownArgs fields by @truecallerabreham in #992
  • Honor secrets_case_sensitive on Windows by overriding _load_env_vars by @hramezani in #1001
  • Fix empty JSON arrays in CLI list arguments by @hansu650 in #994
  • Bump samuelcolvin/check-python-version action to v5.1 by @Viicos in #1014
  • build(deps): bump the python-packages group with 7 updates by @dependabot[bot] in #1015
  • Only skip partial updates for discriminated unions selecting another type by @SammyTourani in #997
  • fix: handle bare typing.Dict in EnvSettingsSource.next_field (#1007) by @DawnofGenX in #1009
  • Apply case-insensitive field matching to nested pydantic dataclasses by @sungbin1015 in #1010
  • Document that runtime validation arguments are not applied to BaseSettings by @hramezani in #1017
  • Fix named union alias parsing in environment sources by @hulkbig in #1006
  • Drop support for Python 3.10 by @hramezani in #1003
  • Add support for Python 3.15 by @hramezani in #1026
  • Give each CliUnknownArgs field only its own parser leftovers by @fhgffy in #1004
  • Preserve empty containers in CLI serialization by @fallintoplace in #1008
  • Honor env_parse_none_str for Azure Key Vault secrets by @fhgffy in #1019
  • Fix nested Azure Key Vault dash translation by @goudan789 in #1025
  • Support pydantic 2.14 by @hramezani in #1029
  • Read field names when validate_by_alias=False on pydantic 2.14 by @maharanay22 in #1022
  • Ignore aliases in every source when validate_by_alias=False by @hramezani in #1033
  • Honor explicit empty TOML table header overrides by @xiehuanyi in #1027
  • Fix CLI parsing for named collection type aliases by @JackyHsu-X in #1032
  • Fix JSON decoding for optional named union aliases by @hulkbig in #1030
  • Decode optional Annotated named union aliases by @hramezani in #1041
  • Expand home paths when checking TOML table headers by @JunaidAhamed-7777 in #1034
  • Fix config file and CLI values being ignored when alias differs from validation_alias by @hzmsh in #1038
  • Honor env_parse_none_str for GCP Secret Manager by @Ganesh1729-ui in #1040
  • Simplify alias resolution branch and test alias with AliasChoices/AliasPath by @hramezani in #1042
  • Keep case of non-option CLI args when case_sensitive=False by @hzmsh in #1036
  • Prepare release 2.16.0 by @hramezani in #1043

New Contributors

Full Changelog: v2.15.0...v2.16.0

Don't miss a new pydantic-settings release

NewReleases is sending notifications on new releases.