{"id":13458893,"url":"https://github.com/kodemore/kink","last_synced_at":"2025-05-14T17:03:18.355Z","repository":{"id":37493326,"uuid":"230152763","full_name":"kodemore/kink","owner":"kodemore","description":"Dependency injection container made for Python","archived":false,"fork":false,"pushed_at":"2024-10-19T09:57:51.000Z","size":204,"stargazers_count":424,"open_issues_count":11,"forks_count":28,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-12T08:36:20.489Z","etag":null,"topics":["dependency-injection","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kodemore.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-12-25T20:57:36.000Z","updated_at":"2025-03-31T01:10:43.000Z","dependencies_parsed_at":"2023-01-25T11:45:31.686Z","dependency_job_id":"67e87933-e7dd-4286-9b25-4588918118a4","html_url":"https://github.com/kodemore/kink","commit_stats":{"total_commits":66,"total_committers":7,"mean_commits":9.428571428571429,"dds":"0.12121212121212122","last_synced_commit":"da169b2f7dcae9349f2ee457a8b247751d89a2a3"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodemore%2Fkink","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodemore%2Fkink/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodemore%2Fkink/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodemore%2Fkink/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kodemore","download_url":"https://codeload.github.com/kodemore/kink/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254190375,"owners_count":22029632,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency-injection","python"],"created_at":"2024-07-31T09:00:59.426Z","updated_at":"2025-05-14T17:03:18.305Z","avatar_url":"https://github.com/kodemore.png","language":"Python","readme":"# Kink ![PyPI](https://img.shields.io/pypi/v/kink) ![Linting and Tests](https://github.com/kodemore/kink/workflows/Linting%20and%20Tests/badge.svg?branch=master) [![codecov](https://codecov.io/gh/kodemore/kink/branch/master/graph/badge.svg)](https://codecov.io/gh/kodemore/kink) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\nDependency injection container made for python\n\n## Features\n\n- Easy to use interface\n- Extensible with custom dependency resolvers\n- Automatic dependency injection (Autowiring)\n- Lightweight\n- Support for async with asyncio\n\n\n## Installation\n\n### Pip\n\n```shell\npip install kink\n```\n\n### Poetry\nIf you don't know poetry, I highly recommend visiting their [webpage](https://python-poetry.org)\n\n```shell\npoetry add kink\n```\n\n# Why using dependency injection in python?\n\n## Short story \n\nBecause python is a multi paradigm language and this should encourage \nyou to use best OOP practices improving your workflow and your code and have more time\nfor your hobbies and families instead monkey-patching entire world.\n\n## Long story\n\nDependency happens when one component (component might be a class, or a function) `A` uses other component \n`B`. We say than that `A` depends on `B`.\n\nInstead hardcoding dependency inside your components and making your code tightly coupled\nyou are loosing it by providing(injecting) required behaviour either by subclassing or\nplugging additional code. This is called `Inversion of Control` which keeps your code\noriented around behaviour rather than control. There are many benefits coming out of it:\n- increased modularity\n- better extensibility and flexibility\n- it helps you understand higher concepts like event driven programming\n\nThis is where dependency injection comes in place. Dependency injection is a specific\nstyle of inversion of control, which generally says instead hardcoding dependency pass\ndependant object as a parameter to a method rather than having method creating it itself.\n( who would thought it is so easy :)? ). It can go even further than that; when you pass\na dependency don't rely on a particular implementation rely on an abstraction (`Dependency Inversion Principle`).\n\nSo you might ask why do I need it? Here is couple reasons:\n\n### Relying on the global state is evil\n\nCoding is hard enough ( business requirements are changing all the time, deadlines are\nshortening, clients wants more, there are so many unknowns you have to figure out), \nrelying on unpredictable state makes it even harder:\n- it might introduce potential bugs\n- makes code harder to maintain\n- concurrency becomes harder to achieve\n- balancing mokey-patching well is a hard task\n\n### Great, but now I have additional work I have to manage now all my dependencies write more code and deadlines are coming even closer!\n\nTrue, that is why you should pick up Dependency Injection Container to do all this work \nfor you. Kink gives you one decorator and simple `dict-like` object to bootstrap and manipulate\nyour container.\nNo need for manual work and manual dependency management. Give it a try and you will love it!\n\n# Usage\n\nTo fully utilise the potential of kink it is recommended to bootstrap your initial dependencies\n(config values, or instances of classes that are standalone, requires no other dependencies than themselves).\nSome people prefer to keep it in `__init__.py` in the top module of your application, other\ncreate separate `bootstrap.py` file for this purpose. Once all is setup the only step left \nis to decorate your classes/functions with `@inject` decorator.\n\n## Bootstrapping/Adding services manually\n\n### Adding *service* to di container\n\nDependency container is a dict-like object, adding new service to dependency container is as \nsimple as the following example:\n\n```python\nfrom kink import di\nfrom os import getenv\n\ndi[\"db_name\"] = getenv(\"DB_NAME\")\ndi[\"db_password\"] = getenv(\"DB_PASSWORD\")\n```\n\n### Adding *on-demand service* to dependency injection container\n\nKink also supports on-demand service creation. In order to define such a service, \nlambda function should be used: \n\n```python\nfrom kink import di\nfrom sqlite3 import connect\n\ndi[\"db_connection\"] = lambda di: connect(di[\"db_name\"])\n```\n\nIn this scenario connection to database will not be established until service is requested.\n\n### Adding factorised services to dependency injection\n\nFactorised services are services that are instantiated every time they are requested.\n\n```python\nfrom kink import di\nfrom sqlite3 import connect\n\ndi.factories[\"db_connection\"] = lambda di: connect(di[\"db_name\"])\n\nconnection_1 = di[\"db_connection\"]\nconnection_2 = di[\"db_connection\"]\n\nconnection_1 != connection_2\n```\n\nIn the above example we defined factorised service `db_connection`, and below by accessing the service from di we created\ntwo separate connection to database.\n\n\n## Requesting services from dependency injection container\n\nTo access given service just reference it inside `di` like you would do this with\na normal dictionary, full example below:\n\n```python\nfrom kink import di\nfrom sqlite3 import connect\n\n# Bootstrapping\ndi[\"db_name\"] = \"test_db.db\"\ndi[\"db_connection\"] = lambda di: connect(di[\"db_name\"])\n\n\n# Getting a service\nconnection = di[\"db_connection\"] # will return instance of sqlite3.Connection\nassert connection == di[\"db_connection\"] # True\n```\n\n\n## Autowiring dependencies\n\nAutowiring is the ability of the container to automatically create and inject dependencies.\nIt detects dependencies of the component tries to search for references in the container\nand if all references are present an instance of requested service is returned.\n\nAutowiring system in kink works in two ways:\n- matching argument's names\n- matching argument's type annotation\n\n### How dependencies are prioritised by autowiring mechanism\n\nAutowiring mechanism priorities dependencies automatically, so when multiple\nmatches are found for the service this is how it works;\nFirstly passed arguments are prioritied - if you pass arguments manually to the service\nthey will take precendence over anything else. Next argument's names are taken into\nconsideration and last but not least argument's type annotations.\n\n### Matching argument's names\n\nIf you don't like type annotations or would like to take advantage of autowiring's \nprecedence mechanism use this style.\n\nThis is a very simple mechanism we have already seen in previous examples. \nAutowiring system checks function argument's names and tries to search for \nservices with the same names inside the container. \n\n### Matching argument's type annotations\n\nIf you are like me and like type annotations and use static analysis tools this is\na preferred way working with DI container. \n\nIn this scenario names are ignored instead argument's type annotations are inspected\nand looked up inside di container. This requires aliases when bootstrapping \nyour services in DI container or simply adding them to container in the way that\nits type is the key by which service is accessed. Please consider the following example:\n\n```python\nfrom kink import di, inject\nfrom sqlite3 import connect, Connection\n\n\ndi[\"db_name\"] = \"test_db.db\"\ndi[Connection] = lambda di: connect(di[\"db_name\"])  # sqlite connection can be accessed by its type\n\n@inject # Constructor injection will happen here\nclass UserRepository:\n  def __init__(self, db: Connection): # `db` argument will be resolved because `Connection` instance is present in the container. \n    self.db = db\n\nrepo = di[UserRepository]\nassert repo.db == di[Connection] # True\n```\n\n## Constructor injection\n```python\nfrom kink import inject, di\nimport MySQLdb\n\n# Set dependencies\ndi[\"db_host\"] = \"localhost\"\ndi[\"db_name\"] = \"test\"\ndi[\"db_user\"] = \"user\"\ndi[\"db_password\"] = \"password\"\ndi[\"db_connection\"] = lambda di: MySQLdb.connect(host=di[\"db_host\"], user=di[\"db_user\"], passwd=di[\"db_password\"], db=di[\"db_name\"])\n\n@inject\nclass AbstractRepository:\n    def __init__(self, db_connection):\n        self.connection = db_connection\n\n\nclass UserRepository(AbstractRepository):\n    ...\n\n\nrepository = di[UserRepository] # will retrieve instance of UserRepository from di container\nrepository.connection # mysql db connection is resolved and available to use.\n```\n\nWhen class is annotated by `inject` annotation it will be automatically added to the container for future use (eg autowiring).\n\n\n## Services aliasing\n\nWhen you register a service with `@inject` decorator you can attach your own alias name, please consider the following example:\n\n```python\nfrom kink import inject\nfrom typing import Protocol\n\nclass IUserRepository(Protocol):\n    ...\n\n@inject(alias=IUserRepository)\nclass UserRepository:\n    ...\n\n\nassert di[IUserRepository] == di[UserRepository] # returns true\n```\n\nFor more examples check [tests](/tests) directory\n\n### Retrieving all instances with the same alias\nAliases in `kink` do not have to be unique, but by default when autowiring mechnism is called the service that\nwas registered first within given alias will be returned. If for some reason you would like to retrieve all\nservices that alias to the same name (eg implementing strategy pattern), `kink` provides a useful functionality\nfor doing so. Please consider the following example:\n\n```python\nfrom kink import inject\nfrom typing import Protocol, List\n\nclass IUserRepository(Protocol):\n    ...\n\n@inject(alias=IUserRepository)\nclass MongoUserRepository:\n    ...\n\n@inject(alias=IUserRepository)\nclass MySQLUserRepository:\n    ...\n\n@inject()\nclass UserRepository:\n    def __init__(self, repos: List[IUserRepository]) -\u003e None: # all services that alias to IUserRepository will be passed here\n        self._repos = repos\n        \n    def store_to_mysql(self, user: ...):\n        self._repos[1].store(user)\n    \n    def store_to_mongo(self, user: ...):\n        self._repos[0].store(user)\n```\n\n## Clearing di cache\n\nSometimes it might come handy to clear cached services in di container. Simple way of \ndoing this is calling `di.clear_cache()` method like in the following example.\n\n```python\nfrom kink import inject, di\n\n... # set and accesss your services\n\ndi.clear_cache() # this will clear cache of all services inside di container that are not factorised services\n```\n\n## Integration with FastAPI\n\n```python\nfrom fastapi import APIRouter, Depends, status\nfrom fastapi.responses import JSONResponse, Response\nfrom kink import di\n\nrouter = APIRouter()\n\n# register service in the DI container\ndi[ClientService] = ClientService()\n\n@router.post(\n    \"/clients\",\n    response_model=ClientDTO,\n    responses={400: {\"model\": APIErrorMessage}, 500: {\"model\": APIErrorMessage}},\n    tags=[\"clients\"],\n)\nasync def create_client(\n    request: CreateClientDTO, service: ClientService = Depends(lambda: di[ClientService])\n) -\u003e JSONResponse:\n    result = service.create(request)\n    return JSONResponse(content=result.dict(), status_code=status.HTTP_201_CREATED)\n```\n\nA complete example, together with tests you can find it [here](https://github.com/szymon6927/hexagonal-architecture-python\n).\n\n# Articles on Kink\n\n- [https://www.netguru.com/codestories/dependency-injection-with-python-make-it-easy](https://www.netguru.com/codestories/dependency-injection-with-python-make-it-easy)\n\n","funding_links":[],"categories":["Software","Python"],"sub_categories":["DI Frameworks / Containers"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkodemore%2Fkink","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkodemore%2Fkink","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkodemore%2Fkink/lists"}