{"id":13458924,"url":"https://github.com/maldoinc/wireup","last_synced_at":"2026-04-15T23:04:12.048Z","repository":{"id":192917613,"uuid":"680634709","full_name":"maldoinc/wireup","owner":"maldoinc","description":"Type-driven dependency injection for Python. Fail-fast validation, explicit lifetimes, native integrations for FastAPI, Flask, Django, and more.","archived":false,"fork":false,"pushed_at":"2026-04-10T19:33:31.000Z","size":8386,"stargazers_count":383,"open_issues_count":17,"forks_count":29,"subscribers_count":4,"default_branch":"master","last_synced_at":"2026-04-10T21:29:44.449Z","etag":null,"topics":["aiohttp","async","dependency-injection","dependency-injection-container","dependency-injection-framework","dependency-injector","design-pattern","di","django","fastapi","flask","free-threading","ioc-container","pep703","python","service-locator","starlette"],"latest_commit_sha":null,"homepage":"https://maldoinc.github.io/wireup/","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/maldoinc.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":".github/contributing.md","funding":null,"license":"license.md","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-08-19T22:09:32.000Z","updated_at":"2026-04-10T19:43:35.000Z","dependencies_parsed_at":"2026-02-21T13:10:55.931Z","dependency_job_id":null,"html_url":"https://github.com/maldoinc/wireup","commit_stats":null,"previous_names":["maldoinc/wireup"],"tags_count":42,"template":false,"template_full_name":null,"purl":"pkg:github/maldoinc/wireup","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maldoinc%2Fwireup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maldoinc%2Fwireup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maldoinc%2Fwireup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maldoinc%2Fwireup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maldoinc","download_url":"https://codeload.github.com/maldoinc/wireup/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maldoinc%2Fwireup/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31863501,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"ssl_error","status_checked_at":"2026-04-15T15:24:39.138Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["aiohttp","async","dependency-injection","dependency-injection-container","dependency-injection-framework","dependency-injector","design-pattern","di","django","fastapi","flask","free-threading","ioc-container","pep703","python","service-locator","starlette"],"created_at":"2024-07-31T09:00:59.792Z","updated_at":"2026-04-15T23:04:12.042Z","avatar_url":"https://github.com/maldoinc.png","language":"Python","funding_links":[],"categories":["Software","Third-Party Packages","Python","Third-Party Extensions"],"sub_categories":["DI Frameworks / Containers","Dependency Injection"],"readme":"\u003cdiv align=\"center\"\u003e\n\u003ch1\u003eWireup\u003c/h1\u003e\n\u003cp\u003e\nType-driven dependency injection for Python. Wireup is battle-tested in production, thread-safe, no-GIL (PEP 703) ready, and fail-fast by design: \u003cstrong\u003eif the container starts, it works\u003c/strong\u003e.\n\u003c/p\u003e\n\n[![GitHub](https://img.shields.io/github/license/maldoinc/wireup)](https://github.com/maldoinc/wireup)\n[![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/maldoinc/wireup/run_all.yml)](https://github.com/maldoinc/wireup)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/wireup)](https://pypi.org/project/wireup/)\n[![PyPI - Version](https://img.shields.io/pypi/v/wireup)](https://pypi.org/project/wireup/)\n\u003cbr /\u003e\n\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#quick-start\"\u003e\u003cstrong\u003eQuick Start\u003c/strong\u003e\u003c/a\u003e \u0026middot;\n  \u003ca href=\"https://maldoinc.github.io/wireup\"\u003e\u003cstrong\u003eDocs\u003c/strong\u003e\u003c/a\u003e \u0026middot;\n  \u003ca href=\"https://maldoinc.github.io/wireup/latest/benchmarks/\"\u003e\u003cstrong\u003eBenchmarks\u003c/strong\u003e\u003c/a\u003e \u0026middot;\n  \u003ca href=\"https://maldoinc.github.io/wireup/latest/migrate_to_wireup/\"\u003e\u003cstrong\u003eMigrate to Wireup\u003c/strong\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Why Wireup?\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd align=\"center\" valign=\"top\" width=\"33%\"\u003e\n\u003ch3\u003e🔁 Define Once, Use Anywhere\u003c/h3\u003e\nReuse the same application code in APIs, CLIs, workers, and scripts without rewriting your wiring.\n\u003c/td\u003e\n\u003ctd align=\"center\" valign=\"top\" width=\"33%\"\u003e\n\u003ch3\u003e✅ Correct by Default\u003c/h3\u003e\nIf the container starts, your dependency graph is valid. Wireup checks for missing or misconfigured dependencies to avoid surprises at runtime. See \u003ca href=\"https://maldoinc.github.io/wireup/latest/what_wireup_validates/\"\u003eWhat Wireup Validates\u003c/a\u003e\n\u003c/td\u003e\n\u003ctd align=\"center\" valign=\"top\" width=\"33%\"\u003e\n\u003ch3\u003e🌐 Framework-Ready\u003c/h3\u003e\nNative integrations for \u003cstrong\u003eFastAPI\u003c/strong\u003e, \u003cstrong\u003eDjango\u003c/strong\u003e, \u003cstrong\u003eFlask\u003c/strong\u003e, \u003cstrong\u003eStarlette\u003c/strong\u003e, \u003cstrong\u003eCelery\u003c/strong\u003e, \u003cstrong\u003eClick\u003c/strong\u003e, \u003cstrong\u003eTyper\u003c/strong\u003e, and more.\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd align=\"center\" valign=\"top\"\u003e\n\u003ch3\u003e⚡ Zero-Overhead Handlers\u003c/h3\u003e\nResolve singleton constructor dependencies once at startup in \u003ca href=\"https://maldoinc.github.io/wireup/latest/integrations/fastapi/class_based_handlers/\"\u003eFastAPI\u003c/a\u003e and \u003ca href=\"https://maldoinc.github.io/wireup/latest/integrations/aiohttp/class_based_handlers/\"\u003eAIOHTTP\u003c/a\u003e class-based handlers, not per request.\n\u003c/td\u003e\n\u003ctd align=\"center\" valign=\"top\"\u003e\n\u003ch3\u003e🧩 Advanced Wiring\u003c/h3\u003e\nGo beyond simple constructor injection with\n\u003ca href=\"https://maldoinc.github.io/wireup/latest/reusable_bundles/\"\u003ereusable bundles\u003c/a\u003e,\n\u003ca href=\"https://maldoinc.github.io/wireup/latest/lifetimes_and_scopes/\"\u003eexplicit scope context sharing\u003c/a\u003e, and\nmore using plain Python.\n\u003c/td\u003e\n\u003ctd align=\"center\" valign=\"top\"\u003e\n\u003ch3\u003e🧪 Easy to test\u003c/h3\u003e\nOverride dependencies with context managers, keep tests isolated, and restore the original graph automatically.\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## Benchmarks\n\n\u003cp align=\"center\"\u003e\n    \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"docs/pages/img/benchmarks_scoped_dark.svg\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"docs/pages/img/benchmarks_scoped_light.svg\"\u003e\n    \u003cimg alt=\"Scoped Dependency Injection Performance\" src=\"docs/pages/img/benchmarks_scoped_light.svg\" height=\"300\"\u003e\n    \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ci\u003eDense dependency graph resolved per request in FastAPI + Uvicorn\u003cbr\u003e\n    (Requests per second, higher is better. Manual Wiring represents the upper bound.)\u003cbr\u003e\n    Full methodology and reproducibility: \u003ca href=\"https://maldoinc.github.io/wireup/latest/benchmarks/\"\u003ebenchmarks\u003c/a\u003e.\u003c/i\u003e\n\u003c/p\u003e\n\n## Installation\n\n```bash\npip install wireup\n```\n\n\n## Quick Start\n\n```python\nimport fastapi\nimport wireup\nimport wireup.integration.fastapi\nfrom wireup import Injected, injectable\n\n\n@injectable\nclass Database:\n    def query(self, sql: str) -\u003e list[str]: ...\n\n\n@injectable\nclass UserService:\n    def __init__(self, db: Database) -\u003e None:\n        self.db = db\n\n    def get_users(self) -\u003e list[str]:\n        return self.db.query(\"SELECT name FROM users\")\n\n\napp = fastapi.FastAPI()\n\n\n@app.get(\"/users\")\ndef get_users(service: Injected[UserService]) -\u003e list[str]:\n    return service.get_users()\n\n\ncontainer = wireup.create_async_container(injectables=[Database, UserService])\nwireup.integration.fastapi.setup(container, app)\n```\n\nFor a full end-to-end walkthrough, start with the [Getting Started guide](https://maldoinc.github.io/wireup/latest/getting_started/).\n\n## Basic Usage\n\nWireup also supports config injection, decorator-free domain models, and package-level registration.\n\n**1. Inject Configuration**\n\nInject configuration alongside dependencies. No need to write factories just to pass a config value.\n\n```python\n@injectable\nclass Database:\n    def __init__(self, url: Annotated[str, Inject(config=\"db_url\")]) -\u003e None:\n        self.engine = sqlalchemy.create_engine(url)\n\ncontainer = wireup.create_sync_container(\n    injectables=[Database],\n    config={\"db_url\": os.environ[\"DB_URL\"]}\n)\n```\n\n**2. Clean Architecture**\n\nNeed strict boundaries? Use factories to wire pure domain objects and integrate external libraries like Pydantic.\n\n```python\n# 1. No Wireup imports\nclass Database:\n    def __init__(self, url: str) -\u003e None:\n        self.engine = create_engine(url)\n\n# 2. Configuration (Pydantic)\nclass Settings(BaseModel):\n    db_url: str = \"sqlite://\"\n```\n\n```python\n# 3. Wireup factories\n@injectable\ndef make_settings() -\u003e Settings:\n    return Settings()\n\n@injectable\ndef make_database(settings: Settings) -\u003e Database:\n    return Database(url=settings.db_url)\n\ncontainer = wireup.create_sync_container(injectables=[make_settings, make_database])\n```\n\n**3. Package-level registration**\n\nNo need to list every injectable manually. Provide entire modules or packages to register all at once.\n\n```python\nimport app\nimport wireup\n\ncontainer = wireup.create_sync_container(\n    injectables=[\n        app.services,\n        app.repositories,\n        app.factories\n    ]\n)\n```\n\n## More Features\n\n### 🎯 Function Injection\n\nInject dependencies into CLI commands, background tasks, event handlers, or any standalone function that needs container access.\n\n```python\n@inject_from_container(container)\ndef migrate_database(db: Injected[Database], settings: Injected[Settings]) -\u003e None:\n    ...\n```\n\n### 📝 Interfaces \u0026 Abstractions\n\nBind implementations to interfaces using Protocols or ABCs.\n\n```python\nclass Notifier(Protocol):\n    def notify(self) -\u003e None: ...\n\n@injectable(as_type=Notifier)\nclass SlackNotifier:\n    def notify(self) -\u003e None: ...\n\n# SlackNotifier is injected wherever Notifier is requested\n@app.post(\"/notify\")\ndef send_notification(notifier: Injected[Notifier]) -\u003e None:\n    notifier.notify()\n```\n\n### 🏭 Factories \u0026 Resources\n\nDefer instantiation to specialized factories when complex initialization or cleanup is required. \nFull support for sync, async, and generator factories. Wireup handles cleanup at the right time based on lifetime.\n\n```python\nclass WeatherClient:\n    def __init__(self, client: requests.Session) -\u003e None:\n        self.client = client\n\n@injectable\ndef weather_client_factory() -\u003e Iterator[WeatherClient]:\n    with requests.Session() as session:\n        yield WeatherClient(client=session)\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eAsync example\u003c/summary\u003e\n\n```python\nclass WeatherClient:\n    def __init__(self, client: aiohttp.ClientSession) -\u003e None:\n        self.client = client\n\n@injectable\nasync def weather_client_factory() -\u003e AsyncIterator[WeatherClient]:\n    async with aiohttp.ClientSession() as session:\n        yield WeatherClient(client=session)\n```\n\n\u003c/details\u003e\n\n\n### 🔄 Lifetimes \u0026 Scopes\n\nDeclare dependencies as `singleton`, `scoped`, or `transient` to control reuse explicitly.\n\n```python\n# Singleton: one instance per application (default)\n@injectable\nclass Settings:\n    pass\n\n# Async singleton with cleanup — no lru_cache, no app.state\n@injectable\nasync def database_factory(settings: Settings) -\u003e AsyncIterator[AsyncConnection]:\n    async with create_async_engine(settings.db_url).connect() as connection:\n        yield connection\n\n# Scoped: one instance per request, shared within that request\n@injectable(lifetime=\"scoped\")\nclass RequestContext:\n    def __init__(self) -\u003e None:\n        self.request_id = uuid4()\n\n# Transient: fresh instance every time\n@injectable(lifetime=\"transient\")\nclass OrderProcessor:\n    pass\n```\n\n### 🛡️ Startup Validation\n\nWireup validates the dependency graph when the container is created. See \u003ca href=\"https://maldoinc.github.io/wireup/latest/what_wireup_validates/\"\u003eWhat Wireup Validates\u003c/a\u003e for the full rules and limits.\n\n```python\n# Missing dependencies: caught at startup, not at runtime\n@injectable\nclass Foo:\n    def __init__(self, unknown: NotManagedByWireup) -\u003e None: ...\n\ncontainer = wireup.create_sync_container(injectables=[Foo])\n# ❌ Parameter 'unknown' of 'Foo' depends on an unknown injectable 'NotManagedByWireup'.\n```\n\nIt also catches circular dependencies, duplicate registrations, misconfigured lifetimes, and missing config at startup.\n\n### 🧪 Testing\n\nWireup decorators only collect metadata. Injectables are plain classes and functions, so you can test them directly with no special setup.\n\nSwap dependencies during tests with `container.override`:\n\n```python\nwith container.override.injectable(target=Database, new=in_memory_database):\n    # Injectables that depend on Database will receive in_memory_database\n    # for the duration of this context manager\n    response = client.get(\"/users\")\n```\n\n## 📚 Documentation\n\nSee the docs for integrations, lifetimes, factories, testing, and more advanced patterns.\n\n[https://maldoinc.github.io/wireup](https://maldoinc.github.io/wireup)\n\nIf Wireup is useful to you, a star on GitHub helps others find it.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaldoinc%2Fwireup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaldoinc%2Fwireup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaldoinc%2Fwireup/lists"}