{"id":27603535,"url":"https://github.com/taskiq-python/taskiq-dependencies","last_synced_at":"2025-04-22T19:17:23.062Z","repository":{"id":142762320,"uuid":"544366917","full_name":"taskiq-python/taskiq-dependencies","owner":"taskiq-python","description":"FastAPI-like dependency injection implementation","archived":false,"fork":false,"pushed_at":"2025-02-26T22:07:15.000Z","size":254,"stargazers_count":17,"open_issues_count":1,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-11T08:36:47.654Z","etag":null,"topics":["dependency-injection","fastapi-like","taskiq"],"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/taskiq-python.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}},"created_at":"2022-10-02T09:48:48.000Z","updated_at":"2025-02-26T22:06:30.000Z","dependencies_parsed_at":"2023-03-15T01:04:48.349Z","dependency_job_id":"5f732028-c162-4b4e-b93f-cd51e3aee94a","html_url":"https://github.com/taskiq-python/taskiq-dependencies","commit_stats":{"total_commits":71,"total_committers":3,"mean_commits":"23.666666666666668","dds":0.09859154929577463,"last_synced_commit":"6caa86e75ca9bd7d366c4946eaeeb3cf3d21c42c"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taskiq-python%2Ftaskiq-dependencies","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taskiq-python%2Ftaskiq-dependencies/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taskiq-python%2Ftaskiq-dependencies/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taskiq-python%2Ftaskiq-dependencies/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/taskiq-python","download_url":"https://codeload.github.com/taskiq-python/taskiq-dependencies/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249235017,"owners_count":21235129,"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","fastapi-like","taskiq"],"created_at":"2025-04-22T19:17:21.604Z","updated_at":"2025-04-22T19:17:23.042Z","avatar_url":"https://github.com/taskiq-python.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Taskiq dependencies\n\nThis project is used to add FastAPI-like dependency injection to projects.\n\nThis project is a part of the taskiq, but it doesn't have any dependencies,\nand you can easily integrate it in any project.\n\n# Installation\n\n```bash\npip install taskiq-dependencies\n```\n\n# Usage\n\nLet's imagine you want to add DI in your project. What should you do?\nAt first we need to create a dependency graph, check if there any cycles\nand compute the order of dependencies. This can be done with DependencyGraph.\nIt does all of those actions on create. So we can remember all graphs at the start of\nour program for later use. Or we can do it when needed, but it's less optimal.\n\n```python\nfrom taskiq_dependencies import Depends\n\n\ndef dep1() -\u003e int:\n    return 1\n\n\ndef target_func(some_int: int = Depends(dep1)):\n    print(some_int)\n    return some_int + 1\n\n```\n\nIn this example we have a function called `target_func` and as you can see, it depends on `dep1` dependency.\n\nTo create a dependnecy graph have to write this:\n```python\nfrom taskiq_dependencies import DependencyGraph\n\ngraph = DependencyGraph(target_func)\n```\n\nThat's it. Now we want to resolve all dependencies and call a function. It's simple as this:\n\n```python\nwith graph.sync_ctx() as ctx:\n    graph.target(**ctx.resolve_kwargs())\n```\n\nVoila! We resolved all dependencies and called a function with no arguments.\nThe `resolve_kwargs` function will return a dict, where keys are parameter names, and values are resolved dependencies.\n\n\n### Async usage\n\nIf your lib is asynchronous, you should use async context, it's similar to sync context, but instead of `with` you should use `async with`. But this way your users can use async dependencies and async generators. It's not possible in sync context.\n\n\n```python\nasync with graph.async_ctx() as ctx:\n    kwargs = await ctx.resolve_kwargs()\n```\n\n## Q\u0026A\n\n\u003e Why should I use `with` or `async with` statements?\n\nBecuase users can use generator functions as dependencies.\nEverything before `yield` happens before injecting the dependency, and everything after `yield` is executed after the `with` statement is over.\n\n\u003e How to provide default dependencies?\n\nIt maybe useful to have default dependencies for your project.\nFor example, taskiq has `Context` and `State` classes that can be used as dependencies. `sync_context` and `async_context` methods have a parameter, where you can pass a dict with precalculated dependencies.\n\n\n```python\nfrom taskiq_dependencies import Depends, DependencyGraph\n\n\nclass DefaultDep:\n    ...\n\n\ndef target_func(dd: DefaultDep = Depends()):\n    print(dd)\n    return 1\n\n\ngraph = DependencyGraph(target_func)\n\nwith graph.sync_ctx({DefaultDep: DefaultDep()}) as ctx:\n    print(ctx.resolve_kwargs())\n\n```\n\nYou can run this code. It will resolve dd dependency into a `DefaultDep` variable you provide.\n\n\n## Getting parameters information\n\nIf you want to get the information about how this dependency was specified,\nyou can use special class `ParamInfo` for that.\n\n```python\nfrom taskiq_dependencies import Depends, DependencyGraph, ParamInfo\n\n\ndef dependency(info: ParamInfo = Depends()) -\u003e str:\n    assert info.name == \"dd\"\n    return info.name\n\ndef target_func(dd: str = Depends(dependency)):\n    print(dd)\n    return 1\n\n\ngraph = DependencyGraph(target_func)\n\nwith graph.sync_ctx() as ctx:\n    print(ctx.resolve_kwargs())\n\n```\n\nThe ParamInfo has the information about name and parameters signature. It's useful if you want to create a dependency that changes based on parameter name, or signature.\n\n\nAlso ParamInfo contains the initial graph that was used.\n\n## Exception propagation\n\nBy default if error happens within the context, we send this error to the dependency,\nso you can close it properly. You can disable this functionality by setting `exception_propagation` parameter to `False`.\n\nLet's imagine that you want to get a database session from pool and commit after the function is done.\n\n\n```python\nasync def get_session():\n    session = sessionmaker()\n\n    yield session\n\n    await session.commit()\n\n```\n\nBut what if the error happened when the dependant function was called? In this case you want to rollback, instead of commit.\nTo solve this problem, you can just wrap the `yield` statement in `try except` to handle the error.\n\n```python\nasync def get_session():\n    session = sessionmaker()\n\n    try:\n        yield session\n    except Exception:\n        await session.rollback()\n        return\n\n    await session.commit()\n\n```\n\n**Also, as a library developer, you can disable exception propagation**. If you do so, then no exception will ever be propagated to dependencies and no such `try except` expression will ever work.\n\n\nExample of disabled propogation.\n\n```python\n\ngraph = DependencyGraph(target_func)\n\nwith graph.sync_ctx(exception_propagation=False) as ctx:\n    print(ctx.resolve_kwargs())\n\n\n```\n\n\n## Generics support\n\nWe support generics substitution for class-based dependencies.\nFor example, let's define an interface and a class. This class can be\nparameterized with some type and we consider this type a dependency.\n\n```python\nimport abc\nfrom typing import Any, Generic, TypeVar\n\nclass MyInterface(abc.ABC):\n    @abc.abstractmethod\n    def getval(self) -\u003e Any:\n        ...\n\n\n_T = TypeVar(\"_T\", bound=MyInterface)\n\n\nclass MyClass(Generic[_T]):\n    # We don't know exact type, but we assume\n    # that it can be used as a dependency.\n    def __init__(self, resource: _T = Depends()):\n        self.resource = resource\n\n    @property\n    def my_value(self) -\u003e Any:\n        return self.resource.getval()\n\n```\n\nNow let's create several implementation of defined interface:\n\n```python\n\ndef getstr() -\u003e str:\n    return \"strstr\"\n\n\ndef getint() -\u003e int:\n    return 100\n\n\nclass MyDep1(MyInterface):\n    def __init__(self, s: str = Depends(getstr)) -\u003e None:\n        self.s = s\n\n    def getval(self) -\u003e str:\n        return self.s\n\n\nclass MyDep2(MyInterface):\n    def __init__(self, i: int = Depends(getint)) -\u003e None:\n        self.i = i\n\n    def getval(self) -\u003e int:\n        return self.i\n\n```\n\nNow you can use these dependencies by just setting proper type hints.\n\n```python\ndef my_target(\n    d1: MyClass[MyDep1] = Depends(),\n    d2: MyClass[MyDep2] = Depends(),\n) -\u003e None:\n    print(d1.my_value)\n    print(d2.my_value)\n\n\nwith DependencyGraph(my_target).sync_ctx() as ctx:\n    my_target(**ctx.resolve_kwargs())\n\n```\n\nThis code will is going to print:\n\n```\nstrstr\n100\n```\n\n## Dependencies replacement\n\nYou can replace dependencies in runtime, it will recalculate graph\nand will execute your function with updated dependencies.\n\n**!!! This functionality tremendously slows down dependency resolution.**\n\nUse this functionality only for tests. Otherwise, you will end up building dependency graphs on every resolution request. Which is very slow.\n\nBut for tests it may be a game changer, since you don't want to change your code, but some dependencies instead.\n\nHere's an example. Imagine you have a built graph for a specific function, like this:\n\n```python\nfrom taskiq_dependencies import DependencyGraph, Depends\n\n\ndef dependency() -\u003e int:\n    return 1\n\n\ndef target(dep_value: int = Depends(dependency)) -\u003e None:\n    assert dep_value == 1\n\ngraph = DependencyGraph(target)\n```\n\nNormally, you would call the target, by writing something like this:\n\n```python\nwith graph.sync_ctx() as ctx:\n    target(**ctx.resolve_kwargs())\n```\n\nBut what if you want to replace dependency in runtime, just\nbefore resolving kwargs? The solution is to add `replaced_deps`\nparameter to the context method. For example:\n\n```python\ndef replaced() -\u003e int:\n    return 2\n\n\nwith graph.sync_ctx(replaced_deps={dependency: replaced}) as ctx:\n    target(**ctx.resolve_kwargs())\n```\n\nFurthermore, the new dependency can depend on other dependencies. Or you can change type of your dependency, like generator instead of plain return. Everything should work as you would expect it.\n\n## Annotated types\n\nTaskiq dependenices also support dependency injection through Annotated types.\n\n```python\nfrom typing import Annotated\n\nasync def my_function(dependency: Annotated[int, Depends(my_func)]):\n    pass\n```\n\nOr you can specify classes\n\n\n```python\nfrom typing import Annotated\n\nclass MyClass:\n    pass\n\nasync def my_function(dependency: Annotated[MyClass, Depends(my_func)]):\n    pass\n```\n\nAnd, of course you can easily save such type aliases in variables.\n\n```python\nfrom typing import Annotated\n\nDepType = Annotated[int, Depends(my_func)]\n\ndef my_function(dependency: DepType):\n    pass\n\n```\n\nAlso we support overrides for annotated types.\n\nFor example:\n\n```python\nfrom typing import Annotated\n\nDepType = Annotated[int, Depends(my_func)]\n\ndef my_function(\n    dependency: DepType,\n    no_cache_dep: Annotated[DepType, Depends(my_func, use_cache=False)],\n) -\u003e None:\n    pass\n\n```\n\nAlso, please note that if you're using `from __future__ import annotations` it won't work for python \u003c= 3.9. Because the `inspect.signature` function doesn't support it. In all future versions it will work as expected.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaskiq-python%2Ftaskiq-dependencies","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftaskiq-python%2Ftaskiq-dependencies","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaskiq-python%2Ftaskiq-dependencies/lists"}