aiodi is a modern Python Dependency Injection library that allows you to standardize and centralize the way objects are constructed in your application highly inspired on PHP Symfony's DependencyInjection Component.
Key Features:
- Native-based: Implements PEP 621 storing project metadata in pyproject.toml.
- Dual mode: Setting dependencies using Python and using configuration files.
- Clean: Wherever you want just use it, no more decorators and defaults everywhere.
Use the package manager pip to install aiodi.
pip install aiodi- Visit aiodi docs.
# sample/pyproject.toml
[tool.aiodi.variables]
name = "%env(str:APP_NAME, 'sample')%"version = "%env(int:APP_VERSION, '1')%"log_level = "%env(APP_LEVEL, 'INFO')%"debug = "%env(bool:int:APP_DEBUG, '0')%"text = "Hello World"
[tool.aiodi.services."_defaults"]
project_dir = "../../.."
[tool.aiodi.services."logging.Logger"]
class = "sample.libs.utils.get_simple_logger"arguments = { name = "%var(name)%", level = "%var(log_level)%" }
[tool.aiodi.services."UserLogger"]
type = "sample.libs.users.infrastructure.in_memory_user_logger.InMemoryUserLogger"arguments = { commands = "@logging.Logger" }
[tool.aiodi.services."*"]
_defaults = { autoregistration = { resource = "sample/libs/*", exclude = "sample/libs/users/{domain,infrastructure/in_memory_user_logger.py,infrastructure/*command.py}" } }# sample/apps/settings.pyfromtypingimportOptionalfromaiodiimportContainer, ContainerBuilderdefcontainer(filename: str, cwd: Optional[str] =None) ->Container:
returnContainerBuilder(filenames=[filename], cwd=cwd).load()# sample/apps/cli/main.pyfromsample.apps.settingsimportcontainerfromloggingimportLoggerdefmain() ->None:
di=container(filename='../../pyproject.toml')
di.get(Logger).info('Just simple call get with the type')
di.get('UserLogger').logger().info('Just simple call get with the service name')fromabcimportABC, abstractmethodfromloggingimportLogger, getLogger, NOTSET, StreamHandler, FormatterfromosimportgetenvfromaiodiimportContainerfromtypingimportOptional, Union_CONTAINER: Optional[Container] =Nonedefget_simple_logger(
name: Optional[str] =None,
level: Union[str, int] =NOTSET,
fmt: str='[%(asctime)s] - %(name)s - %(levelname)s - %(message)s',
) ->Logger:
logger=getLogger(name)
logger.setLevel(level)
handler=StreamHandler()
handler.setLevel(level)
formatter=Formatter(fmt)
handler.setFormatter(formatter)
logger.addHandler(handler)
returnloggerclassGreetTo(ABC):
@abstractmethoddef__call__(self, who: str) ->None:
passclassGreetToWithPrint(GreetTo):
def__call__(self, who: str) ->None:
print('Hello '+who)
classGreetToWithLogger(GreetTo):
_logger: Loggerdef__init__(self, logger: Logger) ->None:
self._logger=loggerdef__call__(self, who: str) ->None:
self._logger.info('Hello '+who)
defcontainer() ->Container:
global_CONTAINERif_CONTAINER:
return_CONTAINERdi=Container({'env': {
'name': getenv('APP_NAME', 'aiodi'),
'log_level': getenv('APP_LEVEL', 'INFO'),
}})
di.resolve([
(
Logger,
get_simple_logger,
{
'name': di.resolve_parameter(lambdadi_: di_.get('env.name', typ=str)),
'level': di.resolve_parameter(lambdadi_: di_.get('env.log_level', typ=str)),
},
),
(GreetTo, GreetToWithLogger), # -> (GreetTo, GreetToWithLogger, {})GreetToWithPrint, # -> (GreetToWithPrint, GreetToWithPrint, {})
])
di.set('who', 'World!')
# ..._CONTAINER=direturndidefmain() ->None:
di=container()
di.get(Logger).info('Just simple call get with the type')
forgreet_toindi.get(GreetTo, instance_of=True):
greet_to(di.get('who'))
if__name__=='__main__':
main()- Python >= 3.10
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.