pydantic / pydantic-settings

Settings management using pydantic
https://docs.pydantic.dev/latest/usage/pydantic_settings/
MIT License
593 stars 61 forks source link

How can I use Settings().model_dump() to access property names directly instead of using a dictionary? #267

Closed skhaz closed 5 months ago

skhaz commented 5 months ago

I have the following code

from pydantic import AliasChoices
from pydantic import Field
from pydantic import RedisDsn
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    redis_dsn: RedisDsn = Field("redis://localhost:6379")

    def model_dump(self):
        return self

print(Settings().model_dump().redis_dsn)

And I would like to know how to access it using .redis_dsn instead of ['redis_dsn']. Is this possible?

Thank you.

hramezani commented 5 months ago

You can use

settings = Settings()
print(settings.redis_dsn)
skhaz commented 5 months ago

It worked, but Pyright complains that there are missing parameters in the constructor. Is there a workaround besides ignoring the error with a comment?

Screenshot 2024-04-11 at 11 10 51
hramezani commented 5 months ago

hmmm, I am not sure. Do you have same problem with mypy?

skhaz commented 5 months ago

Yes.

$ mypy settings.py
settings.py:17: error: Missing named argument "redis_dsn" for "Settings"  [call-arg]
Found 1 error in 1 file (checked 1 source file)
hramezani commented 5 months ago

Have you configured the mypy plugin?

skhaz commented 5 months ago

I have added this to my pyproject.toml file.

[mypy]
plugins = pydantic.mypy

Do I need to install any additional packages? The page doesn't mention that.

hramezani commented 5 months ago

Here is the config that works for me. please try it:

[mypy]
plugins = pydantic.mypy

[pydantic-mypy]
init_forbid_extra = True
init_typed = True
warn_required_dynamic_aliases = True

[mypy-pydantic_core.*]
follow_imports = skip
hramezani commented 5 months ago

@skhaz have you checked the config? I am going to close the issue

skhaz commented 5 months ago

I tried the config, same problem.

hramezani commented 5 months ago

what version of mypy are you using?

skhaz commented 5 months ago
$ mypy --version
mypy 1.9.0 (compiled: yes)

Installed using uv

$ uv pip install mypy
hramezani commented 5 months ago

I don't have this problem with mypy 1.9.0 but let's keep the issue open for a while to see if someone else complain

skhaz commented 5 months ago

If you want to reproduce:

from pydantic import Field
from pydantic import RedisDsn
from pydantic_settings import BaseSettings
from pydantic_settings import SettingsConfigDict
from typing import Optional

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

    redis_dsn: RedisDsn = Field("redis://localhost:6379")

settings = Settings()

print(settings.redis_dsn)

My PyRight (on Zed) also complains

Screenshot 2024-04-15 at 13 54 50
hramezani commented 5 months ago

I tried the your example code with mypy 1.9.0. Unfortunately I couldn't reproduce it:

$ mypy --version 
mypy 1.9.0 (compiled: yes)
$ mypy /tmp/test8.py --config-file /tmp/mypy.ini
Success: no issues found in 1 source file

You probably need to clear you mypy cache: rm -r .mypy_cache

skhaz commented 5 months ago

This is not only a mypy issue, PyRight have the same complains.

zhangbc97 commented 5 months ago

add default kwargs for Field might solve this problem

Field(default="redis://localhost:6379")

https://docs.pydantic.dev/latest/integrations/visual_studio_code/#adding-a-default-with-field

skhaz commented 5 months ago

And how would it be for the settings that I don't want a default? I want to make it mandatory.

zhangbc97 commented 5 months ago

And how would it be for the settings that I don't want a default? I want to make it mandatory.

Field(...)

skhaz commented 5 months ago

Thank you @zhangbc97.

For who needs, here is a working example below:

from pydantic import Field
from pydantic import RedisDsn
from pydantic_settings import BaseSettings
from pydantic_settings import SettingsConfigDict
from typing import Optional

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

    redis_dsn: RedisDsn = Field(default="redis://localhost:6379")

    mail_password: str = Field(default=..., min_length=3)

settings = Settings()

print(settings.redis_dsn)
print(settings.mail_password)