python-dependency-injector icon indicating copy to clipboard operation
python-dependency-injector copied to clipboard

Conf as pydantic

Open jneo8 opened this issue 2 years ago • 6 comments

Hi,

I just wonder if there any possible transform configuration as pydantic.BaseSettings type?

There are some frameworks are dependency on pydantic settings.

This is weird to import pydantic to DI's config then to provide it as pydantic again

Or can I use like

from dependency_injector import containers
from pydantic import BaseSettings

class Settings(BaseSettings):
    ...


class Contrainer(containers.DeclarativeContainer):
    settings = providers.Singleton(Settings)

jneo8 avatar Jul 21 '21 14:07 jneo8

Hi @jneo8 , check this: https://python-dependency-injector.ets-labs.org/providers/configuration.html#loading-from-a-pydantic-settings

rmk135 avatar Jul 21 '21 19:07 rmk135

@rmk135 Yes, The doc say we can load pydantic as config, then inject config to another provider. This is how we load config right now.

pydantic -> config

But if I need to inject pydantic's BaseSettings to another provider. So now my workaround is

pydantic -> config -> resource-provider-which-return-pydantic -> another provider

jneo8 avatar Jul 22 '21 08:07 jneo8

Configuration provider doesn't keep a reference to a provided pydantic object. Only data is stored. You probably could do like you mentioned if you need to inject pydantic object providers.Singleton(Settings).

It would be helpful if you could provide a sample code for demonstrating a problem.

rmk135 avatar Jul 22 '21 22:07 rmk135

I'm trying to use fastapi-jwt-auth

On their basic example, User have to write func to provide config like:

# callback to get your configuration
@AuthJWT.load_config
def get_config():
    return Settings()

And this func need BaseSettings object to work.

So here is my workaround right now:

from dependency_injector import containers, providers
from fastapi_jwt_auth import AuthJWT
from pydantic import BaseSettings


class Container(containers.DeclarativeContainer):
    """Dependency injector container."""

    def _get_jwt_settings(authjwt_secret_key: str):
        """Workaround to call AuthJWT.load_config without decorator.

        For more details please see how fastapi_jwt_auth implement load_config
        with classmethod.
        """

        class JWTSettings(BaseSettings):
            authjwt_secret_key: str

        def get_settings():
            return JWTSettings(authjwt_secret_key=authjwt_secret_key)

        return get_settings

    config = providers.Configuration()

    jwt_settings = providers.Resource(
        _get_jwt_settings,
        authjwt_secret_key=config.AUTHJWT_SECRET_KEY,
    )

    jwt_config = providers.Resource(
        AuthJWT.load_config,
        settings=jwt_settings,
    )

It work but I just consider is there any more clean way to make it work.

jneo8 avatar Jul 23 '21 07:07 jneo8

I'd like to second this concern- my current use case would benefit from a passed pydantic object rather than the dict:

from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject

from pydantic import BaseSettings
import yaml

from slac_services.modeling import ModelDBConfig, ModelDB, ModelingService, RemoteModelingService, LocalModelingService


class Settings(BaseSettings):
    model_db: ModelDBConfig


class ServiceContainer(containers.DeclarativeContainer):

    config = providers.Configuration()

    model_db = providers.Singleton(
        ModelDB,
        config.model_db # would like to pass as Pydantic settings
    )

    remote_modeling_service = providers.Singleton(
        RemoteModelingService,
        model_db=model_db,
    )

    local_modeling_service = providers.Singleton(
        LocalModelingService
    )

jacquelinegarrahan avatar Feb 24 '22 00:02 jacquelinegarrahan

I have the same issue but managed to work around it by

class Application(containers.DeclarativeContainer):
    settings = providers.ThreadSafeSingleton(Settings)

This works, although I would prefer to get the pydantic object from the start when using the pydantic_settings option 😅

radyz avatar Apr 13 '22 03:04 radyz