pydantic-settings icon indicating copy to clipboard operation
pydantic-settings copied to clipboard

Add `RootSettings`

Open rijenkii opened this issue 1 year ago • 9 comments

After making a discussion, question, and not getting any answers, I assume the functionality I'm looking for is not implemented, so I am making a feature request.


I want to use a following model for my settings:

import pydantic

class Clustered(pydantic.BaseModel):
    is_clustered: typing.Literal[True]
    server_name: str


class NonClustered(pydantic.BaseModel):
    is_clustered: typing.Literal[False] = False

Settings = pydantic.RootModel[Clustered | NonClustered]

~~In pydantic v1 it was possible to do such a thing (I think)~~

EDIT: Nope it wasn't
root@b8ce1c649a1f:/# cat main.py 
import typing
import pydantic

class Clustered(pydantic.BaseModel):
    is_clustered: typing.Literal[True]
    server_name: str


class NonClustered(pydantic.BaseModel):
    is_clustered: typing.Literal[False] = False

class Settings(pydantic.BaseSettings):
    __root__: Clustered | NonClustered

print(Settings().dict())
root@b8ce1c649a1f:/# python main.py 
Traceback (most recent call last):
  File "//main.py", line 15, in <module>
    print(Settings().dict())
          ^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/env_settings.py", line 40, in __init__
    super().__init__(
  File "/usr/local/lib/python3.12/site-packages/pydantic/main.py", line 341, in __init__
    raise validation_error
pydantic.error_wrappers.ValidationError: 1 validation error for Settings
__root__
  field required (type=value_error.missing

But in v2 __root__ was removed, and multiple inheritance does not work:

>>> class Settings(pydantic_settings.BaseSettings, pydantic.RootModel[NonClustered | Clustered]): pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_model_construction.py", line 117, in __new__
    cls: type[BaseModel] = super().__new__(mcs, cls_name, bases, namespace, **kwargs)  # type: ignore
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen abc>", line 106, in __new__
  File "/usr/local/lib/python3.12/site-packages/pydantic/root_model.py", line 50, in __init_subclass__
    raise PydanticUserError(
pydantic.errors.PydanticUserError: `RootModel` does not support setting `model_config['extra']`

What I assume needs to happen is RootSettings has to be added, so the following code can be written:

Settings = pydantic_settings.RootSettings[Clustered | NonClustered]

rijenkii avatar Dec 06 '23 12:12 rijenkii

Thanks @rijenkii for reporting this.

Yeah, it is not possible in pydantic-settings but I think it is not possible to fetch data from env variables with your provided example in v1:

class Settings(pydantic.BaseSettings):
    __root__: Clustered | NonClustered

Have you tried it in v1?

hramezani avatar Dec 06 '23 14:12 hramezani

You are right, it is not possible. I thought I've tried that, but it seems I've messed up somewhere. I've edited the OP (and the stackoverflow question) and redacted the part about v1.

rijenkii avatar Dec 06 '23 14:12 rijenkii

@hramezani Actually, what was possible in pydantic v1 (given the example above):

Settings = typing.Union[Clustered, NonClustered]
pydantic.parse_obj_as(Settings)

Now fails when is_clustered=False in env var (I assume that it disregards the union). Any way around this, if we want to upgrade to pydantic v2?

pavelrib avatar Feb 25 '24 08:02 pavelrib

@pavelrib Have you tried TypeAdapter

hramezani avatar Feb 27 '24 11:02 hramezani

@hramezani Thanks for the heads up, but can you please give an example of the usage in that case? I've figured you meant probably:

Settings: TypeAdapter[typing.Union[Clustered, NonClustered]] = TypeAdapter(typing.Union[Clustered, NonClustered])

But what's the next step to evaluate Settings? All examples show validation with an actual object, but here the values should be implicitly read from env.

pavelrib avatar Feb 28 '24 16:02 pavelrib

@hramezani Is there anything that I'm missing here?

pavelrib avatar Mar 13 '24 12:03 pavelrib

No, I think TypeAdapter is not good for this usecase

hramezani avatar Mar 14 '24 14:03 hramezani

@hramezani Is there any plan to add it in the near future? I've tried playing with different definitions and parsing methods to no avail.

pavelrib avatar Mar 28 '24 10:03 pavelrib

@pavelrib I don't think that I can add it shortly. I hope some contributors work on this but this can be a complex feature

hramezani avatar Mar 28 '24 11:03 hramezani