pico-ioc


Namepico-ioc JSON
Version 0.4.0 PyPI version JSON
download
home_pageNone
SummaryA minimalist, zero-dependency Inversion of Control (IoC) container for Python.
upload_time2025-08-10 14:44:50
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseNone
keywords ioc di dependency injection inversion of control decorator
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # πŸ“¦ Pico-IoC: A Minimalist IoC Container for Python

[![PyPI](https://img.shields.io/pypi/v/pico-ioc.svg)](https://pypi.org/project/pico-ioc/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
![CI (tox matrix)](https://github.com/dperezcabrera/pico-ioc/actions/workflows/ci.yml/badge.svg)

**Pico-IoC** is a tiny, zero-dependency, decorator-based Inversion of Control container for Python.
Build loosely-coupled, testable apps without manual wiring. Inspired by the Spring ecosystem.

---

## ✨ Key Features

* **Zero dependencies** β€” pure Python.
* **Decorator API** β€” `@component`, `@factory_component`, `@provides`.
* **Auto discovery** β€” scans a package and registers components.
* **Eager by default, fail-fast** β€” non-lazy bindings are instantiated immediately after `init()`. Missing deps fail startup.
* **Opt-in lazy** β€” set `lazy=True` to defer creation (wrapped in `ComponentProxy`).
* **Factories** β€” encapsulate complex creation logic.
* **Smart resolution** β€” by **parameter name**, then **type annotation**, then **MRO fallback**, then **string(name)**.
* **Re-entrancy guard** β€” prevents `get()` during scanning.
* **Auto-exclude caller** β€” `init()` skips the calling module to avoid double scanning.

---

## πŸ“¦ Installation

```bash
pip install pico-ioc
```

---

## πŸš€ Quick Start

```python
from pico_ioc import component, init

@component
class AppConfig:
    def get_db_url(self):
        return "postgresql://user:pass@host/db"

@component
class DatabaseService:
    def __init__(self, config: AppConfig):
        self._cs = config.get_db_url()
    def get_data(self):
        return f"Data from {self._cs}"

container = init(__name__)  # blueprint runs here (eager + fail-fast)
db = container.get(DatabaseService)
print(db.get_data())
```

---

## 🧩 Custom Component Keys

```python
from pico_ioc import component, init

@component(name="config")  # custom key
class AppConfig:
    db_url = "postgresql://user:pass@localhost/db"

@component
class Repository:
    def __init__(self, config: "config"):  # resolve by NAME
        self.url = config.db_url

container = init(__name__)
print(container.get("config").db_url)
```

---

## 🏭 Factories and `@provides`

* Default is **eager** (`lazy=False`). Eager bindings are constructed at the end of `init()`.
* Use `lazy=True` for on-first-use creation via `ComponentProxy`.

```python
from pico_ioc import factory_component, provides, init

COUNTER = {"value": 0}

@factory_component
class ServicesFactory:
    @provides(key="heavy_service", lazy=True)
    def heavy(self):
        COUNTER["value"] += 1
        return {"payload": "hello"}

container = init(__name__)
svc = container.get("heavy_service")  # not created yet
print(COUNTER["value"])               # 0
print(svc["payload"])                 # triggers creation
print(COUNTER["value"])               # 1
```

---

## 🧠 Dependency Resolution Order

1. parameter **name**
2. exact **type annotation**
3. **MRO fallback** (walk base classes)
4. `str(name)`

---

## ⚑ Eager vs. Lazy (Blueprint Behavior)

At the end of `init()`, Pico-IoC performs a **blueprint**:

- **Eager** (`lazy=False`, default): instantiated immediately; failures stop startup.
- **Lazy** (`lazy=True`): returns a `ComponentProxy`; instantiated on first real use.

**Lifecycle:**

           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           β”‚        init()         β”‚
           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚
                      β–Ό
           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           β”‚   Scan & bind deps    β”‚
           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚
                      β–Ό
           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           β”‚  Blueprint instantiates all β”‚
           β”‚    non-lazy (eager) beans   β”‚
           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚
           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           β”‚   Container ready     β”‚
           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜


**Best practice:** keep eager+fail-fast for production parity with Spring; use lazy only for heavy/optional deps or to support negative tests.

---

## πŸ”„ Migration Guide (v0.2.1 β†’ v0.3.0)

* **Defaults changed:** `@component` and `@provides` now default to `lazy=False` (eager).
* **Proxy renamed:** `LazyProxy` β†’ `ComponentProxy` (only relevant if referenced directly).
* **Tests/fixtures:** components intentionally missing deps should be marked `@component(lazy=True)` (to avoid failing `init()`), or excluded from the scan.

Example fix for an intentional failure case:

```python
@component(lazy=True)
class MissingDep:
    def __init__(self, missing):
        self.missing = missing
```

---

## πŸ›  API Reference

### `init(root, *, exclude=None, auto_exclude_caller=True) -> PicoContainer`

Scan and bind components in `root` (str module name or module).
Skips the calling module if `auto_exclude_caller=True`.
Runs blueprint (instantiate all `lazy=False` bindings).

### `@component(cls=None, *, name=None, lazy=False)`

Register a class as a component.
Use `name` for a custom key.
Set `lazy=True` to defer creation.

### `@factory_component`

Mark a class as a component factory (its methods can `@provides` bindings).

### `@provides(key, *, lazy=False)`

Declare that a factory method provides a component under `key`.
Set `lazy=True` for deferred creation (`ComponentProxy`).

---

## πŸ§ͺ Testing

```bash
pip install tox
tox -e py311
```

Tip: for β€œmissing dependency” tests, mark those components as `lazy=True` so `init()` remains fail-fast for real components while your test still asserts failure on resolution.

---

## πŸ”Œ Extensibility: Plugins, Binder, and Lifecycle Hooks

From `v0.4.0` onward, Pico-IoC can be cleanly extended without patching the core.
This enables optional integration layers like `pico-ioc-rest` for Flask, FastAPI, etc., while keeping the core dependency-free.

### Plugin Protocol

A plugin is any object that implements some or all of the following methods:

```python
from pico_ioc import PicoPlugin, Binder

class MyPlugin:
    def before_scan(self, package, binder: Binder): ...
    def visit_class(self, module, cls, binder: Binder): ...
    def after_scan(self, package, binder: Binder): ...
    def after_bind(self, container, binder: Binder): ...
    def before_eager(self, container, binder: Binder): ...
    def after_ready(self, container, binder: Binder): ...
```

All hooks are optional. If present, they are called in this order during `init()`:

1. **before\_scan** β€” called before package scanning starts.
2. **visit\_class** β€” called for every class discovered during scanning.
3. **after\_scan** β€” called after scanning all modules.
4. **after\_bind** β€” called after the core has bound all components/factories.
5. **before\_eager** β€” called right before eager (non-lazy) instantiation.
6. **after\_ready** β€” called after all eager instantiation is complete.

### Binder API

Plugins receive a [`Binder`](#binder-api) instance in each hook, allowing them to:

* **bind**: register new providers in the container.
* **has**: check if a binding exists.
* **get**: resolve a binding immediately.

Example plugin that binds a β€œmarker” component when a certain class is discovered:

```python
class MarkerPlugin:
    def visit_class(self, module, cls, binder):
        if cls.__name__ == "SpecialService" and not binder.has("marker"):
            binder.bind("marker", lambda: {"ok": True}, lazy=False)

container = init("my_app", plugins=(MarkerPlugin(),))
assert container.get("marker") == {"ok": True}
```

### Creating Extensions

With the plugin API, you can build separate packages like `pico-ioc-rest`:

```python
from pico_ioc import PicoPlugin, Binder, create_instance, resolve_param
from flask import Flask

class FlaskRestPlugin:
    def __init__(self):
        self.controllers = []

    def visit_class(self, module, cls, binder: Binder):
        if getattr(cls, "_is_controller", False):
            self.controllers.append(cls)

    def after_bind(self, container, binder: Binder):
        app: Flask = container.get(Flask)
        for ctl_cls in self.controllers:
            ctl = create_instance(ctl_cls, container)
            # register routes here using `resolve_param` for handler DI
```

### Public Helpers for Extensions

Plugins can reuse Pico-IoC’s DI logic without duplicating it:

* **`create_instance(cls, container)`** β€” instantiate a class with DI, respecting Pico-IoC’s resolution order.
* **`resolve_param(container, parameter)`** β€” resolve a single function/class parameter via Pico-IoC rules.

---

## ❓ FAQ

**Q: Can I make the container lenient at startup?**
A: By design it’s strict. Prefer `lazy=True` on specific bindings or exclude problem modules from the scan.

**Q: Thread safety?**
A: Container uses `ContextVar` to guard re-entrancy during scanning. Singletons are created once per container; typical usage is in single-threaded app startup, then read-mostly.

**Q: Frameworks?**
A: Framework-agnostic. Works with Flask, FastAPI, CLIs, scripts, etc.

---

## πŸ“œ License

MIT β€” see [LICENSE](https://opensource.org/licenses/MIT)



            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "pico-ioc",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "ioc, di, dependency injection, inversion of control, decorator",
    "author": null,
    "author_email": "David Perez Cabrera <dperezcabrera@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/f7/a7/cf3ea9c7b4cc2ab37d9dbf13406e23f3f3ff813eff438dbdee309ba7c08a/pico_ioc-0.4.0.tar.gz",
    "platform": null,
    "description": "# \ud83d\udce6 Pico-IoC: A Minimalist IoC Container for Python\n\n[![PyPI](https://img.shields.io/pypi/v/pico-ioc.svg)](https://pypi.org/project/pico-ioc/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n![CI (tox matrix)](https://github.com/dperezcabrera/pico-ioc/actions/workflows/ci.yml/badge.svg)\n\n**Pico-IoC** is a tiny, zero-dependency, decorator-based Inversion of Control container for Python.\nBuild loosely-coupled, testable apps without manual wiring. Inspired by the Spring ecosystem.\n\n---\n\n## \u2728 Key Features\n\n* **Zero dependencies** \u2014 pure Python.\n* **Decorator API** \u2014 `@component`, `@factory_component`, `@provides`.\n* **Auto discovery** \u2014 scans a package and registers components.\n* **Eager by default, fail-fast** \u2014 non-lazy bindings are instantiated immediately after `init()`. Missing deps fail startup.\n* **Opt-in lazy** \u2014 set `lazy=True` to defer creation (wrapped in `ComponentProxy`).\n* **Factories** \u2014 encapsulate complex creation logic.\n* **Smart resolution** \u2014 by **parameter name**, then **type annotation**, then **MRO fallback**, then **string(name)**.\n* **Re-entrancy guard** \u2014 prevents `get()` during scanning.\n* **Auto-exclude caller** \u2014 `init()` skips the calling module to avoid double scanning.\n\n---\n\n## \ud83d\udce6 Installation\n\n```bash\npip install pico-ioc\n```\n\n---\n\n## \ud83d\ude80 Quick Start\n\n```python\nfrom pico_ioc import component, init\n\n@component\nclass AppConfig:\n    def get_db_url(self):\n        return \"postgresql://user:pass@host/db\"\n\n@component\nclass DatabaseService:\n    def __init__(self, config: AppConfig):\n        self._cs = config.get_db_url()\n    def get_data(self):\n        return f\"Data from {self._cs}\"\n\ncontainer = init(__name__)  # blueprint runs here (eager + fail-fast)\ndb = container.get(DatabaseService)\nprint(db.get_data())\n```\n\n---\n\n## \ud83e\udde9 Custom Component Keys\n\n```python\nfrom pico_ioc import component, init\n\n@component(name=\"config\")  # custom key\nclass AppConfig:\n    db_url = \"postgresql://user:pass@localhost/db\"\n\n@component\nclass Repository:\n    def __init__(self, config: \"config\"):  # resolve by NAME\n        self.url = config.db_url\n\ncontainer = init(__name__)\nprint(container.get(\"config\").db_url)\n```\n\n---\n\n## \ud83c\udfed Factories and `@provides`\n\n* Default is **eager** (`lazy=False`). Eager bindings are constructed at the end of `init()`.\n* Use `lazy=True` for on-first-use creation via `ComponentProxy`.\n\n```python\nfrom pico_ioc import factory_component, provides, init\n\nCOUNTER = {\"value\": 0}\n\n@factory_component\nclass ServicesFactory:\n    @provides(key=\"heavy_service\", lazy=True)\n    def heavy(self):\n        COUNTER[\"value\"] += 1\n        return {\"payload\": \"hello\"}\n\ncontainer = init(__name__)\nsvc = container.get(\"heavy_service\")  # not created yet\nprint(COUNTER[\"value\"])               # 0\nprint(svc[\"payload\"])                 # triggers creation\nprint(COUNTER[\"value\"])               # 1\n```\n\n---\n\n## \ud83e\udde0 Dependency Resolution Order\n\n1. parameter **name**\n2. exact **type annotation**\n3. **MRO fallback** (walk base classes)\n4. `str(name)`\n\n---\n\n## \u26a1 Eager vs. Lazy (Blueprint Behavior)\n\nAt the end of `init()`, Pico-IoC performs a **blueprint**:\n\n- **Eager** (`lazy=False`, default): instantiated immediately; failures stop startup.\n- **Lazy** (`lazy=True`): returns a `ComponentProxy`; instantiated on first real use.\n\n**Lifecycle:**\n\n           \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n           \u2502        init()         \u2502\n           \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                      \u2502\n                      \u25bc\n           \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n           \u2502   Scan & bind deps    \u2502\n           \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                      \u2502\n                      \u25bc\n           \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n           \u2502  Blueprint instantiates all \u2502\n           \u2502    non-lazy (eager) beans   \u2502\n           \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                      \u2502\n           \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n           \u2502   Container ready     \u2502\n           \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n\n**Best practice:** keep eager+fail-fast for production parity with Spring; use lazy only for heavy/optional deps or to support negative tests.\n\n---\n\n## \ud83d\udd04 Migration Guide (v0.2.1 \u2192 v0.3.0)\n\n* **Defaults changed:** `@component` and `@provides` now default to `lazy=False` (eager).\n* **Proxy renamed:** `LazyProxy` \u2192 `ComponentProxy` (only relevant if referenced directly).\n* **Tests/fixtures:** components intentionally missing deps should be marked `@component(lazy=True)` (to avoid failing `init()`), or excluded from the scan.\n\nExample fix for an intentional failure case:\n\n```python\n@component(lazy=True)\nclass MissingDep:\n    def __init__(self, missing):\n        self.missing = missing\n```\n\n---\n\n## \ud83d\udee0 API Reference\n\n### `init(root, *, exclude=None, auto_exclude_caller=True) -> PicoContainer`\n\nScan and bind components in `root` (str module name or module).\nSkips the calling module if `auto_exclude_caller=True`.\nRuns blueprint (instantiate all `lazy=False` bindings).\n\n### `@component(cls=None, *, name=None, lazy=False)`\n\nRegister a class as a component.\nUse `name` for a custom key.\nSet `lazy=True` to defer creation.\n\n### `@factory_component`\n\nMark a class as a component factory (its methods can `@provides` bindings).\n\n### `@provides(key, *, lazy=False)`\n\nDeclare that a factory method provides a component under `key`.\nSet `lazy=True` for deferred creation (`ComponentProxy`).\n\n---\n\n## \ud83e\uddea Testing\n\n```bash\npip install tox\ntox -e py311\n```\n\nTip: for \u201cmissing dependency\u201d tests, mark those components as `lazy=True` so `init()` remains fail-fast for real components while your test still asserts failure on resolution.\n\n---\n\n## \ud83d\udd0c Extensibility: Plugins, Binder, and Lifecycle Hooks\n\nFrom `v0.4.0` onward, Pico-IoC can be cleanly extended without patching the core.\nThis enables optional integration layers like `pico-ioc-rest` for Flask, FastAPI, etc., while keeping the core dependency-free.\n\n### Plugin Protocol\n\nA plugin is any object that implements some or all of the following methods:\n\n```python\nfrom pico_ioc import PicoPlugin, Binder\n\nclass MyPlugin:\n    def before_scan(self, package, binder: Binder): ...\n    def visit_class(self, module, cls, binder: Binder): ...\n    def after_scan(self, package, binder: Binder): ...\n    def after_bind(self, container, binder: Binder): ...\n    def before_eager(self, container, binder: Binder): ...\n    def after_ready(self, container, binder: Binder): ...\n```\n\nAll hooks are optional. If present, they are called in this order during `init()`:\n\n1. **before\\_scan** \u2014 called before package scanning starts.\n2. **visit\\_class** \u2014 called for every class discovered during scanning.\n3. **after\\_scan** \u2014 called after scanning all modules.\n4. **after\\_bind** \u2014 called after the core has bound all components/factories.\n5. **before\\_eager** \u2014 called right before eager (non-lazy) instantiation.\n6. **after\\_ready** \u2014 called after all eager instantiation is complete.\n\n### Binder API\n\nPlugins receive a [`Binder`](#binder-api) instance in each hook, allowing them to:\n\n* **bind**: register new providers in the container.\n* **has**: check if a binding exists.\n* **get**: resolve a binding immediately.\n\nExample plugin that binds a \u201cmarker\u201d component when a certain class is discovered:\n\n```python\nclass MarkerPlugin:\n    def visit_class(self, module, cls, binder):\n        if cls.__name__ == \"SpecialService\" and not binder.has(\"marker\"):\n            binder.bind(\"marker\", lambda: {\"ok\": True}, lazy=False)\n\ncontainer = init(\"my_app\", plugins=(MarkerPlugin(),))\nassert container.get(\"marker\") == {\"ok\": True}\n```\n\n### Creating Extensions\n\nWith the plugin API, you can build separate packages like `pico-ioc-rest`:\n\n```python\nfrom pico_ioc import PicoPlugin, Binder, create_instance, resolve_param\nfrom flask import Flask\n\nclass FlaskRestPlugin:\n    def __init__(self):\n        self.controllers = []\n\n    def visit_class(self, module, cls, binder: Binder):\n        if getattr(cls, \"_is_controller\", False):\n            self.controllers.append(cls)\n\n    def after_bind(self, container, binder: Binder):\n        app: Flask = container.get(Flask)\n        for ctl_cls in self.controllers:\n            ctl = create_instance(ctl_cls, container)\n            # register routes here using `resolve_param` for handler DI\n```\n\n### Public Helpers for Extensions\n\nPlugins can reuse Pico-IoC\u2019s DI logic without duplicating it:\n\n* **`create_instance(cls, container)`** \u2014 instantiate a class with DI, respecting Pico-IoC\u2019s resolution order.\n* **`resolve_param(container, parameter)`** \u2014 resolve a single function/class parameter via Pico-IoC rules.\n\n---\n\n## \u2753 FAQ\n\n**Q: Can I make the container lenient at startup?**\nA: By design it\u2019s strict. Prefer `lazy=True` on specific bindings or exclude problem modules from the scan.\n\n**Q: Thread safety?**\nA: Container uses `ContextVar` to guard re-entrancy during scanning. Singletons are created once per container; typical usage is in single-threaded app startup, then read-mostly.\n\n**Q: Frameworks?**\nA: Framework-agnostic. Works with Flask, FastAPI, CLIs, scripts, etc.\n\n---\n\n## \ud83d\udcdc License\n\nMIT \u2014 see [LICENSE](https://opensource.org/licenses/MIT)\n\n\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "A minimalist, zero-dependency Inversion of Control (IoC) container for Python.",
    "version": "0.4.0",
    "project_urls": {
        "Homepage": "https://github.com/dperezcabrera/pico-ioc",
        "Issue Tracker": "https://github.com/dperezcabrera/pico-ioc/issues",
        "Repository": "https://github.com/dperezcabrera/pico-ioc"
    },
    "split_keywords": [
        "ioc",
        " di",
        " dependency injection",
        " inversion of control",
        " decorator"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ed939f684f9d1abc3f6b330869af9293e6e34ac942722018e3cbec1a4543fe3a",
                "md5": "1441b048dd4e354d29b3ac4560cd106a",
                "sha256": "9c8f821cb4ecec6c97e68b7428946de9a159a5da6e475ae4b065f9c3791ecada"
            },
            "downloads": -1,
            "filename": "pico_ioc-0.4.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "1441b048dd4e354d29b3ac4560cd106a",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 8295,
            "upload_time": "2025-08-10T14:44:49",
            "upload_time_iso_8601": "2025-08-10T14:44:49.409976Z",
            "url": "https://files.pythonhosted.org/packages/ed/93/9f684f9d1abc3f6b330869af9293e6e34ac942722018e3cbec1a4543fe3a/pico_ioc-0.4.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "f7a7cf3ea9c7b4cc2ab37d9dbf13406e23f3f3ff813eff438dbdee309ba7c08a",
                "md5": "4d0aff491ebed386ee217407693b05b8",
                "sha256": "5515257aa42bb27ffe650c9bb86e8562303a4983fda8264d2e64c04e8d405926"
            },
            "downloads": -1,
            "filename": "pico_ioc-0.4.0.tar.gz",
            "has_sig": false,
            "md5_digest": "4d0aff491ebed386ee217407693b05b8",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 14941,
            "upload_time": "2025-08-10T14:44:50",
            "upload_time_iso_8601": "2025-08-10T14:44:50.717916Z",
            "url": "https://files.pythonhosted.org/packages/f7/a7/cf3ea9c7b4cc2ab37d9dbf13406e23f3f3ff813eff438dbdee309ba7c08a/pico_ioc-0.4.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-10 14:44:50",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "dperezcabrera",
    "github_project": "pico-ioc",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "pico-ioc"
}
        
Elapsed time: 0.71383s