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?