Metadata-Version: 2.4
Name: asyncmy
Version: 0.2.12
Summary: The fastest asyncio MySQL/MariaDB driver for Python
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: driver,asyncio,mysql
Author: long2ice
Author-email: long2ice@gmail.com
Requires-Python: >=3.9
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Project-URL: Documentation, https://github.com/long2ice/asyncmy
Project-URL: Homepage, https://github.com/long2ice/asyncmy
Project-URL: Repository, https://github.com/long2ice/asyncmy.git
Description-Content-Type: text/markdown

# asyncmy — The fastest asyncio MySQL/MariaDB driver

[![PyPI](https://img.shields.io/pypi/v/asyncmy.svg)](https://pypi.org/pypi/asyncmy)
[![License](https://img.shields.io/github/license/long2ice/asyncmy)](https://github.com/long2ice/asyncmy)
[![CI](https://github.com/long2ice/asyncmy/actions/workflows/ci.yml/badge.svg)](https://github.com/long2ice/asyncmy/actions/workflows/ci.yml)
[![Release](https://github.com/long2ice/asyncmy/actions/workflows/pypi.yml/badge.svg)](https://github.com/long2ice/asyncmy/actions/workflows/pypi.yml)

`asyncmy` is the fastest asyncio MySQL/MariaDB driver for Python. It keeps the familiar [aiomysql](https://github.com/aio-libs/aiomysql) API while rewriting the entire protocol core in [Cython](https://cython.org/) — down to pointer-level packet parsing. In [our benchmarks](./benchmark/README.md) it outperforms every driver tested, including the C-based synchronous `mysqlclient`.

## Features

- 🚀 **Fastest in every benchmark** — reads large result sets 2.1x faster than `mysqlclient` and 5x faster than `aiomysql`/`pymysql` ([details](./benchmark/README.md))
- 🔌 **Drop-in aiomysql replacement** — same API, same cursors (`DictCursor`, `SSCursor`), same pool semantics
- ⚡ **C-speed protocol core** — rows are parsed in bulk from the receive buffer in a single C loop, values decode straight from wire bytes via the CPython C-API
- 🏊 **Built-in connection pool** — `asyncmy.create_pool()`, no extra dependency, 2x aiomysql's pooled throughput
- 📡 **MySQL replication protocol** over asyncio ([BinLogStream](https://github.com/long2ice/asyncmy/blob/dev/asyncmy/replication/binlogstream.py))
- ✅ **CI-tested on MySQL and MariaDB** ([workflow](https://github.com/long2ice/asyncmy/blob/dev/.github/workflows/ci.yml))

## Benchmark

asyncmy ranks **#1 in all four scenarios** against `mysqlclient`, `pymysql`, and `aiomysql` (warmup + best-of-3, see [methodology](./benchmark/README.md#methodology)):

| Test | asyncmy Rank | Performance |
| ---- | ------------ | ----------- |
| **Large Result Set** (33k rows, all types) | 🏆 **#1/4** | 0.031s — 2.1x faster than mysqlclient, 5.2x faster than aiomysql |
| **Connection Pool** (2k queries) | 🏆 **#1/2** | ~17,000 qps — 2x aiomysql's throughput |
| **Concurrent Queries** (50 connections) | 🏆 **#1/2** | ~8,600 qps — 1.6x faster than aiomysql |
| **Batch Insert** (10k rows) | 🏆 **#1/4** | ~91,000 rows/sec — fastest of all four drivers |

The protocol core is engineered for zero waste on the hot path:

- **Bulk packet parsing**: one socket read serves hundreds of row packets, parsed in a single C loop with no event-loop round-trips
- **Pointer-based protocol reads**: integers and length-encoded values are read directly from raw memory, no `struct` calls
- **Direct row decoding**: cell values decode straight from the receive buffer via the CPython C-API (`PyUnicode_DecodeUTF8`, `PyTuple_New`), skipping intermediate objects
- **Zero-decode numeric/temporal columns**: `int`/`float`/`datetime` values parse directly from bytes, and dates are built with the C datetime API
- **Escape fast path**: strings without special characters are returned as-is, no translation pass

📊 **[View detailed benchmarks →](./benchmark/README.md)**

## Install

**Requirements:** Python ≥ 3.9

```bash
pip install asyncmy
```

### Windows

asyncmy uses Cython extensions; on Windows you need **Microsoft C++ Build Tools** to build them.

1. Download [Microsoft C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/).
2. Open CMD as Administrator (recommended) and `cd` to the folder **where** the installer was downloaded.
3. Rename the installer (e.g. `vs_buildtools__XXXXXXXXX.XXXXXXXXXX.exe`) to `vs_buildtools.exe` for convenience.
4. Run (ensure ~5–6GB free disk space):

   ```bash
   vs_buildtools.exe --norestart --passive --downloadThenInstall --includeRecommended --add Microsoft.VisualStudio.Workload.NativeDesktop --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Workload.MSBuildTools
   ```

5. Wait for installation to complete, then restart your computer.
6. Install asyncmy:

   ```bash
   pip install asyncmy
   ```

You can uninstall the Build Tools afterward if desired.

## Usage

### `connect`

Use `asyncmy.connect()` for a single connection. For many concurrent connections, use a [connection pool](#pool).

```py
import asyncio
import os

from asyncmy import connect
from asyncmy.cursors import DictCursor


async def main():
    conn = await connect(
        user=os.getenv("DB_USER"),
        password=os.getenv("DB_PASSWORD", ""),
    )
    async with conn.cursor(cursor=DictCursor) as cursor:
        await cursor.execute("CREATE DATABASE IF NOT EXISTS test")
        await cursor.execute("""
            CREATE TABLE IF NOT EXISTS test.`asyncmy` (
                `id`       int PRIMARY KEY AUTO_INCREMENT,
                `decimal`  decimal(10, 2),
                `date`     date,
                `datetime` datetime,
                `float`    float,
                `string`   varchar(200),
                `tinyint`  tinyint
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
        """.strip())
    await conn.ensure_closed()


if __name__ == "__main__":
    asyncio.run(main())
```

### Pool

For multiple connections, use a connection pool. Pass the same kwargs as `connect()` (e.g. `host`, `user`, `password`).

```py
import asyncio
import asyncmy


async def main():
    pool = await asyncmy.create_pool(host="localhost", user="root", password="")
    async with pool.acquire() as conn:
        async with conn.cursor() as cursor:
            await cursor.execute("SELECT 1")
            ret = await cursor.fetchone()
            assert ret == (1,)
    pool.close()
    await pool.wait_closed()


if __name__ == "__main__":
    asyncio.run(main())
```

## Replication

asyncmy supports the MySQL replication protocol (like [python-mysql-replication](https://github.com/noplay/python-mysql-replication)) over asyncio.

```py
import asyncio

from asyncmy import connect
from asyncmy.replication import BinLogStream


async def main():
    conn = await connect()
    ctl_conn = await connect()

    stream = BinLogStream(
        conn,
        ctl_conn,
        server_id=1,
        master_log_file="binlog.000172",
        master_log_position=2235312,
        resume_stream=True,
        blocking=True,
    )
    async for event in stream:
        print(event)
    await conn.ensure_closed()
    await ctl_conn.ensure_closed()


if __name__ == "__main__":
    asyncio.run(main())
```

## Acknowledgments

asyncmy builds on these projects:

- [PyMySQL](https://github.com/PyMySQL/PyMySQL) — pure Python MySQL client
- [aiomysql](https://github.com/aio-libs/aiomysql) — asyncio MySQL driver
- [python-mysql-replication](https://github.com/noplay/python-mysql-replication) — MySQL replication protocol (pure Python, on top of PyMySQL)

## License

[Apache-2.0](./LICENSE)

