Python Dependency Injector: Configuration初期化の罠
Configurationプロバイダについて
過去の記事[1] [2] でも紹介しているPythonのDIライブラリ、 Dependency Injector です。
Configuration
プロバイダにより、JSON・YAML・INI・環境変数等の設定値を読み込むことができます。
以下ではJSONを例にとって進めます。読み込み方には2通りあります。
{
"message": "Hello world!"
}
from dependency_injector import containers, providers
class Container(containers.DeclarativeContainer):
config1 = providers.Configuration(json_files=["conf.json"])
config2 = providers.Configuration()
config2.from_json("conf.json")
if __name__ == '__main__':
container = Container()
print(container.config1.message()) # Hello world!
print(container.config2.message()) # Hello world!
疑問
ところが、プロバイダをContainer
に入れずに書いてみると、片方だけ動かなくなります。
from dependency_injector import containers, providers
config1 = providers.Configuration(json_files=["conf.json"])
config2 = providers.Configuration()
config2.from_json("conf.json")
print(config1.message()) # None
print(config2.message()) # Hello world!
克服
load
を呼びましょう。
Load configuration.
This method loads configuration from configuration files or pydantic settings that were set earlier with set_*() methods or provided to the init(), e.g.:
json_files=
yaml_files=
ini_files=
pydantic_settings=
のいずれも同様で、これらを使う場合はload()
をして初めて読み込まれます。
from_json
from_yaml
from_ini
from_pydantic
についてはload不要で即時に読み込まれる仕様のようです。
from dependency_injector import containers, providers
config1 = providers.Configuration(json_files=["conf.json"])
config1.load() # !!!
config2 = providers.Configuration()
config2.from_json("conf.json")
print(config1.message()) # Hello world!
print(config2.message()) # Hello world!
Container
にプロバイダを入れた場合、コンテナの初期化時に自動的にload
してくれるそうで、それで一見奇怪なこの挙動になります。
以上は私が本家GitHubのissueに問い合わせて理解したものです。
余談: やりたかったこと
設定値によって注入するオブジェクトを変更するようなことをしたい場合、コンテナ内でConfiguration
から値を読みたいことがあります。その際load
につまづくかもしれません。
from dependency_injector import containers, providers
class Container(containers.DeclarativeContainer):
config = providers.Configuration(json_files=["conf.json"])
config.load()
if config.target() == "Production":
s3_factory = providers.Factory(S3Client)
elif config.target() == "Development":
s3_factory = providers.Factory(MockS3Client)
-
FastAPIでDIをする (Dependency Injectorを使う) https://zenn.dev/shimat/articles/4be773f427c502 ↩︎
-
FastAPI + Dependency Injector において Configuration をモックする https://zenn.dev/shimat/articles/d566561e37ceda ↩︎
Discussion