pydantic-settings
pydantic-settings copied to clipboard
The nested BaseSettings raise a validation error when its attribute is uppercase.
Initial Checks
- [X] I have searched GitHub for a duplicate issue and I'm sure this is something new
- [X] I have searched Google & StackOverflow for a solution and couldn't find anything
- [X] I have read and followed the docs and still think this is a bug
- [X] I am confident that the issue is with pydantic (not my code, or another library in the ecosystem like FastAPI or mypy)
Description
The nested BaseSettings raise a validation error when its attribute is uppercase. But it is OK when its attribute is lowercase. You can reproduce the error with the example code below.
I didn't find any docs about how the upper case affects the behavior of pydantic.
Did I miss anything?
Thanks
Example Code
# The following code will raise error...
from pydantic import BaseSettings
class Nested(BaseSettings):
GOOD_BOY: str = "james"
class MyConfig(BaseSettings):
nested: Nested = Nested()
class Config:
# env_prefix = "MY_" # the prefix can't be mixed with the `env_nested_delimiter`
env_nested_delimiter = '__'
os.environ["NESTED__GOOD_BOY"] = "env_boy"
config = MyConfig()
print(config)
# The following code will run correctly.
from pydantic import BaseSettings
class Nested(BaseSettings):
good_boy: str = "james"
class MyConfig(BaseSettings):
nested: Nested = Nested()
class Config:
# env_prefix = "MY_" # the prefix can't be mixed with the `env_nested_delimiter`
env_nested_delimiter = '__'
os.environ["NESTED__GOOD_BOY"] = "env_boy"
config = MyConfig()
print(config)
# The only difference is that its attribute is uppercase.
Python, Pydantic & OS Version
pydantic version: 1.9.2
pydantic compiled: True
install path: /sdc/home/xiaoyang/miniconda3/lib/python3.9/site-packages/pydantic
python version: 3.9.7 (default, Sep 16 2021, 13:09:58) [GCC 7.5.0]
platform: Linux-5.4.0-126-generic-x86_64-with-glibc2.27
optional deps. installed: ['dotenv', 'typing-extensions']
Affected Components
- [ ] Compatibility between releases
- [ ] Data validation/parsing
- [ ] Data serialization -
.dict()
and.json()
- [ ] JSON Schema
- [ ] Dataclasses
- [X] Model Config
- [ ] Field Types - adding or changing a particular data type
- [ ] Function validation decorator
- [ ] Generic Models
- [ ] Other Model behaviour -
construct()
, pickling, private attributes, ORM mode - [ ] Plugins and integration with other tools - mypy, FastAPI, python-devtools, Hypothesis, VS Code, PyCharm, etc.
any specific reason why your Nested
class also inherits from BaseSettings
?
I think this is because all env vars are converted to lower case unless you have case_sensitive = True
.
You can fix this with either:
-
class Nested(BaseModel)
instead ofclass Nested(BaseSettings)
- or, you can set
case_sensitive = True
in config onMyConfig.Config
I agree in V2 that in "case insensitive" should mean case insensitive.
any specific reason why your
Nested
class also inherits fromBaseSettings
?
Hi, @dsal3389 , @samuelcolvin Thanks for your replies.
I didn't know that the nested setting should inherit from BaseModel
instead of BaseSettings
.
Thanks for letting me know about the more standard implementation.
And the following code still does not work
import os
from pydantic import BaseSettings, BaseModel
class Nested(BaseModel):
GOOD_BOY: str = "james"
class MyConfig(BaseSettings):
nested: Nested = Nested()
class Config:
# env_prefix = "MY_" # the prefix can't be mixed with the `env_nested_delimiter`
env_nested_delimiter = '__'
os.environ["NESTED__GOOD_BOY"] = "env_boy_v03"
config = MyConfig()
print(config)
The case_sensitive = True
works!
It got fixed in pydantic-settings
FYI, pydantic-settings now is a separate package and is in alpha state. you can install it by pip install pydantic-settings --pre
and test it.
Here is your example in pydantic-settings:
import os
from pydantic import BaseModel, ConfigDict
from pydantic_settings import BaseSettings
class Nested(BaseModel):
GOOD_BOY: str = "james"
class MyConfig(BaseSettings):
nested: Nested = Nested()
model_config = ConfigDict(env_nested_delimiter='__')
os.environ["NESTED__GOOD_BOY"] = "env_boy_v03"
config = MyConfig()
print(config)