Skip to content
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

Feature request: Add _init_ignore_none to BaseSettings #247

Open
jjeff07 opened this issue Feb 26, 2024 · 4 comments
Open

Feature request: Add _init_ignore_none to BaseSettings #247

jjeff07 opened this issue Feb 26, 2024 · 4 comments
Assignees

Comments

@jjeff07
Copy link

jjeff07 commented Feb 26, 2024

I would like to keep the default order of init_settings and then env_settings but if I pass None in the init then it will error.

Example:

# .env
username=user
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="allow")
    username: str

settings = Settings(username=None)
username
  Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]
    For further information visit https://errors.pydantic.dev/2.6/v/string_type, <traceback object at 0x0000014EA7D5E680>)

Implementation:

from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="allow")
    username: str

settings = Settings(username=None, _env_ignore_none=True)
print(settings.username)
>>> user
@jjeff07 jjeff07 changed the title Feature request: Add _init_ignore_empty to BaseSettings Feature request: Add _init_ignore_none to BaseSettings Feb 26, 2024
@hramezani
Copy link
Member

Thanks @jjeff07 for this feature request.

You are setting explicitly the value for username in init and it is the correct behavior.
As this is a rare case, I would like not to accept this feature because adding more flags makes the code base complex.
You can implement your custom InitSettingsSource and have your desired behavior there.

@jjeff07
Copy link
Author

jjeff07 commented Feb 27, 2024

If I am allowed to set _env_ignore_empty=True to ignore empty environment variables shouldn't there be a way to do it with init args? For example:

import os
import httpx
from pydantic import BaseModel
from pydantic_settings import BaseSettings


os.environ['password'] = 'world'


class Settings(BaseSettings):
    url: str
    username: str
    password: str


class Client(Settings, BaseModel):
    def __init__(self, url=None, username=None, password=None):
        super().__init__(url=url, username=username, password=password)
        self.client = httpx.Client(base_url=self.url, auth=(self.username, self.password))


client = Client(url='google.com', username='Hello')

"""
(<class 'pydantic_core._pydantic_core.ValidationError'>, 1 validation error for Client
password
  Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]
    For further information visit https://errors.pydantic.dev/2.6/v/string_type, <traceback object at 0x00000226F8500D80>)
"""

@hramezani
Copy link
Member

You can have your desired behavior like:

from typing import Any, Tuple, Type

from pydantic_settings import BaseSettings, InitSettingsSource, PydanticBaseSettingsSource, SettingsConfigDict


class MyInitSettingsSource(InitSettingsSource):
    def __init__(self, settings_cls: type[BaseSettings], init_kwargs: dict[str, Any]):
        init_kwargs = {k: v for k, v in init_kwargs.items() if v is not None}
        super().__init__(settings_cls, init_kwargs)


class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="allow")
    username: str

    @classmethod
    def settings_customise_sources(
        cls,
        settings_cls: Type[BaseSettings],
        init_settings: PydanticBaseSettingsSource,
        env_settings: PydanticBaseSettingsSource,
        dotenv_settings: PydanticBaseSettingsSource,
        file_secret_settings: PydanticBaseSettingsSource,
    ) -> Tuple[PydanticBaseSettingsSource, ...]:
        return MyInitSettingsSource(settings_cls, init_settings.init_kwargs), env_settings, dotenv_settings, file_secret_settings


settings = Settings(username=None)

@jjeff07
Copy link
Author

jjeff07 commented Feb 27, 2024

It would be nice if there were no Unresolved References though. Probably why I couldn't figure out a solution as there are no documented examples.

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants