diff options
author | CoprDistGit <infra@openeuler.org> | 2023-05-05 12:57:14 +0000 |
---|---|---|
committer | CoprDistGit <infra@openeuler.org> | 2023-05-05 12:57:14 +0000 |
commit | 8200de36ba52e1e3547531c3c040d76bc859057f (patch) | |
tree | a5b745bde731a47a57d6ab15b2653e40c535536e | |
parent | 77978b18ada3006ae5aaf9661b426ee77a3ec22b (diff) |
automatic import of python-kinkopeneuler20.03
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | python-kink.spec | 1069 | ||||
-rw-r--r-- | sources | 1 |
3 files changed, 1071 insertions, 0 deletions
@@ -0,0 +1 @@ +/kink-0.6.6.tar.gz diff --git a/python-kink.spec b/python-kink.spec new file mode 100644 index 0000000..fd79bf8 --- /dev/null +++ b/python-kink.spec @@ -0,0 +1,1069 @@ +%global _empty_manifest_terminate_build 0 +Name: python-kink +Version: 0.6.6 +Release: 1 +Summary: Dependency injection for python. +License: MIT +URL: https://github.com/kodemore/kink +Source0: https://mirrors.nju.edu.cn/pypi/web/packages/6a/5a/a2ddf3da373e59fb655c84fe04d8aa52656689f22b6bacc13fea5e61dc70/kink-0.6.6.tar.gz +BuildArch: noarch + +Requires: python3-typing_extensions + +%description +# Kink   [](https://codecov.io/gh/kodemore/kink) [](https://opensource.org/licenses/MIT) +Dependency injection container made for python + +## Features + +- Easy to use interface +- Extensible with custom dependency resolvers +- Automatic dependency injection (Autowiring) +- Lightweight +- Support for async with asyncio + + +## Installation + +### Pip + +```shell +pip install kink +``` + +### Poetry +If you don't know poetry, I highly recommend visiting their [webpage](https://python-poetry.org) + +```shell +poetry add kink +``` + +# Why using dependency injection in python? + +## Short story + +Because python is a multi paradigm language and this should encourage +you to use best OOP practices improving your workflow and your code and have more time +for your hobbies and families instead monkey-patching entire world. + +## Long story + +Dependency happens when one component (component might be a class, or a function) `A` uses other component +`B`. We say than that `A` depends on `B`. + +Instead hardcoding dependency inside your components and making your code tightly coupled +you are loosing it by providing(injecting) required behaviour either by subclassing or +plugging additional code. This is called `Inversion of Control` which keeps your code +oriented around behaviour rather than control. There are many benefits coming out of it: +- increased modularity +- better extensibility and flexibility +- it helps you understand higher concepts like event driven programming + +This is where dependency injection comes in place. Dependency injection is a specific +style of inversion of control, which generally says instead hardcoding dependency pass +dependant object as a parameter to a method rather than having method creating it itself. +( who would thought it is so easy :)? ). It can go even further than that; when you pass +a dependency don't rely on a particular implementation rely on an abstraction (`Dependency Inversion Principle`). + +So you might ask why do I need it? Here is couple reasons: + +### Relying on the global state is evil + +Coding is hard enough ( business requirements are changing all the time, deadlines are +shortening, clients wants more, there are so many unknowns you have to figure out), +relying on unpredictable state makes it even harder: +- it might introduce potential bugs +- makes code harder to maintain +- concurrency becomes harder to achieve +- balancing mokey-patching well is a hard task + +### Great, but now I have additional work I have to manage now all my dependencies write more code and deadlines are coming even closer! + +True, that is why you should pick up Dependency Injection Container to do all this work +for you. Kink gives you one decorator and simple `dict-like` object to bootstrap and manipulate +your container. +No need for manual work and manual dependency management. Give it a try and you will love it! + +# Usage + +To fully utilise the potential of kink it is recommended to bootstrap your initial dependencies +(config values, or instances of classes that are standalone, requires no other dependencies than themselves). +Some people prefer to keep it in `__init__.py` in the top module of your application, other +create separate `bootstra.py` file for this purpose. Once all is setup the only step left +is to decorate your classes/functions with `@inject` decorator. + +## Bootstrapping/Adding services manually + +### Adding *service* to di container + +Dependency container is a dict-like object, adding new service to dependency container is as +simple as the following example: + +```python +from kink import di +from os import getenv + +di["db_name"] = getenv("DB_NAME") +di["db_password"] = getenv("DB_PASSWORD") +``` + +### Adding *on-demand service* to dependency injection container + +Kink also supports on-demand service creation. In order to define such a service, +lambda function should be used: + +```python +from kink import di +from sqlite3 import connect + +di["db_connection"] = lambda di: connect(di["db_name"]) +``` + +In this scenario connection to database will not be established until service is requested. + +### Adding factorised services to dependency injection + +Factorised services are services that are instantiated every time they are requested. + +```python +from kink import di +from sqlite3 import connect + +di.factories["db_connection"] = lambda di: connect(di["db_name"]) + +connection_1 = di["db_connection"] +connection_2 = di["db_connection"] + +connection_1 != connection_2 +``` + +In the above example we defined factorised service `db_connection`, and below by accessing the service from di we created +two separate connection to database. + + +## Requesting services from dependency injection container + +To access given service just reference it inside `di` like you would do this with +a normal dictionary, full example below: + +```python +from kink import di +from sqlite3 import connect + +# Bootstrapping +di["db_name"] = "test_db.db" +di["db_connection"] = lambda di: connect(di["db_name"]) + + +# Getting a service +connection = di["db_connection"] # will return instance of sqlite3.Connection +assert connection == di["db_connection"] # True +``` + + +## Autowiring dependencies + +Autowiring is the ability of the container to automatically create and inject dependencies. +It detects dependencies of the component tries to search for references in the container +and if all references are present an instance of requested service is returned. + +Autowiring system in kink works in two ways: +- matching argument's names +- matching argument's type annotation + +### How dependencies are prioritised by autowiring mechanism + +Autowiring mechanism priorities dependencies automatically, so when multiple +matches are found for the service this is how it works; +Firstly passed arguments are prioritied - if you pass arguments manually to the service +they will take precendence over anything else. Next argument's names are taken into +consideration and last but not least argument's type annotations. + +### Matching argument's names + +If you don't like type annotations or would like to take advantage of autowiring's +precedence mechanism use this style. + +This is a very simple mechanism we have already seen in previous examples. +Autowiring system checks function argument's names and tries to search for +services with the same names inside the container. + +### Matching argument's type annotations + +If you are like me and like type annotations and use static analysis tools this is +a preferred way working with DI container. + +In this scenario names are ignored instead argument's type annotations are inspected +and looked up inside di container. This requires aliases when bootstrapping +your services in DI container or simply adding them to container in the way that +its type is the key by which service is accessed. Please consider the following example: + +```python +from kink import di, inject +from sqlite3 import connect, Connection + + +di["db_name"] = "test_db.db" +di[Connection] = lambda di: connect(di["db_name"]) # sqlite connection can be accessed by its type + +@inject # Constructor injection will happen here +class UserRepository: + def __init__(self, db: Connection): # `db` argument will be resolved because `Connection` instance is present in the container. + self.db = db + +repo = di[UserRepository] +assert repo.db == di[Connection] # True +``` + +## Constructor injection +```python +from kink import inject, di +import MySQLdb + +# Set dependencies +di["db_host"] = "localhost" +di["db_name"] = "test" +di["db_user"] = "user" +di["db_password"] = "password" +di["db_connection"] = lambda di: MySQLdb.connect(host=di["db_host"], user=di["db_user"], passwd=di["db_password"], db=di["db_name"]) + +@inject +class AbstractRepository: + def __init__(self, db_connection): + self.connection = db_connection + + +class UserRepository(AbstractRepository): + ... + + +repository = di[UserRepository] # will retrieve instance of UserRepository from di container +repository.connection # mysql db connection is resolved and available to use. +``` + +When class is annotated by `inject` annotation it will be automatically added to the container for future use (eg autowiring). + + +## Services aliasing + +When you register a service with `@inject` decorator you can attach your own alias name, please consider the following example: + +```python +from kink import inject +from typing import Protocol + +class IUserRepository(Protocol): + ... + +@inject(alias=IUserRepository) +class UserRepository: + ... + + +assert di[IUserRepository] == di[UserRepository] # returns true +``` + +For more examples check [tests](/tests) directory + +### Retrieving all instances with the same alias +Aliases in `kink` do not have to be unique, but by default when autowiring mechnism is called the service that +was registered first within given alias will be returned. If for some reason you would like to retrieve all +services that alias to the same name (eg implementing strategy pattern), `kink` provides a useful functionality +for doing so. Please consider the following example: + +```python +from kink import inject +from typing import Protocol, List + +class IUserRepository(Protocol): + ... + +@inject(alias=IUserRepository) +class MongoUserRepository: + ... + +@inject(alias=IUserRepository) +class MySQLUserRepository: + ... + +@inject() +class UserRepository: + def __init__(self, repos: List[IUserRepository]) -> None: # all services that alias to IUserRepository will be passed here + self._repos = repos + + def store_to_mysql(self, user: ...): + self._repos[1].store(user) + + def store_to_mongo(self, user: ...): + self._repos[0].store(user) +``` + +## Clearing di cache + +Sometimes it might come handy to clear cached services in di container. Simple way of +doing this is calling `di.clear_cache()` method like in the following example. + +```python +from kink import inject, di + +... # set and accesss your services + +di.clear_cache() # this will clear cache of all services inside di container that are not factorised services +``` + +## Integration with FastAPI + +```python +from fastapi import APIRouter, Depends, status +from fastapi.responses import JSONResponse, Response +from kink import di + +router = APIRouter() + +# register service in the DI container +di[ClientService] = ClientService() + +@router.post( + "/clients", + response_model=ClientDTO, + responses={400: {"model": APIErrorMessage}, 500: {"model": APIErrorMessage}}, + tags=["clients"], +) +async def create_client( + request: CreateClientDTO, service: ClientService = Depends(lambda: di[ClientService]) +) -> JSONResponse: + result = service.create(request) + return JSONResponse(content=result.dict(), status_code=status.HTTP_201_CREATED) +``` + +A complete example, together with tests you can find it [here](https://github.com/szymon6927/hexagonal-architecture-python +). + +# Articles on Kink + +- [https://www.netguru.com/codestories/dependency-injection-with-python-make-it-easy](https://www.netguru.com/codestories/dependency-injection-with-python-make-it-easy) + + + +%package -n python3-kink +Summary: Dependency injection for python. +Provides: python-kink +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-kink +# Kink   [](https://codecov.io/gh/kodemore/kink) [](https://opensource.org/licenses/MIT) +Dependency injection container made for python + +## Features + +- Easy to use interface +- Extensible with custom dependency resolvers +- Automatic dependency injection (Autowiring) +- Lightweight +- Support for async with asyncio + + +## Installation + +### Pip + +```shell +pip install kink +``` + +### Poetry +If you don't know poetry, I highly recommend visiting their [webpage](https://python-poetry.org) + +```shell +poetry add kink +``` + +# Why using dependency injection in python? + +## Short story + +Because python is a multi paradigm language and this should encourage +you to use best OOP practices improving your workflow and your code and have more time +for your hobbies and families instead monkey-patching entire world. + +## Long story + +Dependency happens when one component (component might be a class, or a function) `A` uses other component +`B`. We say than that `A` depends on `B`. + +Instead hardcoding dependency inside your components and making your code tightly coupled +you are loosing it by providing(injecting) required behaviour either by subclassing or +plugging additional code. This is called `Inversion of Control` which keeps your code +oriented around behaviour rather than control. There are many benefits coming out of it: +- increased modularity +- better extensibility and flexibility +- it helps you understand higher concepts like event driven programming + +This is where dependency injection comes in place. Dependency injection is a specific +style of inversion of control, which generally says instead hardcoding dependency pass +dependant object as a parameter to a method rather than having method creating it itself. +( who would thought it is so easy :)? ). It can go even further than that; when you pass +a dependency don't rely on a particular implementation rely on an abstraction (`Dependency Inversion Principle`). + +So you might ask why do I need it? Here is couple reasons: + +### Relying on the global state is evil + +Coding is hard enough ( business requirements are changing all the time, deadlines are +shortening, clients wants more, there are so many unknowns you have to figure out), +relying on unpredictable state makes it even harder: +- it might introduce potential bugs +- makes code harder to maintain +- concurrency becomes harder to achieve +- balancing mokey-patching well is a hard task + +### Great, but now I have additional work I have to manage now all my dependencies write more code and deadlines are coming even closer! + +True, that is why you should pick up Dependency Injection Container to do all this work +for you. Kink gives you one decorator and simple `dict-like` object to bootstrap and manipulate +your container. +No need for manual work and manual dependency management. Give it a try and you will love it! + +# Usage + +To fully utilise the potential of kink it is recommended to bootstrap your initial dependencies +(config values, or instances of classes that are standalone, requires no other dependencies than themselves). +Some people prefer to keep it in `__init__.py` in the top module of your application, other +create separate `bootstra.py` file for this purpose. Once all is setup the only step left +is to decorate your classes/functions with `@inject` decorator. + +## Bootstrapping/Adding services manually + +### Adding *service* to di container + +Dependency container is a dict-like object, adding new service to dependency container is as +simple as the following example: + +```python +from kink import di +from os import getenv + +di["db_name"] = getenv("DB_NAME") +di["db_password"] = getenv("DB_PASSWORD") +``` + +### Adding *on-demand service* to dependency injection container + +Kink also supports on-demand service creation. In order to define such a service, +lambda function should be used: + +```python +from kink import di +from sqlite3 import connect + +di["db_connection"] = lambda di: connect(di["db_name"]) +``` + +In this scenario connection to database will not be established until service is requested. + +### Adding factorised services to dependency injection + +Factorised services are services that are instantiated every time they are requested. + +```python +from kink import di +from sqlite3 import connect + +di.factories["db_connection"] = lambda di: connect(di["db_name"]) + +connection_1 = di["db_connection"] +connection_2 = di["db_connection"] + +connection_1 != connection_2 +``` + +In the above example we defined factorised service `db_connection`, and below by accessing the service from di we created +two separate connection to database. + + +## Requesting services from dependency injection container + +To access given service just reference it inside `di` like you would do this with +a normal dictionary, full example below: + +```python +from kink import di +from sqlite3 import connect + +# Bootstrapping +di["db_name"] = "test_db.db" +di["db_connection"] = lambda di: connect(di["db_name"]) + + +# Getting a service +connection = di["db_connection"] # will return instance of sqlite3.Connection +assert connection == di["db_connection"] # True +``` + + +## Autowiring dependencies + +Autowiring is the ability of the container to automatically create and inject dependencies. +It detects dependencies of the component tries to search for references in the container +and if all references are present an instance of requested service is returned. + +Autowiring system in kink works in two ways: +- matching argument's names +- matching argument's type annotation + +### How dependencies are prioritised by autowiring mechanism + +Autowiring mechanism priorities dependencies automatically, so when multiple +matches are found for the service this is how it works; +Firstly passed arguments are prioritied - if you pass arguments manually to the service +they will take precendence over anything else. Next argument's names are taken into +consideration and last but not least argument's type annotations. + +### Matching argument's names + +If you don't like type annotations or would like to take advantage of autowiring's +precedence mechanism use this style. + +This is a very simple mechanism we have already seen in previous examples. +Autowiring system checks function argument's names and tries to search for +services with the same names inside the container. + +### Matching argument's type annotations + +If you are like me and like type annotations and use static analysis tools this is +a preferred way working with DI container. + +In this scenario names are ignored instead argument's type annotations are inspected +and looked up inside di container. This requires aliases when bootstrapping +your services in DI container or simply adding them to container in the way that +its type is the key by which service is accessed. Please consider the following example: + +```python +from kink import di, inject +from sqlite3 import connect, Connection + + +di["db_name"] = "test_db.db" +di[Connection] = lambda di: connect(di["db_name"]) # sqlite connection can be accessed by its type + +@inject # Constructor injection will happen here +class UserRepository: + def __init__(self, db: Connection): # `db` argument will be resolved because `Connection` instance is present in the container. + self.db = db + +repo = di[UserRepository] +assert repo.db == di[Connection] # True +``` + +## Constructor injection +```python +from kink import inject, di +import MySQLdb + +# Set dependencies +di["db_host"] = "localhost" +di["db_name"] = "test" +di["db_user"] = "user" +di["db_password"] = "password" +di["db_connection"] = lambda di: MySQLdb.connect(host=di["db_host"], user=di["db_user"], passwd=di["db_password"], db=di["db_name"]) + +@inject +class AbstractRepository: + def __init__(self, db_connection): + self.connection = db_connection + + +class UserRepository(AbstractRepository): + ... + + +repository = di[UserRepository] # will retrieve instance of UserRepository from di container +repository.connection # mysql db connection is resolved and available to use. +``` + +When class is annotated by `inject` annotation it will be automatically added to the container for future use (eg autowiring). + + +## Services aliasing + +When you register a service with `@inject` decorator you can attach your own alias name, please consider the following example: + +```python +from kink import inject +from typing import Protocol + +class IUserRepository(Protocol): + ... + +@inject(alias=IUserRepository) +class UserRepository: + ... + + +assert di[IUserRepository] == di[UserRepository] # returns true +``` + +For more examples check [tests](/tests) directory + +### Retrieving all instances with the same alias +Aliases in `kink` do not have to be unique, but by default when autowiring mechnism is called the service that +was registered first within given alias will be returned. If for some reason you would like to retrieve all +services that alias to the same name (eg implementing strategy pattern), `kink` provides a useful functionality +for doing so. Please consider the following example: + +```python +from kink import inject +from typing import Protocol, List + +class IUserRepository(Protocol): + ... + +@inject(alias=IUserRepository) +class MongoUserRepository: + ... + +@inject(alias=IUserRepository) +class MySQLUserRepository: + ... + +@inject() +class UserRepository: + def __init__(self, repos: List[IUserRepository]) -> None: # all services that alias to IUserRepository will be passed here + self._repos = repos + + def store_to_mysql(self, user: ...): + self._repos[1].store(user) + + def store_to_mongo(self, user: ...): + self._repos[0].store(user) +``` + +## Clearing di cache + +Sometimes it might come handy to clear cached services in di container. Simple way of +doing this is calling `di.clear_cache()` method like in the following example. + +```python +from kink import inject, di + +... # set and accesss your services + +di.clear_cache() # this will clear cache of all services inside di container that are not factorised services +``` + +## Integration with FastAPI + +```python +from fastapi import APIRouter, Depends, status +from fastapi.responses import JSONResponse, Response +from kink import di + +router = APIRouter() + +# register service in the DI container +di[ClientService] = ClientService() + +@router.post( + "/clients", + response_model=ClientDTO, + responses={400: {"model": APIErrorMessage}, 500: {"model": APIErrorMessage}}, + tags=["clients"], +) +async def create_client( + request: CreateClientDTO, service: ClientService = Depends(lambda: di[ClientService]) +) -> JSONResponse: + result = service.create(request) + return JSONResponse(content=result.dict(), status_code=status.HTTP_201_CREATED) +``` + +A complete example, together with tests you can find it [here](https://github.com/szymon6927/hexagonal-architecture-python +). + +# Articles on Kink + +- [https://www.netguru.com/codestories/dependency-injection-with-python-make-it-easy](https://www.netguru.com/codestories/dependency-injection-with-python-make-it-easy) + + + +%package help +Summary: Development documents and examples for kink +Provides: python3-kink-doc +%description help +# Kink   [](https://codecov.io/gh/kodemore/kink) [](https://opensource.org/licenses/MIT) +Dependency injection container made for python + +## Features + +- Easy to use interface +- Extensible with custom dependency resolvers +- Automatic dependency injection (Autowiring) +- Lightweight +- Support for async with asyncio + + +## Installation + +### Pip + +```shell +pip install kink +``` + +### Poetry +If you don't know poetry, I highly recommend visiting their [webpage](https://python-poetry.org) + +```shell +poetry add kink +``` + +# Why using dependency injection in python? + +## Short story + +Because python is a multi paradigm language and this should encourage +you to use best OOP practices improving your workflow and your code and have more time +for your hobbies and families instead monkey-patching entire world. + +## Long story + +Dependency happens when one component (component might be a class, or a function) `A` uses other component +`B`. We say than that `A` depends on `B`. + +Instead hardcoding dependency inside your components and making your code tightly coupled +you are loosing it by providing(injecting) required behaviour either by subclassing or +plugging additional code. This is called `Inversion of Control` which keeps your code +oriented around behaviour rather than control. There are many benefits coming out of it: +- increased modularity +- better extensibility and flexibility +- it helps you understand higher concepts like event driven programming + +This is where dependency injection comes in place. Dependency injection is a specific +style of inversion of control, which generally says instead hardcoding dependency pass +dependant object as a parameter to a method rather than having method creating it itself. +( who would thought it is so easy :)? ). It can go even further than that; when you pass +a dependency don't rely on a particular implementation rely on an abstraction (`Dependency Inversion Principle`). + +So you might ask why do I need it? Here is couple reasons: + +### Relying on the global state is evil + +Coding is hard enough ( business requirements are changing all the time, deadlines are +shortening, clients wants more, there are so many unknowns you have to figure out), +relying on unpredictable state makes it even harder: +- it might introduce potential bugs +- makes code harder to maintain +- concurrency becomes harder to achieve +- balancing mokey-patching well is a hard task + +### Great, but now I have additional work I have to manage now all my dependencies write more code and deadlines are coming even closer! + +True, that is why you should pick up Dependency Injection Container to do all this work +for you. Kink gives you one decorator and simple `dict-like` object to bootstrap and manipulate +your container. +No need for manual work and manual dependency management. Give it a try and you will love it! + +# Usage + +To fully utilise the potential of kink it is recommended to bootstrap your initial dependencies +(config values, or instances of classes that are standalone, requires no other dependencies than themselves). +Some people prefer to keep it in `__init__.py` in the top module of your application, other +create separate `bootstra.py` file for this purpose. Once all is setup the only step left +is to decorate your classes/functions with `@inject` decorator. + +## Bootstrapping/Adding services manually + +### Adding *service* to di container + +Dependency container is a dict-like object, adding new service to dependency container is as +simple as the following example: + +```python +from kink import di +from os import getenv + +di["db_name"] = getenv("DB_NAME") +di["db_password"] = getenv("DB_PASSWORD") +``` + +### Adding *on-demand service* to dependency injection container + +Kink also supports on-demand service creation. In order to define such a service, +lambda function should be used: + +```python +from kink import di +from sqlite3 import connect + +di["db_connection"] = lambda di: connect(di["db_name"]) +``` + +In this scenario connection to database will not be established until service is requested. + +### Adding factorised services to dependency injection + +Factorised services are services that are instantiated every time they are requested. + +```python +from kink import di +from sqlite3 import connect + +di.factories["db_connection"] = lambda di: connect(di["db_name"]) + +connection_1 = di["db_connection"] +connection_2 = di["db_connection"] + +connection_1 != connection_2 +``` + +In the above example we defined factorised service `db_connection`, and below by accessing the service from di we created +two separate connection to database. + + +## Requesting services from dependency injection container + +To access given service just reference it inside `di` like you would do this with +a normal dictionary, full example below: + +```python +from kink import di +from sqlite3 import connect + +# Bootstrapping +di["db_name"] = "test_db.db" +di["db_connection"] = lambda di: connect(di["db_name"]) + + +# Getting a service +connection = di["db_connection"] # will return instance of sqlite3.Connection +assert connection == di["db_connection"] # True +``` + + +## Autowiring dependencies + +Autowiring is the ability of the container to automatically create and inject dependencies. +It detects dependencies of the component tries to search for references in the container +and if all references are present an instance of requested service is returned. + +Autowiring system in kink works in two ways: +- matching argument's names +- matching argument's type annotation + +### How dependencies are prioritised by autowiring mechanism + +Autowiring mechanism priorities dependencies automatically, so when multiple +matches are found for the service this is how it works; +Firstly passed arguments are prioritied - if you pass arguments manually to the service +they will take precendence over anything else. Next argument's names are taken into +consideration and last but not least argument's type annotations. + +### Matching argument's names + +If you don't like type annotations or would like to take advantage of autowiring's +precedence mechanism use this style. + +This is a very simple mechanism we have already seen in previous examples. +Autowiring system checks function argument's names and tries to search for +services with the same names inside the container. + +### Matching argument's type annotations + +If you are like me and like type annotations and use static analysis tools this is +a preferred way working with DI container. + +In this scenario names are ignored instead argument's type annotations are inspected +and looked up inside di container. This requires aliases when bootstrapping +your services in DI container or simply adding them to container in the way that +its type is the key by which service is accessed. Please consider the following example: + +```python +from kink import di, inject +from sqlite3 import connect, Connection + + +di["db_name"] = "test_db.db" +di[Connection] = lambda di: connect(di["db_name"]) # sqlite connection can be accessed by its type + +@inject # Constructor injection will happen here +class UserRepository: + def __init__(self, db: Connection): # `db` argument will be resolved because `Connection` instance is present in the container. + self.db = db + +repo = di[UserRepository] +assert repo.db == di[Connection] # True +``` + +## Constructor injection +```python +from kink import inject, di +import MySQLdb + +# Set dependencies +di["db_host"] = "localhost" +di["db_name"] = "test" +di["db_user"] = "user" +di["db_password"] = "password" +di["db_connection"] = lambda di: MySQLdb.connect(host=di["db_host"], user=di["db_user"], passwd=di["db_password"], db=di["db_name"]) + +@inject +class AbstractRepository: + def __init__(self, db_connection): + self.connection = db_connection + + +class UserRepository(AbstractRepository): + ... + + +repository = di[UserRepository] # will retrieve instance of UserRepository from di container +repository.connection # mysql db connection is resolved and available to use. +``` + +When class is annotated by `inject` annotation it will be automatically added to the container for future use (eg autowiring). + + +## Services aliasing + +When you register a service with `@inject` decorator you can attach your own alias name, please consider the following example: + +```python +from kink import inject +from typing import Protocol + +class IUserRepository(Protocol): + ... + +@inject(alias=IUserRepository) +class UserRepository: + ... + + +assert di[IUserRepository] == di[UserRepository] # returns true +``` + +For more examples check [tests](/tests) directory + +### Retrieving all instances with the same alias +Aliases in `kink` do not have to be unique, but by default when autowiring mechnism is called the service that +was registered first within given alias will be returned. If for some reason you would like to retrieve all +services that alias to the same name (eg implementing strategy pattern), `kink` provides a useful functionality +for doing so. Please consider the following example: + +```python +from kink import inject +from typing import Protocol, List + +class IUserRepository(Protocol): + ... + +@inject(alias=IUserRepository) +class MongoUserRepository: + ... + +@inject(alias=IUserRepository) +class MySQLUserRepository: + ... + +@inject() +class UserRepository: + def __init__(self, repos: List[IUserRepository]) -> None: # all services that alias to IUserRepository will be passed here + self._repos = repos + + def store_to_mysql(self, user: ...): + self._repos[1].store(user) + + def store_to_mongo(self, user: ...): + self._repos[0].store(user) +``` + +## Clearing di cache + +Sometimes it might come handy to clear cached services in di container. Simple way of +doing this is calling `di.clear_cache()` method like in the following example. + +```python +from kink import inject, di + +... # set and accesss your services + +di.clear_cache() # this will clear cache of all services inside di container that are not factorised services +``` + +## Integration with FastAPI + +```python +from fastapi import APIRouter, Depends, status +from fastapi.responses import JSONResponse, Response +from kink import di + +router = APIRouter() + +# register service in the DI container +di[ClientService] = ClientService() + +@router.post( + "/clients", + response_model=ClientDTO, + responses={400: {"model": APIErrorMessage}, 500: {"model": APIErrorMessage}}, + tags=["clients"], +) +async def create_client( + request: CreateClientDTO, service: ClientService = Depends(lambda: di[ClientService]) +) -> JSONResponse: + result = service.create(request) + return JSONResponse(content=result.dict(), status_code=status.HTTP_201_CREATED) +``` + +A complete example, together with tests you can find it [here](https://github.com/szymon6927/hexagonal-architecture-python +). + +# Articles on Kink + +- [https://www.netguru.com/codestories/dependency-injection-with-python-make-it-easy](https://www.netguru.com/codestories/dependency-injection-with-python-make-it-easy) + + + +%prep +%autosetup -n kink-0.6.6 + +%build +%py3_build + +%install +%py3_install +install -d -m755 %{buildroot}/%{_pkgdocdir} +if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi +if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi +if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi +if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi +pushd %{buildroot} +if [ -d usr/lib ]; then + find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/lib64 ]; then + find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/bin ]; then + find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/sbin ]; then + find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst +fi +touch doclist.lst +if [ -d usr/share/man ]; then + find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst +fi +popd +mv %{buildroot}/filelist.lst . +mv %{buildroot}/doclist.lst . + +%files -n python3-kink -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Fri May 05 2023 Python_Bot <Python_Bot@openeuler.org> - 0.6.6-1 +- Package Spec generated @@ -0,0 +1 @@ +0846d023f801dbcb9050810e8e5fae58 kink-0.6.6.tar.gz |