handless


Namehandless JSON
Version 0.1.0 PyPI version JSON
download
home_pageNone
SummaryA Python dependency injection container that automatically resolves and injects dependencies without polluting your code with framework-specific decorators. Inspired by Lagom, Svcs, and C# .NET DI, it keeps your code clean and flexible while offering multiple service registration options. 🚀
upload_time2025-07-16 21:25:07
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseMIT License Copyright (c) 2025 Benoît Godard Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords di ioc dependency injection dependency resolver inversion of control service container
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # handless <!-- omit in toc -->

> :construction: This repository is currently under construction. Its public API might change at any time without notice nor major version bump.

A Python dependency injection container that automatically resolves and injects dependencies without polluting your code with framework-specific decorators. Inspired by [lagom] and [svcs], it keeps your code clean and flexible while offering multiple service registration options. 🚀

- [🔧 What is Dependency Injection, and Why Should You Care?](#-what-is-dependency-injection-and-why-should-you-care)
- [🧱 What is a DI Container?](#-what-is-a-di-container)
- [🚀 What This Library Solves](#-what-this-library-solves)
- [Getting started](#getting-started)
- [Core](#core)
  - [Containers](#containers)
    - [Register a value](#register-a-value)
    - [Register a factory](#register-a-factory)
      - [Use the given type as its own factory](#use-the-given-type-as-its-own-factory)
  - [Register an alias](#register-an-alias)
  - [Lifetimes](#lifetimes)
  - [Context managers and cleanups](#context-managers-and-cleanups)
  - [Context local registry](#context-local-registry)
- [Recipes](#recipes)
  - [Registering implementations for protocols and abstract classes](#registering-implementations-for-protocols-and-abstract-classes)
  - [Choosing dependencies at runtime](#choosing-dependencies-at-runtime)
  - [Use with FastAPI](#use-with-fastapi)
- [Q\&A](#qa)
  - [Why requiring having a context object to resolve types instead of using the container directly?](#why-requiring-having-a-context-object-to-resolve-types-instead-of-using-the-container-directly)
  - [Why using a fluent API to register types as a two step process?](#why-using-a-fluent-api-to-register-types-as-a-two-step-process)
  - [Why using objects for lifetimes? (Why not using enums or literals?)](#why-using-objects-for-lifetimes-why-not-using-enums-or-literals)
- [Contributing](#contributing)

## 🔧 What is Dependency Injection, and Why Should You Care?

In modern software design, **dependency injection (DI)** is a technique where a component’s dependencies are **provided from the outside**, rather than hard-coded inside it. This leads to:

- ✅ More modular and testable code
- ✅ Easier substitution of dependencies (e.g., mocks, stubs, alternative implementations)
- ✅ Clearer separation of concerns

**Example without DI:**

```python
class Service:
    def __init__(self):
        self.db = Database()  # tightly coupled
```

**Example with DI:**

```python
class Service:
    def __init__(self, db: Database):
        self.db = db  # dependency injected
```

---

## 🧱 What is a DI Container?

As your project grows, wiring up dependencies manually becomes tedious and error-prone.

A **DI container** automates this by:

- 🔍 Scanning constructor signatures or factory functions
- 🔗 Resolving and injecting required dependencies
- ♻️ Managing object lifetimes (singleton, transient, scoped...)
- 🧹 Handling cleanup for context-managed resources

Instead of writing all the wiring logic yourself, the container does it for you — predictably and declaratively.

---

## 🚀 What This Library Solves

This library provides a lightweight, flexible **dependency injection container for Python** that helps you:

- ✅ **Register** services with factories, values or aliases
- ✅ **Resolve** dependencies automatically (with type hints or custom logic)
- ✅ **Manage lifecycles** — including context-aware caching and cleanup (singleton, transient, contextual)
- ✅ **Control instantiation** via explicit contexts, ensuring predictability

It’s designed to be **explicit, minimal, and intuitive** — avoiding magic while saving you boilerplate.

## Getting started

Install it through you preferred packages manager:

```shell
pip install handless
```

Once installed, you can create a container allowing you to specify how to resolve your types and start resolving them. Here is an example showcasing most features of the container.

```python
import smtplib
from dataclasses import dataclass
from typing import Protocol

from handless import Container, Contextual, ResolutionContext, Singleton, Transient


@dataclass
class User:
    email: str


@dataclass
class Config:
    smtp_host: str


class UserRepository(Protocol):
    def add(self, cat: User) -> None: ...
    def get(self, email: str) -> User | None: ...


class InMemoryUserRepository(UserRepository):
    def __init__(self) -> None:
        self._users: list[User] = []

    def add(self, user: User) -> None:
        self._users.append(user)

    def get(self, email: str) -> User | None:
        for user in self._users:
            if user.email == email:
                return user
        return None


class NotificationManager(Protocol):
    def send(self, user: User, message: str) -> None: ...


class StdoutNotificationManager(NotificationManager):
    def send(self, user: User, message: str) -> None:
        print(f"{user.email} - {message}")  # noqa: T201


class EmailNotificationManager(NotificationManager):
    def __init__(self, smtp: smtplib.SMTP) -> None:
        self.server = smtp
        self.server.noop()

    def send(self, user: User, message: str) -> None:
        msg = f"Subject: My Service notification\n{message}"
        self.server.sendmail(
            from_addr="myservice@example.com", to_addrs=[user.email], msg=msg
        )


class UserService:
    def __init__(
        self, users: UserRepository, notifications: NotificationManager
    ) -> None:
        self.users = users
        self.notifications = notifications

    def create_user(self, email: str) -> None:
        user = User(email)
        self.users.add(user)
        self.notifications.send(user, "Your account has been created")

    def get_user(self, email: str) -> User:
        user = self.users.get(email)
        if not user:
            msg = f"There is no user with email {email}"
            raise ValueError(msg)
        return user


config = Config(smtp_host="stdout")

container = Container()
container.register(Config).value(config)

# User repository
container.register(InMemoryUserRepository).self(lifetime=Singleton())
container.register(UserRepository).alias(InMemoryUserRepository)  # type: ignore[type-abstract]

# Notification manager
container.register(smtplib.SMTP).factory(
    lambda ctx: smtplib.SMTP(ctx.resolve(Config).smtp_host)),
    lifetime=Singleton(),
    enter=True,
)
container.register(StdoutNotificationManager).self(lifetime=Transient())
container.register(EmailNotificationManager).self()


@container.factory
def create_notification_manager(
    config: Config, ctx: ResolutionContext
) -> NotificationManager:
    if config.smtp_host == "stdout":
        return ctx.resolve(StdoutNotificationManager)
    return ctx.resolve(EmailNotificationManager)


# Top level service
container.register(UserService).self(lifetime=Contextual())


with container.open_context() as ctx:
    service = ctx.resolve(UserService)
    service.create_user("hello.world@handless.io")
    # hello.world@handless.io - Your account has been created
    print(service.get_user("hello.world@handless.io"))  # noqa: T201
    # User(email='hello.world@handless.io')  # noqa: ERA001


container.release()
```

## Core

### Containers

Containers allows to register types and specify how to resolve them (get an instance of this type). Each registered type get a factory function attached depending on how you registered it.

There should be at most one container per entrypoint in your application (a CLI, a HTTP server, ...). You can share the same container for all your entrypoints. A test is considered as an entrypoint as well.

> :bulb: The container should be placed on your application composition root. This can be as simple as a `bootstrap.py` file on your package root.

> :warning The container is the most "high level" component of your application. It can import anything from any sub modules. However, none of your code should depends on the container itself. Otherwise you're going to use the service locator anti-pattern. There can be exceptions to this rule, for example, when used in an HTTP API controllers (as suggested in `svcs`).

#### Register a value

You can register a value directly for your type. When resolved, the provided value will be returned as-is.

```python
from handless import Container


class Foo:
    pass

foo = Foo()
container = Container()
container.register(Foo).value(foo)
resolved_foo = container.open_context().resolve(Foo)
assert resolved_foo is foo
```

#### Register a factory

If you want the container to create instances of your types for you you can instead register a factory. A factory is a callable taking no or several arguments and returning an instance of the type registered. The callable can be a lambda function, a regular function or even a type (a class). When resolved, the container will take care of calling the factory and return its return value. If your factory takes arguments, the container will first resolve its arguments using their type annotations and pass them to the factory.

> :warning: your callable arguments must have type annotation to be properly resolved. If missing, an error will be raised at registration time.

```python
from handless import Container


class Foo:
    def __init__(self, bar: int) -> None:
    self.bar = bar

def create_foo(bar: int) -> Foo:
    return Foo(bar)

container = Container()
container.register(int).value(42)
container.register(Foo).factory(create_foo)
resolved_foo = container.open_context().resolve(Foo)

assert isinstance(resolved_foo, Foo)
assert resolved_foo.bar == 42
```

##### Use the given type as its own factory

When you want to register a type and use it as its own factory, you can use the `self()` method instead. The previous example can be simplified as following:

```python
from handless import Container


class Foo:
    def __init__(self, bar: int) -> None:
    self.bar = bar

container = Container()
container.register(int).value(42)
container.register(Foo).self()
resolved_foo = container.open_context().resolve(Foo)

assert isinstance(resolved_foo, Foo)
assert resolved_foo.bar == 42
```

### Register an alias

> :construction: Under construction

### Lifetimes

> :construction: Under construction

### Context managers and cleanups

If your application has no shutdown mechanism you can register your container `release` method using `atexit` module to release on program exit.

```python
import atexit

from handless import Container

container = Container()
container.register(str).value("hello world!")

# hello world!
atexit.register(container.release)
```

Releasing the container is idempotent and can be used several times. Each time, all singletons will be cleared and then context manager exited, if any.

### Context local registry

> :construction: Under construction

## Recipes

### Registering implementations for protocols and abstract classes

> :construction: Under construction

### Choosing dependencies at runtime

> :construction: Under construction

### Use with FastAPI

> :construction: Under construction

## Q&A

### Why requiring having a context object to resolve types instead of using the container directly?

- Separation of concerns
- Simpler API
- Transient dependencies captivity
- Everything is a context
- Easier management and release of resolved values

### Why using a fluent API to register types as a two step process?

- type hints limitations

### Why using objects for lifetimes? (Why not using enums or literals?)

- Allow creating its own lifetimes
- Allows to add options in the future
- Avoid if statements

## Contributing

Running tests: `uv run nox`

[lagom]: https://lagom-di.readthedocs.io
[svcs]: https://svcs.hynek.me/

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "handless",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "DI, IoC, dependency injection, dependency resolver, inversion of control, service container",
    "author": null,
    "author_email": "g0di <benoit.godard.p@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/1c/45/a1aa1b94b9ff1881ef3a89c31f21a7fa912777b5ea3529562b80555caa2b/handless-0.1.0.tar.gz",
    "platform": null,
    "description": "# handless <!-- omit in toc -->\n\n> :construction: This repository is currently under construction. Its public API might change at any time without notice nor major version bump.\n\nA Python dependency injection container that automatically resolves and injects dependencies without polluting your code with framework-specific decorators. Inspired by [lagom] and [svcs], it keeps your code clean and flexible while offering multiple service registration options. \ud83d\ude80\n\n- [\ud83d\udd27 What is Dependency Injection, and Why Should You Care?](#-what-is-dependency-injection-and-why-should-you-care)\n- [\ud83e\uddf1 What is a DI Container?](#-what-is-a-di-container)\n- [\ud83d\ude80 What This Library Solves](#-what-this-library-solves)\n- [Getting started](#getting-started)\n- [Core](#core)\n  - [Containers](#containers)\n    - [Register a value](#register-a-value)\n    - [Register a factory](#register-a-factory)\n      - [Use the given type as its own factory](#use-the-given-type-as-its-own-factory)\n  - [Register an alias](#register-an-alias)\n  - [Lifetimes](#lifetimes)\n  - [Context managers and cleanups](#context-managers-and-cleanups)\n  - [Context local registry](#context-local-registry)\n- [Recipes](#recipes)\n  - [Registering implementations for protocols and abstract classes](#registering-implementations-for-protocols-and-abstract-classes)\n  - [Choosing dependencies at runtime](#choosing-dependencies-at-runtime)\n  - [Use with FastAPI](#use-with-fastapi)\n- [Q\\&A](#qa)\n  - [Why requiring having a context object to resolve types instead of using the container directly?](#why-requiring-having-a-context-object-to-resolve-types-instead-of-using-the-container-directly)\n  - [Why using a fluent API to register types as a two step process?](#why-using-a-fluent-api-to-register-types-as-a-two-step-process)\n  - [Why using objects for lifetimes? (Why not using enums or literals?)](#why-using-objects-for-lifetimes-why-not-using-enums-or-literals)\n- [Contributing](#contributing)\n\n## \ud83d\udd27 What is Dependency Injection, and Why Should You Care?\n\nIn modern software design, **dependency injection (DI)** is a technique where a component\u2019s dependencies are **provided from the outside**, rather than hard-coded inside it. This leads to:\n\n- \u2705 More modular and testable code\n- \u2705 Easier substitution of dependencies (e.g., mocks, stubs, alternative implementations)\n- \u2705 Clearer separation of concerns\n\n**Example without DI:**\n\n```python\nclass Service:\n    def __init__(self):\n        self.db = Database()  # tightly coupled\n```\n\n**Example with DI:**\n\n```python\nclass Service:\n    def __init__(self, db: Database):\n        self.db = db  # dependency injected\n```\n\n---\n\n## \ud83e\uddf1 What is a DI Container?\n\nAs your project grows, wiring up dependencies manually becomes tedious and error-prone.\n\nA **DI container** automates this by:\n\n- \ud83d\udd0d Scanning constructor signatures or factory functions\n- \ud83d\udd17 Resolving and injecting required dependencies\n- \u267b\ufe0f Managing object lifetimes (singleton, transient, scoped...)\n- \ud83e\uddf9 Handling cleanup for context-managed resources\n\nInstead of writing all the wiring logic yourself, the container does it for you \u2014 predictably and declaratively.\n\n---\n\n## \ud83d\ude80 What This Library Solves\n\nThis library provides a lightweight, flexible **dependency injection container for Python** that helps you:\n\n- \u2705 **Register** services with factories, values or aliases\n- \u2705 **Resolve** dependencies automatically (with type hints or custom logic)\n- \u2705 **Manage lifecycles** \u2014 including context-aware caching and cleanup (singleton, transient, contextual)\n- \u2705 **Control instantiation** via explicit contexts, ensuring predictability\n\nIt\u2019s designed to be **explicit, minimal, and intuitive** \u2014 avoiding magic while saving you boilerplate.\n\n## Getting started\n\nInstall it through you preferred packages manager:\n\n```shell\npip install handless\n```\n\nOnce installed, you can create a container allowing you to specify how to resolve your types and start resolving them. Here is an example showcasing most features of the container.\n\n```python\nimport smtplib\nfrom dataclasses import dataclass\nfrom typing import Protocol\n\nfrom handless import Container, Contextual, ResolutionContext, Singleton, Transient\n\n\n@dataclass\nclass User:\n    email: str\n\n\n@dataclass\nclass Config:\n    smtp_host: str\n\n\nclass UserRepository(Protocol):\n    def add(self, cat: User) -> None: ...\n    def get(self, email: str) -> User | None: ...\n\n\nclass InMemoryUserRepository(UserRepository):\n    def __init__(self) -> None:\n        self._users: list[User] = []\n\n    def add(self, user: User) -> None:\n        self._users.append(user)\n\n    def get(self, email: str) -> User | None:\n        for user in self._users:\n            if user.email == email:\n                return user\n        return None\n\n\nclass NotificationManager(Protocol):\n    def send(self, user: User, message: str) -> None: ...\n\n\nclass StdoutNotificationManager(NotificationManager):\n    def send(self, user: User, message: str) -> None:\n        print(f\"{user.email} - {message}\")  # noqa: T201\n\n\nclass EmailNotificationManager(NotificationManager):\n    def __init__(self, smtp: smtplib.SMTP) -> None:\n        self.server = smtp\n        self.server.noop()\n\n    def send(self, user: User, message: str) -> None:\n        msg = f\"Subject: My Service notification\\n{message}\"\n        self.server.sendmail(\n            from_addr=\"myservice@example.com\", to_addrs=[user.email], msg=msg\n        )\n\n\nclass UserService:\n    def __init__(\n        self, users: UserRepository, notifications: NotificationManager\n    ) -> None:\n        self.users = users\n        self.notifications = notifications\n\n    def create_user(self, email: str) -> None:\n        user = User(email)\n        self.users.add(user)\n        self.notifications.send(user, \"Your account has been created\")\n\n    def get_user(self, email: str) -> User:\n        user = self.users.get(email)\n        if not user:\n            msg = f\"There is no user with email {email}\"\n            raise ValueError(msg)\n        return user\n\n\nconfig = Config(smtp_host=\"stdout\")\n\ncontainer = Container()\ncontainer.register(Config).value(config)\n\n# User repository\ncontainer.register(InMemoryUserRepository).self(lifetime=Singleton())\ncontainer.register(UserRepository).alias(InMemoryUserRepository)  # type: ignore[type-abstract]\n\n# Notification manager\ncontainer.register(smtplib.SMTP).factory(\n    lambda ctx: smtplib.SMTP(ctx.resolve(Config).smtp_host)),\n    lifetime=Singleton(),\n    enter=True,\n)\ncontainer.register(StdoutNotificationManager).self(lifetime=Transient())\ncontainer.register(EmailNotificationManager).self()\n\n\n@container.factory\ndef create_notification_manager(\n    config: Config, ctx: ResolutionContext\n) -> NotificationManager:\n    if config.smtp_host == \"stdout\":\n        return ctx.resolve(StdoutNotificationManager)\n    return ctx.resolve(EmailNotificationManager)\n\n\n# Top level service\ncontainer.register(UserService).self(lifetime=Contextual())\n\n\nwith container.open_context() as ctx:\n    service = ctx.resolve(UserService)\n    service.create_user(\"hello.world@handless.io\")\n    # hello.world@handless.io - Your account has been created\n    print(service.get_user(\"hello.world@handless.io\"))  # noqa: T201\n    # User(email='hello.world@handless.io')  # noqa: ERA001\n\n\ncontainer.release()\n```\n\n## Core\n\n### Containers\n\nContainers allows to register types and specify how to resolve them (get an instance of this type). Each registered type get a factory function attached depending on how you registered it.\n\nThere should be at most one container per entrypoint in your application (a CLI, a HTTP server, ...). You can share the same container for all your entrypoints. A test is considered as an entrypoint as well.\n\n> :bulb: The container should be placed on your application composition root. This can be as simple as a `bootstrap.py` file on your package root.\n\n> :warning The container is the most \"high level\" component of your application. It can import anything from any sub modules. However, none of your code should depends on the container itself. Otherwise you're going to use the service locator anti-pattern. There can be exceptions to this rule, for example, when used in an HTTP API controllers (as suggested in `svcs`).\n\n#### Register a value\n\nYou can register a value directly for your type. When resolved, the provided value will be returned as-is.\n\n```python\nfrom handless import Container\n\n\nclass Foo:\n    pass\n\nfoo = Foo()\ncontainer = Container()\ncontainer.register(Foo).value(foo)\nresolved_foo = container.open_context().resolve(Foo)\nassert resolved_foo is foo\n```\n\n#### Register a factory\n\nIf you want the container to create instances of your types for you you can instead register a factory. A factory is a callable taking no or several arguments and returning an instance of the type registered. The callable can be a lambda function, a regular function or even a type (a class). When resolved, the container will take care of calling the factory and return its return value. If your factory takes arguments, the container will first resolve its arguments using their type annotations and pass them to the factory.\n\n> :warning: your callable arguments must have type annotation to be properly resolved. If missing, an error will be raised at registration time.\n\n```python\nfrom handless import Container\n\n\nclass Foo:\n    def __init__(self, bar: int) -> None:\n    self.bar = bar\n\ndef create_foo(bar: int) -> Foo:\n    return Foo(bar)\n\ncontainer = Container()\ncontainer.register(int).value(42)\ncontainer.register(Foo).factory(create_foo)\nresolved_foo = container.open_context().resolve(Foo)\n\nassert isinstance(resolved_foo, Foo)\nassert resolved_foo.bar == 42\n```\n\n##### Use the given type as its own factory\n\nWhen you want to register a type and use it as its own factory, you can use the `self()` method instead. The previous example can be simplified as following:\n\n```python\nfrom handless import Container\n\n\nclass Foo:\n    def __init__(self, bar: int) -> None:\n    self.bar = bar\n\ncontainer = Container()\ncontainer.register(int).value(42)\ncontainer.register(Foo).self()\nresolved_foo = container.open_context().resolve(Foo)\n\nassert isinstance(resolved_foo, Foo)\nassert resolved_foo.bar == 42\n```\n\n### Register an alias\n\n> :construction: Under construction\n\n### Lifetimes\n\n> :construction: Under construction\n\n### Context managers and cleanups\n\nIf your application has no shutdown mechanism you can register your container `release` method using `atexit` module to release on program exit.\n\n```python\nimport atexit\n\nfrom handless import Container\n\ncontainer = Container()\ncontainer.register(str).value(\"hello world!\")\n\n# hello world!\natexit.register(container.release)\n```\n\nReleasing the container is idempotent and can be used several times. Each time, all singletons will be cleared and then context manager exited, if any.\n\n### Context local registry\n\n> :construction: Under construction\n\n## Recipes\n\n### Registering implementations for protocols and abstract classes\n\n> :construction: Under construction\n\n### Choosing dependencies at runtime\n\n> :construction: Under construction\n\n### Use with FastAPI\n\n> :construction: Under construction\n\n## Q&A\n\n### Why requiring having a context object to resolve types instead of using the container directly?\n\n- Separation of concerns\n- Simpler API\n- Transient dependencies captivity\n- Everything is a context\n- Easier management and release of resolved values\n\n### Why using a fluent API to register types as a two step process?\n\n- type hints limitations\n\n### Why using objects for lifetimes? (Why not using enums or literals?)\n\n- Allow creating its own lifetimes\n- Allows to add options in the future\n- Avoid if statements\n\n## Contributing\n\nRunning tests: `uv run nox`\n\n[lagom]: https://lagom-di.readthedocs.io\n[svcs]: https://svcs.hynek.me/\n",
    "bugtrack_url": null,
    "license": "MIT License  Copyright (c) 2025 Beno\u00eet Godard  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
    "summary": "A Python dependency injection container that automatically resolves and injects dependencies without polluting your code with framework-specific decorators. Inspired by Lagom, Svcs, and C# .NET DI, it keeps your code clean and flexible while offering multiple service registration options. \ud83d\ude80",
    "version": "0.1.0",
    "project_urls": {
        "Changelog": "https://github.com/g0di/handless/blob/main/CHANGELOG.md",
        "Documentation": "https://github.com/g0di/handless",
        "Homepage": "https://github.com/g0di/handless",
        "Source": "https://github.com/g0di/handless"
    },
    "split_keywords": [
        "di",
        " ioc",
        " dependency injection",
        " dependency resolver",
        " inversion of control",
        " service container"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "732ac442ff33a520265dd2e92e3f05ca57ad66ac11f86996abcb8001cda2d797",
                "md5": "5c78cdd460f79a8c8a7cba6c0dbb8cef",
                "sha256": "a34d481135ad2db17bba608a07b45f1436536986789170865cc5701bf1860a87"
            },
            "downloads": -1,
            "filename": "handless-0.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "5c78cdd460f79a8c8a7cba6c0dbb8cef",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 15371,
            "upload_time": "2025-07-16T21:25:06",
            "upload_time_iso_8601": "2025-07-16T21:25:06.307312Z",
            "url": "https://files.pythonhosted.org/packages/73/2a/c442ff33a520265dd2e92e3f05ca57ad66ac11f86996abcb8001cda2d797/handless-0.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "1c45a1aa1b94b9ff1881ef3a89c31f21a7fa912777b5ea3529562b80555caa2b",
                "md5": "57a5b38f9ba530488897b13eb2b84fbf",
                "sha256": "21d3e50e1f8a51050a2227ad4d9bfa2becb0f055d784f1ead6c7746d6741e2d5"
            },
            "downloads": -1,
            "filename": "handless-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "57a5b38f9ba530488897b13eb2b84fbf",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 40506,
            "upload_time": "2025-07-16T21:25:07",
            "upload_time_iso_8601": "2025-07-16T21:25:07.543268Z",
            "url": "https://files.pythonhosted.org/packages/1c/45/a1aa1b94b9ff1881ef3a89c31f21a7fa912777b5ea3529562b80555caa2b/handless-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-16 21:25:07",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "g0di",
    "github_project": "handless",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "handless"
}
        
Elapsed time: 1.55294s