-
-
Notifications
You must be signed in to change notification settings - Fork 92
ENV override not properly explained #159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Thanks @Verhaeg for reporting this 🙏 I've tested the example in the doc and created a So, I couldn't reproduce the problem. Please let me know what is the result. |
Ok.. Here is my previous code (simplified) and example of executions: # pylint: disable=too-few-public-methods,invalid-name
from enum import Enum
from typing import Annotated, Literal, Optional
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class BaseSettingsIgnore(BaseSettings):
"""Basic model with already ignoring extra parameters"""
model_config = SettingsConfigDict(extra='ignore', populate_by_name=True)
class CacheType(str, Enum):
"""Possible values for Cache class"""
redis = 'redis'
memory = 'memory'
class EventType(str, Enum):
"""Possible values for Events class"""
memory = 'memory'
pubsub = 'pubsub'
redis = 'redis'
class RedisConfig(BaseSettingsIgnore):
"""Configuration for Redis"""
host: str = Field(default='localhost')
# Setting alias to prevent override from env PORT
port: int = Field(default=6379, gt=0)
db: int = Field(default=0, ge=0, le=15)
prefix: Optional[str] = Field(default=None)
ttl: Optional[int] = Field(default=None, gt=0)
class MemoryCacheConfig(BaseSettingsIgnore):
"""Memory type cache config - Dummy usage"""
type: Literal[CacheType.memory]
ttl: int = Field(default=60 * 60 * 24, ge=0)
class RedisCacheConfig(RedisConfig):
"""Configuration for Redis"""
type: Literal[CacheType.redis]
CacheAnnotation = Annotated[RedisCacheConfig | MemoryCacheConfig, Field(discriminator='type')]
class FeatureFlagConfig(BaseSettingsIgnore):
"""Configurations for SplitIO"""
key: str = Field(default='localhost')
file: Optional[str] = Field(default=None)
redis: Optional[RedisConfig] = Field(default=None)
class PubSubEventHandler(BaseSettingsIgnore):
"""Configuration for PubSub"""
type: Literal[EventType.pubsub]
project: str
topic: str
class RedisEventHandler(RedisConfig):
"""Configuration for PubSub"""
type: Literal[EventType.redis]
class MemoryEventHandler(BaseSettingsIgnore):
"""Memory dummy event handler"""
type: Literal[EventType.memory]
EventAnnotation = Annotated[Optional[MemoryEventHandler | PubSubEventHandler | RedisEventHandler],
Field(default=None, discriminator='type')]
class Config(BaseSettings):
"""Base settings for project"""
model_config = SettingsConfigDict(
env_nested_delimiter='__',
env_file=('dist.env', '.env'),
env_file_encoding='utf-8',
extra='ignore',
use_enum_values=True,
populate_by_name=True
)
cache: CacheAnnotation
events: EventAnnotation
feature_flag: FeatureFlagConfig
max_per_page: int = Field(default=10, gt=0)
config = Config() # pyright: ignore
if __name__ == '__main__':
print(config.model_dump()) .env file FEATURE_FLAG__KEY=localhost
FEATURE_FLAG__FILE=test.ff.yaml
CACHE__TYPE=redis
CACHE__HOST=localhost
CACHE__TTL=60 Tests: ❯ PORT=8000 python config.py
{'cache': {'host': 'localhost', 'port': 8000, 'db': 0, 'prefix': None, 'ttl': 60, 'type': <CacheType.redis: 'redis'>}, 'events': None, 'feature_flag': {'key': 'localhost', 'file': 'test.ff.yaml', 'redis': None}, 'max_per_page': 10} See that Redis port for cache is 8000 instead of default 6379 Sorry for the long example, perhaps it is due to some especial ENV cases like PORT? |
The problem is because sub models are inheriting from Also, you can see in the example that all sub models are inheriting from So, you can fix the problem by changing |
Thanks for the informaion @hramezani, double-checked that documentation uses BaseModel (did not check against that) but also didn't find mention to this "condition".. I thought that it would only behave like that if initialized in the outer scope, not as sub-settings. I think it would be nice to have this mentioned in the documentation ;) In any case, it is explained ;). I'll change my code to follow this standard. Thanks for the help |
Yes, agree. would you like to open a PR? |
Closed in f7e810d |
Uh oh!
There was an error while loading. Please reload this page.
According to the documentation, sub level environments can be overwritten by more specific definitions:
https://docs.pydantic.dev/latest/usage/pydantic_settings/#parsing-environment-variable-values
In this case,
Settings.SubModel.V2 = 'nested-2'
and that's ok. But what is NOT mentioned in the documentation, is if you have an Environment namedV2
and specially if you reuse that value in multiple submodules. The value is passed to each submodule.This is very frustrating, specially when dealing with the ENV
PORT
as it is very common for multiple services to have such definition (i.e.: Databases, Services, etc)What I had to do so far:
The example above "works" but is not very nice, as I had to add
populate_by_name
and set different and "unusable" alias for theport
field.Otherwise, if I had the
PORT=8000
env for example, all my Redis instances where I thought I was using the default port value (6379) would try to use port 8000.This should AT least be in the documentation! But I think this should be fixed and prevented from being the default case.
Selected Assignee: @samuelcolvin
The text was updated successfully, but these errors were encountered: