Skip to content

BaseSettings validates default field value #166

@stinovlas

Description

@stinovlas

Summary

BaseSettings, unlike BaseModel validates field default value. This contradicts the documentation. pydantic docs state:

Validators won't run when the default value is used. This applies both to @field_validator validators and Annotated validators. You can force them to run with Field(validate_default=True).

Documentation of pydantic-settings doesn't mention exception from this behavior.

I don't know whether this behavior is intended or not (I'd guess not, because workarounds are quite ugly, see below). But even if it is, it should probably be documented in pydantic-settings docs.

Minimal working example

from zoneinfo import ZoneInfo
from pydantic import BaseModel, ConfigDict
from pydantic.functional_validators import BeforeValidator
from pydantic_settings import BaseSettings, SettingsConfigDict
from typing_extensions import Annotated

class Model(BaseModel):
    model_config = ConfigDict(arbitrary_types_allowed=True)
    zone: Annotated[ZoneInfo, BeforeValidator(ZoneInfo)] = ZoneInfo("UTC")

class Settings(BaseSettings):
    # arbitrary_types_allowed are not needed for BaseSettings and don't change anything
    zone: Annotated[ZoneInfo, BeforeValidator(ZoneInfo)] = ZoneInfo("UTC")

print(repr(Model().zone))  # prints "zoneinfo.ZoneInfo(key='UTC')"
print(repr(Settings().zone))  # raises TypeError: expected str, bytes or os.PathLike object, not ZoneInfo

python -V

Python 3.11.2

pip --list

Package           Version
----------------- -------
annotated-types   0.5.0
pip               23.0.1
pydantic          2.3.0
pydantic_core     2.6.3
pydantic-settings 2.0.3
python-dotenv     1.0.0
setuptools        66.1.1
typing_extensions 4.7.1

Workarounds

  1. Use plain default value. This works as expected, but doesn't play well with type checkers:
class Settings(BaseSettings):
    zone: Annotated[ZoneInfo, BeforeValidator(ZoneInfo)] = "UTC"
  1. Set validate_default=False explicitely. This works, but is unnecessarily verbose:
class Settings(BaseSettings):
    zone: Annotated[ZoneInfo, Field(validate_default=False), BeforeValidator(ZoneInfo)] = ZoneInfo("UTC")

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions