{"id":18401970,"url":"https://github.com/vimtor/quickd","last_synced_at":"2025-10-31T00:31:20.463Z","repository":{"id":57459402,"uuid":"346736443","full_name":"vimtor/quickd","owner":"vimtor","description":"🐍💉 Decorator type-based dependency injection for Python ","archived":false,"fork":false,"pushed_at":"2024-12-31T17:16:52.000Z","size":19,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-02-15T20:46:02.144Z","etag":null,"topics":["dependency-injection","python","python-decorators","python-types","unit-of-work"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/quickd","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/vimtor.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}},"created_at":"2021-03-11T14:50:36.000Z","updated_at":"2024-12-31T17:16:56.000Z","dependencies_parsed_at":"2022-08-27T22:51:40.165Z","dependency_job_id":null,"html_url":"https://github.com/vimtor/quickd","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vimtor%2Fquickd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vimtor%2Fquickd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vimtor%2Fquickd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vimtor%2Fquickd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vimtor","download_url":"https://codeload.github.com/vimtor/quickd/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239084352,"owners_count":19578773,"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","python-decorators","python-types","unit-of-work"],"created_at":"2024-11-06T02:40:41.252Z","updated_at":"2025-10-31T00:31:15.151Z","avatar_url":"https://github.com/vimtor.png","language":"Python","readme":"\u003cdiv align=\"center\"\u003e\r\n   \u003ch1 align=\"center\"\u003equickd\u003c/h1\u003e\r\n   \u003cp align=\"center\"\u003eDecorator type-based dependency injection for Python\u003c/p\u003e\r\n   \u003cp align=\"center\"\u003e\r\n    \u003cimg alt=\"GitHub Workflow Status\" src=\"https://img.shields.io/github/workflow/status/vimtor/quickd/Test\"\u003e\r\n    \u003cimg alt=\"GitHub release (latest by date)\" src=\"https://img.shields.io/github/v/release/vimtor/quickd\"\u003e\r\n    \u003cimg alt=\"GitHub\" src=\"https://img.shields.io/github/license/vimtor/quickd\"\u003e\r\n    \u003cimg alt=\"PyPI - Downloads\" src=\"https://img.shields.io/pypi/dw/quickd\"\u003e\r\n   \u003c/p\u003e\r\n\u003c/div\u003e\r\n\r\n## 📦 Installation\r\n\r\nThe package `quickd` supports Python \u003e= 3.5. You can install it by doing:\r\n\r\n```shell\r\n$ pip install quickd\r\n```\r\n\r\n## 📜 Example\r\n\r\nHere is a quick example:\r\n\r\n```python\r\nfrom quickd import inject, factory\r\n\r\n\r\nclass Database:\r\n    pass\r\n\r\n\r\nclass PostgreSQL(Database):\r\n    def __str__(self):\r\n        return 'PostgreSQL'\r\n\r\n\r\nclass MySQL(Database):\r\n    def __str__(self):\r\n        return 'MySQL'\r\n\r\n\r\n@inject\r\ndef print_database(database: Database):\r\n    return print(database)\r\n\r\n\r\n@factory\r\ndef choose_database() -\u003e Database:\r\n    return PostgreSQL()\r\n\r\n\r\nprint_database()  # Prints: PostgreSQL\r\nprint_database(MySQL())  # Prints: MySQL\r\n```\r\n\r\n## 🚀 Usage\r\n\r\nThere are only 3 decorators that compose the whole framework\r\n\r\n### `@factory`\r\n\r\n- Registers an instance for a specific type for later use with `@inject`\r\n- Is mandatory to annotate the function with the return type of the class that you want to inject later\r\n- It is **not** dynamic, so the implementation can only be chosen once\r\n\r\n```python\r\nfrom quickd import factory\r\n\r\n\r\n@factory\r\ndef choose_database() -\u003e Database:\r\n    return PostgreSQL()\r\n```\r\n\r\n### `@inject`\r\n\r\n- Injects dependencies to a function by matching its arguments types with what has been registered\r\n- As you can see below, it also works with constructors\r\n\r\n```python\r\nfrom quickd import inject\r\n\r\n\r\n@inject\r\ndef print_database(database: Database):\r\n    return print(database)\r\n\r\n\r\nclass UserService:\r\n    @inject\r\n    def __init__(self, database: Database): pass\r\n```\r\n\r\n### `@service`\r\n\r\n- Registers a class to be later injectable without using `@factory`\r\n- It also applies `@inject` to its constructor\r\n\r\n```python\r\nfrom quickd import service, inject\r\n\r\n\r\n@service\r\nclass UserService:\r\n    def __init__(self):\r\n        self.users = ['Bob', 'Tom']\r\n\r\n    def all(self):\r\n        return self.users\r\n\r\n    def add(self, user):\r\n        self.users.append(user)\r\n\r\n\r\n@inject\r\ndef get_users(service: UserService):\r\n    return service.all()\r\n\r\n\r\n@inject\r\ndef add_user(service: UserService):\r\n    return service.add(\"Pol\")\r\n\r\n\r\nget_users()  # ['Bob', 'Tom']\r\nadd_user()\r\nget_users()  # ['Bob', 'Tom', 'Pol']\r\n```\r\n\r\n## 👨‍🍳 Recipes\r\n\r\nHere are some common solutions to scenarios you will face.\r\n\r\n### Interfaces\r\n\r\n```python\r\nfrom abc import abstractmethod\r\nfrom quickd import inject, factory\r\n\r\n\r\nclass UserRepository:\r\n    @abstractmethod\r\n    def save(self, user):\r\n        pass\r\n\r\n    @abstractmethod\r\n    def search(self, id):\r\n        pass\r\n\r\n\r\nclass UserCreator:\r\n    @inject\r\n    def __int__(self, repository: UserRepository):\r\n        self.repository = repository\r\n\r\n    def create(self, user):\r\n        self.repository.save(user)\r\n\r\n\r\nclass MySQLUserRepository(UserRepository):\r\n    def __int__(self, host, password):\r\n        self.sql = MySQLConnection(host, password)\r\n\r\n    def save(self, user):\r\n        self.sql.execute('INSERT ...')\r\n\r\n    def search(self, id):\r\n        self.sql.execute('SELECT ...')\r\n\r\n\r\n@factory\r\ndef choose_user_repository() -\u003e UserRepository:  # Notice super class is being used\r\n    return MySQLUserRepository('user', '123')\r\n```\r\n\r\n### Testing\r\n\r\nFollowing the above example we can create a unit test mocking the persistance, which will make our tests easier and\r\nfaster.\r\n\r\n```python\r\nfake_user = {'id': 1, 'name': 'Tom'}\r\n\r\n\r\nclass FakeUserRepository(UserRepository):\r\n    def save(self, user):\r\n        assert user == fake_user\r\n\r\n\r\nrepository = FakeUserRepository()\r\nuser_creator = UserCreator(repository)\r\n\r\nuser_creator.create(fake_user)\r\n```\r\n\r\n### Configuration\r\n\r\nThere are multiple ways to configure your classes. A simple approach is to use environment variables on your factory\r\nannotated methods.\r\n\r\n```python\r\nimport os\r\nfrom quickd import factory\r\n\r\n\r\n@factory\r\ndef choose_database() -\u003e Database:\r\n    username = os.environ.get(\"POSTGRES_USER\")\r\n    password = os.environ.get(\"POSTGRES_PASS\")\r\n    return PostgreSQL(username, password)\r\n```\r\n\r\n## 🧠 Motivation\r\n\r\nDependency injection provides a great way to decouple your classes in order to improve testability and maintainability.\r\n\r\nFrameworks like [Spring](https://spring.io/) or [Symfony](https://symfony.com/) are loved by the community.\r\n\r\n\u003e I will just add a parameter to the constructor and Spring will fill with a global instance of the class\r\n\r\nThese frameworks rely heavy on the type system, to know which class should go where.\r\n\r\nFrom Python 3.5 we have the [typing](https://docs.python.org/3/library/typing.html) package. This addition allows us to\r\nhave the dependency injection framework that Python deserves.\r\n\r\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvimtor%2Fquickd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvimtor%2Fquickd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvimtor%2Fquickd/lists"}