How can I pass generic type definition to pydantic model?

26 Views Asked by At

I have the following proof of concept, using Python 3.12 and pydantic 2.6.4:

from abc import ABC, abstractmethod
from typing import Type
from pydantic import BaseModel
from pydantic_settings import BaseSettings


class Settings[T: BaseModel](BaseSettings):
    name: str
    job: T


class BaseJob[T: BaseModel](ABC):
    def __init__(self, name: str, **kwargs) -> None:
        # self.settings = Settings[T](name=name, **kwargs)  # Happy pylance, but raises: AttributeError: 'BaseModel' object has no attribute '__private_attributes__'
        self.settings = Settings[self.settings_model](name=name, **kwargs)  # Works but pylance raises: Cannot access member "workers" for type "BaseModel"

    @abstractmethod
    def run(self) -> None:
        pass

    @property
    @abstractmethod
    def settings_model(self) -> Type[BaseModel]:
        pass


class JobSettings(BaseModel):
    workers: int = 2


class Job(BaseJob[JobSettings]):
    def __init__(self, name: str, **kwargs) -> None:
        super().__init__(name, job={"workers": 4})

    @property
    def settings_model(self) -> Type[BaseModel]:
        return JobSettings

    def run(self) -> None:
        print(self.settings.name)
        print(self.settings.job.workers)


if __name__ == "__main__":
    Job("A generic job").run()

As indicated in the comments, when I use Settings[self.settings_model](name=name, **kwargs) to initialize the settings, the app runs as expected. However, in VsCode pylance is not able to determine that workers is a member of job.

In contrast, if I use Settings[T](name=name, **kwargs) VsCode pylance (and code completion) works fine, but running the app raises an AttributeError.

What would be the correct approach to have a running app with correct code completion? Am I doing something fundamentally wrong?

0

There are 0 best solutions below