Metadata-Version: 2.5
Name: mqttium
Version: 1.0.0rc12
Summary: Reliable async-native MQTT client for Python
Project-URL: Homepage, https://github.com/yoch/mqttium
Project-URL: Documentation, https://mqttium.readthedocs.io
Project-URL: Repository, https://github.com/yoch/mqttium
Project-URL: Issues, https://github.com/yoch/mqttium/issues
Project-URL: Discussions, https://github.com/yoch/mqttium/discussions
Project-URL: Changelog, https://github.com/yoch/mqttium/blob/main/CHANGELOG.md
Author-email: Yoch Melka <795960+yoch@users.noreply.github.com>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: asyncio,backpressure,iot,messaging,mqtt,mqtt-client,mqtt311,mqtt5,persistence,websocket
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Communications
Classifier: Topic :: Internet
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Provides-Extra: benchmark
Requires-Dist: psutil>=6; extra == 'benchmark'
Provides-Extra: dev
Requires-Dist: actionlint-py>=1.7.12.24; extra == 'dev'
Requires-Dist: mypy>=1.17; extra == 'dev'
Requires-Dist: py-spy>=0.4.2; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest-cov>=6; extra == 'dev'
Requires-Dist: pytest-timeout>=2.4; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.16; extra == 'dev'
Requires-Dist: shellcheck-py>=0.11.0.1; extra == 'dev'
Requires-Dist: zizmor>=1.29; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-llmstxt<1,>=0.5; extra == 'docs'
Requires-Dist: mkdocs-material<10,>=9.6; extra == 'docs'
Requires-Dist: mkdocs-redirects<2,>=1.2; extra == 'docs'
Requires-Dist: mkdocs<2,>=1.6; extra == 'docs'
Requires-Dist: mkdocstrings[python]<2,>=1; extra == 'docs'
Provides-Extra: fuzz
Requires-Dist: hypothesis>=6.100; extra == 'fuzz'
Requires-Dist: pytest-asyncio>=0.24; extra == 'fuzz'
Requires-Dist: pytest-timeout>=2.4; extra == 'fuzz'
Requires-Dist: pytest>=8; extra == 'fuzz'
Provides-Extra: release
Requires-Dist: build>=1.2; extra == 'release'
Requires-Dist: check-wheel-contents>=0.6; extra == 'release'
Requires-Dist: twine>=6; extra == 'release'
Requires-Dist: validate-pyproject[all]>=0.24; extra == 'release'
Provides-Extra: security
Requires-Dist: bandit>=1.8; extra == 'security'
Description-Content-Type: text/markdown

<p align="center">
  <img src="https://raw.githubusercontent.com/yoch/mqttium/main/docs/assets/mqttium-logo-900.png" alt="MQTTium logo" width="180">
</p>

<h1 align="center">MQTTium</h1>

<p align="center"><strong>A dependable, dependency-free asyncio MQTT client for Python.</strong></p>

<p align="center">
  <a href="https://pypi.org/project/mqttium/"><img alt="PyPI" src="https://img.shields.io/pypi/v/mqttium.svg"></a>
  <a href="https://pypi.org/project/mqttium/"><img alt="Python versions" src="https://img.shields.io/pypi/pyversions/mqttium.svg"></a>
  <a href="https://github.com/yoch/mqttium/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/yoch/mqttium/actions/workflows/ci.yml/badge.svg"></a>
  <a href="https://codecov.io/gh/yoch/mqttium"><img alt="Coverage" src="https://codecov.io/gh/yoch/mqttium/branch/main/graph/badge.svg"></a>
  <a href="https://mqttium.readthedocs.io/en/stable/"><img alt="Documentation" src="https://readthedocs.org/projects/mqttium/badge/?version=stable"></a>
  <a href="https://github.com/yoch/mqttium/blob/main/LICENSE"><img alt="Apache-2.0 license" src="https://img.shields.io/pypi/l/mqttium.svg"></a>
</p>

MQTTium is an async-native MQTT 3.1.1 and MQTT 5 client for Python 3.11–3.14.
It is designed for services, gateways, and connected devices that need explicit
delivery semantics, bounded resource use, and predictable recovery when a
connection or process fails.

The package has no runtime dependencies and is fully typed.

## Why MQTTium?

| Need | MQTTium provides |
| --- | --- |
| Protocol coverage | MQTT 3.1.1 and MQTT 5, QoS 0/1/2, typed properties, Last Will, and enhanced authentication |
| Explicit completion | Publish receipts that separate local admission from the relevant MQTT acknowledgement exchange |
| Controlled load | Message and byte budgets, wait-or-refuse backpressure, bounded ingress, writes, and application delivery |
| Session continuity | Jittered reconnect plus in-memory or SQLite-backed inflight state with incremental replay |
| Delivery choices | Async iteration, sync or async callbacks, optional dual delivery, and manual acknowledgement |
| Transports | TCP, TLS, WebSocket, and Unix-domain sockets |
| Operations | Immutable runtime snapshots, queue high-water marks, and broker-negotiated limits |
| Efficient production | Bounded `publish_many()` and loop-bound `publish_nowait()` without changing delivery semantics |

MQTTium keeps protocol state in a synchronous state machine and leaves sockets,
timers, callbacks, and task ownership to the asyncio adapter. That separation
makes QoS transitions and rollback independently testable while keeping the
native client free of background threads.

## Install

```bash
python -m pip install mqttium
```

## First round trip

The example subscribes, publishes at QoS 1, waits for PUBACK, and consumes the
message:

```python
import asyncio

from mqttium.api import AsyncClient


async def main() -> None:
    client = AsyncClient("example-client")
    try:
        await client.connect("127.0.0.1", 1883)
        await client.subscribe("devices/+/status", qos=1)

        receipt = await client.publish(
            "devices/demo/status",
            b"online",
            qos=1,
        )
        await receipt.wait()

        async for message in client.messages():
            print(message.topic, message.payload)
            break
    finally:
        await client.disconnect()


asyncio.run(main())
```

For QoS 0, a receipt completes after writer admission because MQTT provides no
broker acknowledgement. QoS 1 completes on PUBACK; QoS 2 completes on PUBCOMP.
Waiting for `publish()` and waiting for `receipt.wait()` therefore answer
different questions.

## Backpressure is part of the API

`publish()` waits for capacity by default. Applications that have a defined
shed, retry, or spill policy can request immediate refusal:

```python
from mqttium import FlowControlError
from mqttium.api import AsyncClient

client = AsyncClient(publish_backpressure="error")

try:
    receipt = await client.publish("telemetry", payload, qos=1)
except FlowControlError:
    await shed_or_retry(payload)
```

Outbound protocol state, encoded writes, inbound protocol state, and delivery
queues have independent bounds because they have different lifetimes. Passing
`None` disables an optional bound and should be a deliberate capacity decision.

For a sustained producer, `publish_many()` consumes an iterable in bounded
chunks and returns one aggregate receipt:

```python
from mqttium.api import PublishMessage

batch = await client.publish_many(
    PublishMessage("telemetry", sample, qos=1) for sample in samples
)
await batch.wait()
```

## Reconnect and durable sessions

Automatic reconnect is opt-in through `ReconnectPolicy`. Durable recovery also
requires a durable broker session; storing client-side inflight state alone is
not sufficient.

```python
from mqttium import MQTTProtocolVersion
from mqttium.api import AsyncClient, Properties, ReconnectPolicy
from mqttium.persistence import SqliteInflightStore

store = SqliteInflightStore("mqtt-session.sqlite")
client = AsyncClient(
    "gateway",
    protocol=MQTTProtocolVersion.MQTTv5,
    clean_start=False,
    connect_properties=Properties({"session_expiry_interval": 86_400}),
    reconnect=ReconnectPolicy(max_retries=None),
    store=store,
)
```

`SqliteInflightStore` persists unfinished outbound QoS 1/2 exchanges and
inbound QoS 2 protocol state. It does not persist arbitrary application work,
delivered callback/iterator queues, or subscription intent. The application
owns the store and must close it after the client has shut down.

## Paho migration

New async applications should use `AsyncClient`. MQTTium also ships a
**Provisional**, Paho-shaped `CallbackAPIVersion.VERSION2` facade for existing
synchronous applications that need an incremental migration path. It is tested
and bounded, but it is not a drop-in promise, a performance-parity promise, or
a second native API. See [Migrating from Paho](https://mqttium.readthedocs.io/en/stable/migration/)
and the [exact compatibility matrix](https://mqttium.readthedocs.io/en/stable/paho-compatibility/).

## Documentation

The complete documentation is available on
[Read the Docs](https://mqttium.readthedocs.io/en/stable/).

| Start here | Use it for |
| --- | --- |
| [Getting started](https://mqttium.readthedocs.io/en/stable/getting-started/) | Installation, lifecycle, publishing, subscribing, and delivery |
| [Configuration and sizing](https://mqttium.readthedocs.io/en/stable/configuration-and-sizing/) | Choosing queue, byte, inflight, timeout, and reconnect settings |
| [Sessions and persistence](https://mqttium.readthedocs.io/en/stable/sessions-and-persistence/) | Broker sessions, reconnect, SQLite, and restart recovery |
| [Transports and security](https://mqttium.readthedocs.io/en/stable/transports-and-tls/) | TCP, TLS, WebSocket, Unix sockets, and credential handling |
| [MQTT 5](https://mqttium.readthedocs.io/en/stable/mqtt-5/) | Properties, authentication, topic aliases, and negotiated limits |
| [Operations](https://mqttium.readthedocs.io/en/stable/operations/) | Runtime snapshots, pressure diagnosis, and graceful shutdown |
| [Stable API reference](https://mqttium.readthedocs.io/en/stable/reference/) | Supported imports, signatures, defaults, and exceptions |
| [Compatibility matrix](https://mqttium.readthedocs.io/en/stable/compatibility/) | Python, platform, broker, protocol, and transport validation |

Architecture, conformance, stability tiers, benchmarking methodology, and
release evidence are documented separately so current contracts are not mixed
with historical reports.

## Performance claims

Performance is treated as an evidence discipline, not a slogan. Changes must
preserve MQTT semantics, bounded memory, backpressure, and event-loop fairness.
The [benchmarking contract](https://mqttium.readthedocs.io/en/stable/benchmarking/)
defines valid comparisons.
Cross-client results will be linked only after the independent benchmark
repository publishes reviewed MQTTium, Paho, and gmqtt runs with exact versions,
environment details, raw artifacts, comparable completion semantics, and stated
limitations.

## Support and contributing

- Read the [support policy](https://github.com/yoch/mqttium/blob/main/SUPPORT.md) before requesting usage help.
- Use the structured issue form for reproducible bugs.
- Report vulnerabilities privately as described in the [security policy](https://github.com/yoch/mqttium/blob/main/SECURITY.md).
- See the [contribution guide](https://github.com/yoch/mqttium/blob/main/CONTRIBUTING.md) for development and validation commands.

MQTTium is original software licensed under [Apache-2.0](https://github.com/yoch/mqttium/blob/main/LICENSE). Paho and
gmqtt are referenced only for migration, interoperability, and independent
comparison.
