Package: dataclass-settings
dataclass-settingsFull documentation here.Bundled Loaders.dataclass-settingsintends to work with anyPEP-681-compliant dataclass-like object,
including but not limited to:Pydantic models(v2+),dataclassesattrs classes.dataclass-settingsowes its existencepydantic-settings, in that
pydantic-settings will be a benchmark fordataclass-settings's featureset.
However it was bourne out of frustration with pydantic-setting's approach to
implementing that featureset.Examplefrom__future__importannotationsfromdataclass_settingsimportload_settings,Env,SecretfrompydanticimportBaseModelclassExample(BaseModel):env:Annotated[str,Env("ENVIRONMENT")]="local"dsn:Annotated[str,Env("DSN"),Secret('dsn')]="dsn://"sub_config:SubConfigclassSubConfig(BaseModel):nested:Annotated[int,Env("NESTED")]="4"example:Example=load_settings(Example)# or, if you want `nested` to be `SUB_CONFIG_NESTED`example:Example=load_settings(Example,nested_delimiter='_')vs Pydantic SettingsSimplicitypydantic-settingsalters how you go about defining your normal pydantic
models. You need to switch (some of the) base classes, you need to configure
the magicalmodel_config = SettingsConfigDict(...)object, etc.The model becomes inherently entangled with the settings-loading library.dataclass-settingsattaches targeted Annotations metadata to a vanilla
pydantic model. You canchooseto not useload_settings(for example, in
tests), and construct the model instance however you'd like.Claritypydantic-settingsmakes it really, really difficult to intuit what the
concrete environment varibale that's going to be loaded for a given field isactuallygoing to be. Based on my own experience, and from perusing their
issue tracker, it seems like this is not an uncommon experience.The combination of field name,SettingsConfigDictsettings, casing,alias/validation_alias/serialization_alias, and relative position of the
env var in the greater config all contribute to it being ataskto deduce
which concrete name will be used when loading.dataclass-settingsbydefaultrequires an explicit, concrete name, which
maps directly to the value being loaded (Env('FOO')loadsFOO, for sure!)If you want to opt into a less explcict, more inferred setup (like
pydantic-settings), you can do so by utilizing thenested_delimiter='_'andinfer_name=Truearguments.Typingpydantic-settingsdoes not playsuperwell with type checkers,
necessitating the use of a mypy plugin for it to not emit type errors into
user code.The code recommended in their documentation for namespacing settings, looks
like:classSettings(BaseSettings):more_settings:SubModel=SubModel()This only type-checks with mypy (after using the plugin), but not
pyright/pylance. Additionally, thisactuallyevaluates theSubModelconstructor during module parsing!These issues seem(?) to be inherent to the strategy of subclassingBaseModel, and building in its logic into the object construction processdataclass-settingssidesteps this problem by decoupling the definition of
the settings from the loading of settings.As such, you're more able to define the model, exactly as you would have with
vanilla pydantic:classSettings(BaseModel):more_settings:SubModelInternally, theload_settingsfunction handles the work of constructing the
requisite input structure pydantic expects to construct the whole object tree.Compatibilitypydantic-settings'sBaseSettingsinherits from pydantic'sBaseModel. And
thus can only function against pydantic models, as the name would imply.dataclass-settings's primary entrypoint is a function that accepts a
supportable type. As such, it can theoretically support any type that has a
well defined object structure, like all ofpydantic,dataclasses, andattrs.Practically,pydantichas the most robust system for parsing/validating a
json-like structure into the models, so it's probably to be the most flexible
anyways. But for many simple cases, particuarly those without nesting, or that
only deal in simple types (like int, float, str, etc); then dataclasses/attrs
can certainly provide a similar experience.FlexibilityAt time of writing,pydantic-settings's strategy around "loaders", i.e.
supportable settings sources is relatively inflexible. Their issue tracker
contains a decent number of requests for a more flexible way of defining
settings priorities among different loaders, or even using different settings
from within a loader.This, at least, doesn't seem to be an inherent issue to the library
necessarily. Just that at present, their API appears to try to reuse
pydantic'sFieldandaliasmechanisms to infer the settings for all
loaders.dataclass-settingsinstead annotates each field individually, with the
loaders that field should use. That means you can have different priorities
(or entirely different loaders!) per field.