<div align="center">
<h1>Wireup</h1>
<p>Performant, concise and type-safe Dependency Injection for Python 3.8+</p>
[](https://github.com/maldoinc/wireup)
[](https://github.com/maldoinc/wireup)
[](https://pypi.org/project/wireup/)
[](https://pypi.org/project/wireup/)
<p><a target="_blank" href="https://maldoinc.github.io/wireup">๐ Documentation</a> | <a target="_blank" href="https://github.com/maldoinc/wireup-demo">๐ฎ Demo Application</a></p>
</div>
Dependency Injection (DI) is a design pattern where dependencies are provided externally rather than created within objects. Wireup automates dependency management using Python's type system, with support for async, generators, modern Python features and integrations for FastAPI, Django, Flask and AIOHTTP out of the box.
> [!TIP]
> **New**: Inject Dependencies in FastAPI with zero runtime overhead using [Class-Based Handlers](https://maldoinc.github.io/wireup/latest/integrations/fastapi/class_based_handlers/).
## Features
### โจ Simple & Type-Safe DI
Inject services and configuration using a clean and intuitive syntax.
```python
@service
class Database:
pass
@service
class UserService:
def __init__(self, db: Database) -> None:
self.db = db
container = wireup.create_sync_container(services=[Database, UserService])
user_service = container.get(UserService) # โ
Dependencies resolved.
```
<details>
<summary>Example With Configuration</summary>
```python
@service
class Database:
def __init__(self, db_url: Annotated[str, Inject(param="db_url")]) -> None:
self.db_url = db_url
container = wireup.create_sync_container(
services=[Database],
parameters={"db_url": os.environ["APP_DB_URL"]}
)
database = container.get(Database) # โ
Dependencies resolved.
```
</details>
### ๐ฏ Function Injection
Inject dependencies directly into functions with a simple decorator.
```python
@inject_from_container(container)
def process_users(service: Injected[UserService]):
# โ
UserService injected.
pass
```
### ๐ Interfaces & Abstract Classes
Define abstract types and have the container automatically inject the implementation.
```python
@abstract
class Notifier(abc.ABC):
pass
@service
class SlackNotifier(Notifier):
pass
notifier = container.get(Notifier)
# โ
SlackNotifier instance.
```
### ๐ Managed Service Lifetimes
Declare dependencies as singletons, scoped, or transient to control whether to inject a fresh copy or reuse existing instances.
```python
# Singleton: One instance per application. `@service(lifetime="singleton")` is the default.
@service
class Database:
pass
# Scoped: One instance per scope/request, shared within that scope/request.
@service(lifetime="scoped")
class RequestContext:
def __init__(self) -> None:
self.request_id = uuid4()
# Transient: When full isolation and clean state is required.
# Every request to create transient services results in a new instance.
@service(lifetime="transient")
class OrderProcessor:
pass
```
### ๐ญ Flexible Creation Patterns
Defer instantiation to specialized factories when complex initialization or cleanup is required.
Full support for async and generators. Wireup handles cleanup at the correct time depending on the service lifetime.
**Synchronous**
```python
class WeatherClient:
def __init__(self, client: requests.Session) -> None:
self.client = client
@service
def weather_client_factory() -> Iterator[WeatherClient]:
with requests.Session() as session:
yield WeatherClient(client=session)
```
**Async**
```python
class WeatherClient:
def __init__(self, client: aiohttp.ClientSession) -> None:
self.client = client
@service
async def weather_client_factory() -> AsyncIterator[WeatherClient]:
async with aiohttp.ClientSession() as session:
yield WeatherClient(client=session)
```
### ๐ก๏ธ Improved Safety
Wireup is mypy strict compliant and will not introduce type errors in your code. It will also warn you at the earliest possible stage about configuration errors to avoid surprises.
**Container Creation**
The container will raise errors at creation time about missing dependencies or other issues.
```python
@service
class Foo:
def __init__(self, unknown: NotManagedByWireup) -> None:
pass
container = wireup.create_sync_container(services=[Foo])
# โ Parameter 'unknown' of 'Foo' depends on an unknown service 'NotManagedByWireup'.
```
**Function Injection**
Injected functions will raise errors at module import time rather than when called.
```python
@inject_from_container(container)
def my_function(oops: Injected[NotManagedByWireup]):
pass
# โ Parameter 'oops' of 'my_function' depends on an unknown service 'NotManagedByWireup'.
```
**Integrations**
Wireup integrations assert that requested injections in the framework are valid.
```python
@app.get("/")
def home(foo: Injected[NotManagedByWireup]):
pass
wireup.integration.flask.setup(container, app)
# โ Parameter 'foo' of 'home' depends on an unknown service 'NotManagedByWireup'.
```
### ๐ Framework-Agnostic
Wireup provides its own Dependency Injection mechanism and is not tied to specific frameworks. Use it anywhere you like.
### ๐ Share Services Between Application and CLI
Share the service layer between your web application and its accompanying CLI using Wireup.
### ๐ Native Integration with Django, FastAPI, Flask and AIOHTTP
Integrate with popular frameworks for a smoother developer experience.
Integrations manage request scopes, injection in endpoints, and lifecycle of services.
```python
app = FastAPI()
container = wireup.create_async_container(services=[UserService, Database])
@app.get("/")
def users_list(user_service: Injected[UserService]):
pass
wireup.integration.fastapi.setup(container, app)
```
### ๐งช Simplified Testing
Wireup does not patch your services and lets you test them in isolation.
If you need to use the container in your tests, you can have it create parts of your services
or perform dependency substitution.
```python
with container.override.service(target=Database, new=in_memory_database):
# The /users endpoint depends on Database.
# During the lifetime of this context manager, requests to inject `Database`
# will result in `in_memory_database` being injected instead.
response = client.get("/users")
```
## ๐ Documentation
For more information [check out the documentation](https://maldoinc.github.io/wireup)
## ๐ฎ Demo application
A demo flask application is available at [maldoinc/wireup-demo](https://github.com/maldoinc/wireup-demo)
Raw data
{
"_id": null,
"home_page": "https://github.com/maldoinc/wireup",
"name": "wireup",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.8",
"maintainer_email": null,
"keywords": "flask, django, injector, dependency injection, dependency injection container, dependency injector",
"author": "Aldo Mateli",
"author_email": "aldo.mateli@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/c4/ae/c2b8bc3b92f968a4da17c586ec5e5c77b62b6dd15212c5a947bab09b43e1/wireup-2.0.1.tar.gz",
"platform": null,
"description": "<div align=\"center\">\n<h1>Wireup</h1>\n<p>Performant, concise and type-safe Dependency Injection for Python 3.8+</p>\n\n[](https://github.com/maldoinc/wireup)\n[](https://github.com/maldoinc/wireup)\n[](https://pypi.org/project/wireup/)\n[](https://pypi.org/project/wireup/)\n\n<p><a target=\"_blank\" href=\"https://maldoinc.github.io/wireup\">\ud83d\udcda Documentation</a> | <a target=\"_blank\" href=\"https://github.com/maldoinc/wireup-demo\">\ud83c\udfae Demo Application</a></p>\n</div>\n\nDependency Injection (DI) is a design pattern where dependencies are provided externally rather than created within objects. Wireup automates dependency management using Python's type system, with support for async, generators, modern Python features and integrations for FastAPI, Django, Flask and AIOHTTP out of the box.\n\n> [!TIP]\n> **New**: Inject Dependencies in FastAPI with zero runtime overhead using [Class-Based Handlers](https://maldoinc.github.io/wireup/latest/integrations/fastapi/class_based_handlers/).\n\n\n## Features\n\n### \u2728 Simple & Type-Safe DI\n\nInject services and configuration using a clean and intuitive syntax.\n\n```python\n@service\nclass Database:\n pass\n\n@service\nclass UserService:\n def __init__(self, db: Database) -> None:\n self.db = db\n\ncontainer = wireup.create_sync_container(services=[Database, UserService])\nuser_service = container.get(UserService) # \u2705 Dependencies resolved.\n```\n\n<details>\n<summary>Example With Configuration</summary>\n\n```python\n@service\nclass Database:\n def __init__(self, db_url: Annotated[str, Inject(param=\"db_url\")]) -> None:\n self.db_url = db_url\n\ncontainer = wireup.create_sync_container(\n services=[Database], \n parameters={\"db_url\": os.environ[\"APP_DB_URL\"]}\n)\ndatabase = container.get(Database) # \u2705 Dependencies resolved.\n```\n\n</details>\n\n### \ud83c\udfaf Function Injection\n\nInject dependencies directly into functions with a simple decorator.\n\n```python\n@inject_from_container(container)\ndef process_users(service: Injected[UserService]):\n # \u2705 UserService injected.\n pass\n```\n\n### \ud83d\udcdd Interfaces & Abstract Classes\n\nDefine abstract types and have the container automatically inject the implementation.\n\n```python\n@abstract\nclass Notifier(abc.ABC):\n pass\n\n@service\nclass SlackNotifier(Notifier):\n pass\n\nnotifier = container.get(Notifier)\n# \u2705 SlackNotifier instance.\n```\n\n\n### \ud83d\udd04 Managed Service Lifetimes\n\nDeclare dependencies as singletons, scoped, or transient to control whether to inject a fresh copy or reuse existing instances.\n\n\n```python\n# Singleton: One instance per application. `@service(lifetime=\"singleton\")` is the default.\n@service\nclass Database:\n pass\n\n# Scoped: One instance per scope/request, shared within that scope/request.\n@service(lifetime=\"scoped\")\nclass RequestContext:\n def __init__(self) -> None:\n self.request_id = uuid4()\n\n# Transient: When full isolation and clean state is required.\n# Every request to create transient services results in a new instance.\n@service(lifetime=\"transient\")\nclass OrderProcessor:\n pass\n```\n\n\n### \ud83c\udfed Flexible Creation Patterns\n\nDefer instantiation to specialized factories when complex initialization or cleanup is required.\nFull support for async and generators. Wireup handles cleanup at the correct time depending on the service lifetime.\n\n**Synchronous**\n\n```python\nclass WeatherClient:\n def __init__(self, client: requests.Session) -> None:\n self.client = client\n\n@service\ndef weather_client_factory() -> Iterator[WeatherClient]:\n with requests.Session() as session:\n yield WeatherClient(client=session)\n```\n\n**Async**\n\n```python\nclass WeatherClient:\n def __init__(self, client: aiohttp.ClientSession) -> None:\n self.client = client\n\n@service\nasync def weather_client_factory() -> AsyncIterator[WeatherClient]:\n async with aiohttp.ClientSession() as session:\n yield WeatherClient(client=session)\n```\n\n\n### \ud83d\udee1\ufe0f Improved Safety\n\nWireup is mypy strict compliant and will not introduce type errors in your code. It will also warn you at the earliest possible stage about configuration errors to avoid surprises.\n\n**Container Creation**\n\nThe container will raise errors at creation time about missing dependencies or other issues.\n\n```python\n@service\nclass Foo:\n def __init__(self, unknown: NotManagedByWireup) -> None:\n pass\n\ncontainer = wireup.create_sync_container(services=[Foo])\n# \u274c Parameter 'unknown' of 'Foo' depends on an unknown service 'NotManagedByWireup'.\n```\n\n**Function Injection**\n\nInjected functions will raise errors at module import time rather than when called.\n\n```python\n@inject_from_container(container)\ndef my_function(oops: Injected[NotManagedByWireup]):\n pass\n\n# \u274c Parameter 'oops' of 'my_function' depends on an unknown service 'NotManagedByWireup'.\n```\n\n**Integrations**\n\nWireup integrations assert that requested injections in the framework are valid.\n\n```python\n@app.get(\"/\")\ndef home(foo: Injected[NotManagedByWireup]):\n pass\n\nwireup.integration.flask.setup(container, app)\n# \u274c Parameter 'foo' of 'home' depends on an unknown service 'NotManagedByWireup'.\n```\n\n### \ud83d\udccd Framework-Agnostic\n\nWireup provides its own Dependency Injection mechanism and is not tied to specific frameworks. Use it anywhere you like.\n\n### \ud83d\udd17 Share Services Between Application and CLI\n\nShare the service layer between your web application and its accompanying CLI using Wireup.\n\n### \ud83d\udd0c Native Integration with Django, FastAPI, Flask and AIOHTTP\n\nIntegrate with popular frameworks for a smoother developer experience.\nIntegrations manage request scopes, injection in endpoints, and lifecycle of services.\n\n```python\napp = FastAPI()\ncontainer = wireup.create_async_container(services=[UserService, Database])\n\n@app.get(\"/\")\ndef users_list(user_service: Injected[UserService]):\n pass\n\nwireup.integration.fastapi.setup(container, app)\n```\n\n### \ud83e\uddea Simplified Testing\n\nWireup does not patch your services and lets you test them in isolation.\n\nIf you need to use the container in your tests, you can have it create parts of your services\nor perform dependency substitution.\n\n```python\nwith container.override.service(target=Database, new=in_memory_database):\n # The /users endpoint depends on Database.\n # During the lifetime of this context manager, requests to inject `Database`\n # will result in `in_memory_database` being injected instead.\n response = client.get(\"/users\")\n```\n\n## \ud83d\udcda Documentation\n\nFor more information [check out the documentation](https://maldoinc.github.io/wireup)\n\n## \ud83c\udfae Demo application\n\nA demo flask application is available at [maldoinc/wireup-demo](https://github.com/maldoinc/wireup-demo)\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Python Dependency Injection Library",
"version": "2.0.1",
"project_urls": {
"Changelog": "https://github.com/maldoinc/wireup/releases",
"Documentation": "https://maldoinc.github.io/wireup/",
"Homepage": "https://github.com/maldoinc/wireup",
"Repository": "https://github.com/maldoinc/wireup"
},
"split_keywords": [
"flask",
" django",
" injector",
" dependency injection",
" dependency injection container",
" dependency injector"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "a980f0e9a28307ae140a67f054c9464e36fc5df341aa8d9d5a0837e20c675cce",
"md5": "056ce109348642a1f27236bdeac2ef42",
"sha256": "436c892d796ccebdc2235be8be7f8c81565ad357819692cb68a00fb1db501652"
},
"downloads": -1,
"filename": "wireup-2.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "056ce109348642a1f27236bdeac2ef42",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.8",
"size": 32483,
"upload_time": "2025-07-20T18:57:24",
"upload_time_iso_8601": "2025-07-20T18:57:24.515974Z",
"url": "https://files.pythonhosted.org/packages/a9/80/f0e9a28307ae140a67f054c9464e36fc5df341aa8d9d5a0837e20c675cce/wireup-2.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "c4aec2b8bc3b92f968a4da17c586ec5e5c77b62b6dd15212c5a947bab09b43e1",
"md5": "3f9c117531aea51b7ffe8e3f072b8447",
"sha256": "c4e0e8860e5aa6389f67ab1a4d1fd9cbaf137bdeff1bbf48f88715501078b2e6"
},
"downloads": -1,
"filename": "wireup-2.0.1.tar.gz",
"has_sig": false,
"md5_digest": "3f9c117531aea51b7ffe8e3f072b8447",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.8",
"size": 26606,
"upload_time": "2025-07-20T18:57:25",
"upload_time_iso_8601": "2025-07-20T18:57:25.862942Z",
"url": "https://files.pythonhosted.org/packages/c4/ae/c2b8bc3b92f968a4da17c586ec5e5c77b62b6dd15212c5a947bab09b43e1/wireup-2.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-20 18:57:25",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "maldoinc",
"github_project": "wireup",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "wireup"
}