pyro-mysql


Namepyro-mysql JSON
Version 0.1.7 PyPI version JSON
download
home_pageNone
SummaryHigh-performance MySQL driver for Python
upload_time2025-10-27 02:27:39
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseMIT
keywords mysql async asyncio database rust pyo3
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # pyro-mysql

A high-performance MySQL driver for Python, backed by Rust.

- [API Overview](#api-overview)
- [Usage](#usage)
- [DataType Mapping](#datatype-mapping)
- [Logging](#logging)
- [PEP-249, sqlalchemy](#pep-249-sqlalchemy)
- [Benchmark](https://htmlpreview.github.io/?https://github.com/elbaro/pyro-mysql/blob/main/report/report/index.html)

<img src="https://github.com/elbaro/pyro-mysql/blob/main/report/chart.png?raw=true" width="800px" />


## Usage


### 0. Import

```py
# Async
from pyro_mysql.async_ import Conn, Pool
from pyro_mysql import AsyncConn, AsyncPool

# Sync
from pyro_mysql.sync import Conn, Transaction
from pyro_mysql import SyncConn, SyncTransaction
````

### 1. Connection


```py
from pyro_mysql.async_ import Conn, Pool, OptsBuilder


# Optionally configure the number of Rust threads
# pyro_mysql.init(worker_threads=1)

def example1():
    conn = await Conn.new(f"mysql://{USER}:{PASSWORD}@{HOST}:{PORT}/{DATABASE}")

def example2():
    pool = Pool(
        OptsBuilder()
            .ip_or_hostname("localhost")
            .port(3333)
            .user("username")
            .db_name("db")
            .wait_timeout(100)
            .tcp_nodelay(True)
            .compression(3)
            .build()
    )
    conn = await pool.get()

def example3(pool):
    with pool.get() as conn:
        ...
```


### 2. Query Execution

`AsyncConn` and `AsyncTransaction` provide the following methods.
`SyncConn`, `SyncPooledConn` and `SyncTransaction` provide similar API.

```py

# Text Protocols - supports multiple statements concatenated with ';' but accepts no arguemnt
def query(self, query: str) -> Awaitable[list[Row]]: ...
def query_first(self, query: str) -> Awaitable[Row | None]: ...
def query_drop(self, query: str) -> Awaitable[None]: ...
def query_batch(self, query: str) -> Awaitable[None]: ...

# Binary Protocols - supports arguments but no multiple statement
def exec(self, query: str, params: Params) -> Awaitable[list[Row]]: ...
def exec_first(self, query: str, params: Params) -> Awaitable[Row | None]: ...
def exec_drop(self, query: str, params: Params) -> Awaitable[None]: ...
def exec_batch(self, query: str, params: Iterable[Params]) -> Awaitable[None]: ...

# Examples
rows = await conn.exec("SELECT * FROM my_table WHERE a=? AND b=?", (a, b))
rows = await conn.exec("SELECT * FROM my_table WHERE a=:x AND b=:y AND c=:y", {'x': 100, 'y': 200})
await conn.exec_batch("SELECT * FROM my_table WHERE a=? AND b=?", [(a1, b1), (a2, b2)])
```

`Awaitable` is a coroutine or `PyroFuture`, which is a Future-like object that tracks a task in the Rust thread. If the returned object is dropped before completion or cancellation, the corresponding task in the Rust thread is cancelled as well.

### 3. Transaction

```py
# async API
async with conn.start_transaction() as tx:
    await tx.exec('INSERT ..')
    await tx.exec('INSERT ..')
    await tx.commit()
    await conn.exec(..)  # this is not allowed

# sync API
with conn.start_transaction() as tx:
    tx.exec('INSERT ..')
    tx.exec('INSERT ..')
    conn.exec('INSERT ..')  # this is not allowed
    tx.rollback()
```

## DataType Mapping

### Python -> MySQL

| Python Type | MySQL Binary Protocol Encoding |
|-------------|------------|
| `None` | `NULL` |
| `bool` | `Int64` |
| `int` | `Int64` |
| `float` | `Double(Float64)` |
| `str \| bytes \| bytearray` | `Bytes` |
| `tuple \| list \| set \| frozenset \| dict` | json-encoded string as `Bytes` |
| `datetime.datetime` | `Date(year, month, day, hour, minute, second, microsecond)` |
| `datetime.date` | `Date(year, month, day, 0, 0, 0, 0)` |
| `datetime.time` | `Time(false, 0, hour, minute, second, microsecond)` |
| `datetime.timedelta` | `Time(is_negative, days, hours, minutes, seconds, microseconds)` |
| `time.struct_time` | `Date(year, month, day, hour, minute, second, 0)` |
| `decimal.Decimal` | `Bytes(str(Decimal))` |
| `uuid.UUID` | `Bytes(UUID.hex)` |

### MySQL -> Python

| MySQL Column | Python |
|-------------|------------|
| `NULL` | `None` |
| `INT` / `TINYINT` / `SMALLINT` / `MEDIUMINT` / `BIGINT` / `YEAR` | `int` |
| `FLOAT` | `float` |
| `DOUBLE` | `float` |
| `DECIMAL` / `NUMERIC` | `decimal.Decimal` |
| `DATE` | `datetime.date` |
| `TIME` | `datetime.timedelta` |
| `DATETIME` | `datetime.datetime` |
| `TIMESTAMP` | `datetime.datetime` |
| `CHAR` / `VARCHAR` / `TEXT` / `TINYTEXT` / `MEDIUMTEXT` / `LONGTEXT` | `str` |
| `BINARY` / `VARBINARY` / `BLOB` / `TINYBLOB` / `MEDIUMBLOB` / `LONGBLOB` | `bytes` |
| `JSON` | the result of json.loads() |
| `ENUM` / `SET` | `str` |
| `BIT` | `bytes` |
| `GEOMETRY` | `bytes` (WKB format) |

## Logging

pyro-mysql sends the Rust logs to the Python logging system, which can be configured with `logging.getLogger("pyro_mysql")`.

```py
# Queries are logged with the DEBUG level
logging.getLogger("pyro_mysql").setLevel(logging.DEBUG)
```

## PEP-249, sqlalchemy

<img src="https://github.com/elbaro/pyro-mysql/blob/main/report/chart_sqlalchemy.png?raw=true" width="800px" />

`pyro_mysql.dbapi` implements PEP-249.
This only exists for compatibility with ORM libraries.
The primary API set (`pyro_mysql.sync`, `pyro_mysql.async_`) is simpler and faster.

```sh
pyro_mysql.dbapi
    # classes
    ├─Connection
    ├─Cursor
    # exceptions
    ├─Warning
    ├─Error
    ├─IntegrityError
    ├─..
```

In sqlalchemy, the following dialects are supported.
- `mysql+pyro_mysql://` (sync)
- `mariadb+pyro_mysql://` (sync)
- `mysql+pyro_mysql_async://` (async)
- `mariadb+pyro_mysql_async://` (async)

The supported connection parameters are [the docs](https://docs.rs/mysql/latest/mysql/struct.OptsBuilder.html#method.from_hash_map) and [`capabilities`](https://docs.rs/mysql/latest/mysql/consts/struct.CapabilityFlags.html) (default 2).

```py
from sqlalchemy import create_engine, text

engine = create_engine("mysql+pyro_mysql://test:1234@localhost/test")
conn = engine.connect()
cursor_result = conn.execute(text("SHOW TABLES"))
for row in cursor_result:
    print(row)
```

```
('information_schema',)
('mysql',)
('performance_schema',)
('sys',)
('test',)
```

To run sqlalchemy tests on pyro_mysql, use this command in the sqlalchemy repo:

```sh
pytest -p pyro_mysql.testing.sqlalchemy_pytest_plugin --dburi=mariadb+pyro_mysql://test:1234@localhost/test -v t
```

`sqlalchemy_pytest_plugin` is required to skip incompatible tests.


## API Overview

- [pyro_mysql](https://github.com/elbaro/pyro-mysql/blob/main/pyro_mysql/__init__.pyi)
- [pyro_mysql,sync](https://github.com/elbaro/pyro-mysql/blob/main/pyro_mysql/sync.pyi)
- [pyro_mysql.async_](https://github.com/elbaro/pyro-mysql/blob/main/pyro_mysql/async_.pyi)
- [pyro_mysql.dbapi](https://github.com/elbaro/pyro-mysql/blob/main/pyro_mysql/dbapi.pyi)
- [pyro_mysql.dbapi_async](https://github.com/elbaro/pyro-mysql/blob/main/pyro_mysql/dbapi_async.pyi)
- [pyro_mysql.error](https://github.com/elbaro/pyro-mysql/blob/main/pyro_mysql/error.pyi)

```
.
└── pyro_mysql/
    ├── init()
    ├── (common classes)/
    │   ├── Row
    │   ├── IsolationLevel
    │   ├── CapabilityFlags
    │   └── PyroFuture
    ├── sync/
    │   ├── Conn
    │   ├── Transaction
    │   ├── Pool
    │   ├── Opts
    │   ├── OptsBuilder
    │   └── PoolOpts
    ├── async_/
    │   ├── Conn
    │   ├── Transaction
    │   ├── Pool
    │   ├── Opts
    │   ├── OptsBuilder
    │   └── PoolOpts
    ├── dbapi/
    │   ├── connect()
    │   ├── Connection
    │   ├── Cursor
    │   └── (exceptions)/
    │       ├── Warning
    │       ├── Error
    │       ├── InterfaceError
    │       ├── DatabaseError
    │       ├── DataError
    │       ├── OperationalError
    │       ├── IntegrityError
    │       ├── InternalError
    │       ├── ProgrammingError
    │       └── NotSupportedError
    ├── dbapi_async/
    │   ├── connect()
    │   ├── Connection
    │   ├── Cursor
    │   └── (exceptions)/
    │       ├── Warning
    │       ├── Error
    │       ├── InterfaceError
    │       ├── DatabaseError
    │       ├── DataError
    │       ├── OperationalError
    │       ├── IntegrityError
    │       ├── InternalError
    │       ├── ProgrammingError
    │       └── NotSupportedError
    └── (aliases)/
        ├── SyncConn
        ├── SyncTransaction
        ├── SyncPool
        ├── SyncOpts
        ├── SyncOptsBuilder
        ├── SyncPoolOpts
        ├── AsyncConn
        ├── AsyncTransaction
        ├── AsyncPool
        ├── AsyncOpts
        ├── AsyncOptsBuilder
        └── AsyncPoolOpts
```


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "pyro-mysql",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "mysql, async, asyncio, database, rust, pyo3",
    "author": null,
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/b5/5c/5c39df89daa5f5b8d111cab02c693c3599789428f840066890cfe358dc50/pyro_mysql-0.1.7.tar.gz",
    "platform": null,
    "description": "# pyro-mysql\n\nA high-performance MySQL driver for Python, backed by Rust.\n\n- [API Overview](#api-overview)\n- [Usage](#usage)\n- [DataType Mapping](#datatype-mapping)\n- [Logging](#logging)\n- [PEP-249, sqlalchemy](#pep-249-sqlalchemy)\n- [Benchmark](https://htmlpreview.github.io/?https://github.com/elbaro/pyro-mysql/blob/main/report/report/index.html)\n\n<img src=\"https://github.com/elbaro/pyro-mysql/blob/main/report/chart.png?raw=true\" width=\"800px\" />\n\n\n## Usage\n\n\n### 0. Import\n\n```py\n# Async\nfrom pyro_mysql.async_ import Conn, Pool\nfrom pyro_mysql import AsyncConn, AsyncPool\n\n# Sync\nfrom pyro_mysql.sync import Conn, Transaction\nfrom pyro_mysql import SyncConn, SyncTransaction\n````\n\n### 1. Connection\n\n\n```py\nfrom pyro_mysql.async_ import Conn, Pool, OptsBuilder\n\n\n# Optionally configure the number of Rust threads\n# pyro_mysql.init(worker_threads=1)\n\ndef example1():\n    conn = await Conn.new(f\"mysql://{USER}:{PASSWORD}@{HOST}:{PORT}/{DATABASE}\")\n\ndef example2():\n    pool = Pool(\n        OptsBuilder()\n            .ip_or_hostname(\"localhost\")\n            .port(3333)\n            .user(\"username\")\n            .db_name(\"db\")\n            .wait_timeout(100)\n            .tcp_nodelay(True)\n            .compression(3)\n            .build()\n    )\n    conn = await pool.get()\n\ndef example3(pool):\n    with pool.get() as conn:\n        ...\n```\n\n\n### 2. Query Execution\n\n`AsyncConn` and `AsyncTransaction` provide the following methods.\n`SyncConn`, `SyncPooledConn` and `SyncTransaction` provide similar API.\n\n```py\n\n# Text Protocols - supports multiple statements concatenated with ';' but accepts no arguemnt\ndef query(self, query: str) -> Awaitable[list[Row]]: ...\ndef query_first(self, query: str) -> Awaitable[Row | None]: ...\ndef query_drop(self, query: str) -> Awaitable[None]: ...\ndef query_batch(self, query: str) -> Awaitable[None]: ...\n\n# Binary Protocols - supports arguments but no multiple statement\ndef exec(self, query: str, params: Params) -> Awaitable[list[Row]]: ...\ndef exec_first(self, query: str, params: Params) -> Awaitable[Row | None]: ...\ndef exec_drop(self, query: str, params: Params) -> Awaitable[None]: ...\ndef exec_batch(self, query: str, params: Iterable[Params]) -> Awaitable[None]: ...\n\n# Examples\nrows = await conn.exec(\"SELECT * FROM my_table WHERE a=? AND b=?\", (a, b))\nrows = await conn.exec(\"SELECT * FROM my_table WHERE a=:x AND b=:y AND c=:y\", {'x': 100, 'y': 200})\nawait conn.exec_batch(\"SELECT * FROM my_table WHERE a=? AND b=?\", [(a1, b1), (a2, b2)])\n```\n\n`Awaitable` is a coroutine or `PyroFuture`, which is a Future-like object that tracks a task in the Rust thread. If the returned object is dropped before completion or cancellation, the corresponding task in the Rust thread is cancelled as well.\n\n### 3. Transaction\n\n```py\n# async API\nasync with conn.start_transaction() as tx:\n    await tx.exec('INSERT ..')\n    await tx.exec('INSERT ..')\n    await tx.commit()\n    await conn.exec(..)  # this is not allowed\n\n# sync API\nwith conn.start_transaction() as tx:\n    tx.exec('INSERT ..')\n    tx.exec('INSERT ..')\n    conn.exec('INSERT ..')  # this is not allowed\n    tx.rollback()\n```\n\n## DataType Mapping\n\n### Python -> MySQL\n\n| Python Type | MySQL Binary Protocol Encoding |\n|-------------|------------|\n| `None` | `NULL` |\n| `bool` | `Int64` |\n| `int` | `Int64` |\n| `float` | `Double(Float64)` |\n| `str \\| bytes \\| bytearray` | `Bytes` |\n| `tuple \\| list \\| set \\| frozenset \\| dict` | json-encoded string as `Bytes` |\n| `datetime.datetime` | `Date(year, month, day, hour, minute, second, microsecond)` |\n| `datetime.date` | `Date(year, month, day, 0, 0, 0, 0)` |\n| `datetime.time` | `Time(false, 0, hour, minute, second, microsecond)` |\n| `datetime.timedelta` | `Time(is_negative, days, hours, minutes, seconds, microseconds)` |\n| `time.struct_time` | `Date(year, month, day, hour, minute, second, 0)` |\n| `decimal.Decimal` | `Bytes(str(Decimal))` |\n| `uuid.UUID` | `Bytes(UUID.hex)` |\n\n### MySQL -> Python\n\n| MySQL Column | Python |\n|-------------|------------|\n| `NULL` | `None` |\n| `INT` / `TINYINT` / `SMALLINT` / `MEDIUMINT` / `BIGINT` / `YEAR` | `int` |\n| `FLOAT` | `float` |\n| `DOUBLE` | `float` |\n| `DECIMAL` / `NUMERIC` | `decimal.Decimal` |\n| `DATE` | `datetime.date` |\n| `TIME` | `datetime.timedelta` |\n| `DATETIME` | `datetime.datetime` |\n| `TIMESTAMP` | `datetime.datetime` |\n| `CHAR` / `VARCHAR` / `TEXT` / `TINYTEXT` / `MEDIUMTEXT` / `LONGTEXT` | `str` |\n| `BINARY` / `VARBINARY` / `BLOB` / `TINYBLOB` / `MEDIUMBLOB` / `LONGBLOB` | `bytes` |\n| `JSON` | the result of json.loads() |\n| `ENUM` / `SET` | `str` |\n| `BIT` | `bytes` |\n| `GEOMETRY` | `bytes` (WKB format) |\n\n## Logging\n\npyro-mysql sends the Rust logs to the Python logging system, which can be configured with `logging.getLogger(\"pyro_mysql\")`.\n\n```py\n# Queries are logged with the DEBUG level\nlogging.getLogger(\"pyro_mysql\").setLevel(logging.DEBUG)\n```\n\n## PEP-249, sqlalchemy\n\n<img src=\"https://github.com/elbaro/pyro-mysql/blob/main/report/chart_sqlalchemy.png?raw=true\" width=\"800px\" />\n\n`pyro_mysql.dbapi` implements PEP-249.\nThis only exists for compatibility with ORM libraries.\nThe primary API set (`pyro_mysql.sync`, `pyro_mysql.async_`) is simpler and faster.\n\n```sh\npyro_mysql.dbapi\n    # classes\n    \u251c\u2500Connection\n    \u251c\u2500Cursor\n    # exceptions\n    \u251c\u2500Warning\n    \u251c\u2500Error\n    \u251c\u2500IntegrityError\n    \u251c\u2500..\n```\n\nIn sqlalchemy, the following dialects are supported.\n- `mysql+pyro_mysql://` (sync)\n- `mariadb+pyro_mysql://` (sync)\n- `mysql+pyro_mysql_async://` (async)\n- `mariadb+pyro_mysql_async://` (async)\n\nThe supported connection parameters are [the docs](https://docs.rs/mysql/latest/mysql/struct.OptsBuilder.html#method.from_hash_map) and [`capabilities`](https://docs.rs/mysql/latest/mysql/consts/struct.CapabilityFlags.html) (default 2).\n\n```py\nfrom sqlalchemy import create_engine, text\n\nengine = create_engine(\"mysql+pyro_mysql://test:1234@localhost/test\")\nconn = engine.connect()\ncursor_result = conn.execute(text(\"SHOW TABLES\"))\nfor row in cursor_result:\n    print(row)\n```\n\n```\n('information_schema',)\n('mysql',)\n('performance_schema',)\n('sys',)\n('test',)\n```\n\nTo run sqlalchemy tests on pyro_mysql, use this command in the sqlalchemy repo:\n\n```sh\npytest -p pyro_mysql.testing.sqlalchemy_pytest_plugin --dburi=mariadb+pyro_mysql://test:1234@localhost/test -v t\n```\n\n`sqlalchemy_pytest_plugin` is required to skip incompatible tests.\n\n\n## API Overview\n\n- [pyro_mysql](https://github.com/elbaro/pyro-mysql/blob/main/pyro_mysql/__init__.pyi)\n- [pyro_mysql,sync](https://github.com/elbaro/pyro-mysql/blob/main/pyro_mysql/sync.pyi)\n- [pyro_mysql.async_](https://github.com/elbaro/pyro-mysql/blob/main/pyro_mysql/async_.pyi)\n- [pyro_mysql.dbapi](https://github.com/elbaro/pyro-mysql/blob/main/pyro_mysql/dbapi.pyi)\n- [pyro_mysql.dbapi_async](https://github.com/elbaro/pyro-mysql/blob/main/pyro_mysql/dbapi_async.pyi)\n- [pyro_mysql.error](https://github.com/elbaro/pyro-mysql/blob/main/pyro_mysql/error.pyi)\n\n```\n.\n\u2514\u2500\u2500 pyro_mysql/\n    \u251c\u2500\u2500 init()\n    \u251c\u2500\u2500 (common classes)/\n    \u2502   \u251c\u2500\u2500 Row\n    \u2502   \u251c\u2500\u2500 IsolationLevel\n    \u2502   \u251c\u2500\u2500 CapabilityFlags\n    \u2502   \u2514\u2500\u2500 PyroFuture\n    \u251c\u2500\u2500 sync/\n    \u2502   \u251c\u2500\u2500 Conn\n    \u2502   \u251c\u2500\u2500 Transaction\n    \u2502   \u251c\u2500\u2500 Pool\n    \u2502   \u251c\u2500\u2500 Opts\n    \u2502   \u251c\u2500\u2500 OptsBuilder\n    \u2502   \u2514\u2500\u2500 PoolOpts\n    \u251c\u2500\u2500 async_/\n    \u2502   \u251c\u2500\u2500 Conn\n    \u2502   \u251c\u2500\u2500 Transaction\n    \u2502   \u251c\u2500\u2500 Pool\n    \u2502   \u251c\u2500\u2500 Opts\n    \u2502   \u251c\u2500\u2500 OptsBuilder\n    \u2502   \u2514\u2500\u2500 PoolOpts\n    \u251c\u2500\u2500 dbapi/\n    \u2502   \u251c\u2500\u2500 connect()\n    \u2502   \u251c\u2500\u2500 Connection\n    \u2502   \u251c\u2500\u2500 Cursor\n    \u2502   \u2514\u2500\u2500 (exceptions)/\n    \u2502       \u251c\u2500\u2500 Warning\n    \u2502       \u251c\u2500\u2500 Error\n    \u2502       \u251c\u2500\u2500 InterfaceError\n    \u2502       \u251c\u2500\u2500 DatabaseError\n    \u2502       \u251c\u2500\u2500 DataError\n    \u2502       \u251c\u2500\u2500 OperationalError\n    \u2502       \u251c\u2500\u2500 IntegrityError\n    \u2502       \u251c\u2500\u2500 InternalError\n    \u2502       \u251c\u2500\u2500 ProgrammingError\n    \u2502       \u2514\u2500\u2500 NotSupportedError\n    \u251c\u2500\u2500 dbapi_async/\n    \u2502   \u251c\u2500\u2500 connect()\n    \u2502   \u251c\u2500\u2500 Connection\n    \u2502   \u251c\u2500\u2500 Cursor\n    \u2502   \u2514\u2500\u2500 (exceptions)/\n    \u2502       \u251c\u2500\u2500 Warning\n    \u2502       \u251c\u2500\u2500 Error\n    \u2502       \u251c\u2500\u2500 InterfaceError\n    \u2502       \u251c\u2500\u2500 DatabaseError\n    \u2502       \u251c\u2500\u2500 DataError\n    \u2502       \u251c\u2500\u2500 OperationalError\n    \u2502       \u251c\u2500\u2500 IntegrityError\n    \u2502       \u251c\u2500\u2500 InternalError\n    \u2502       \u251c\u2500\u2500 ProgrammingError\n    \u2502       \u2514\u2500\u2500 NotSupportedError\n    \u2514\u2500\u2500 (aliases)/\n        \u251c\u2500\u2500 SyncConn\n        \u251c\u2500\u2500 SyncTransaction\n        \u251c\u2500\u2500 SyncPool\n        \u251c\u2500\u2500 SyncOpts\n        \u251c\u2500\u2500 SyncOptsBuilder\n        \u251c\u2500\u2500 SyncPoolOpts\n        \u251c\u2500\u2500 AsyncConn\n        \u251c\u2500\u2500 AsyncTransaction\n        \u251c\u2500\u2500 AsyncPool\n        \u251c\u2500\u2500 AsyncOpts\n        \u251c\u2500\u2500 AsyncOptsBuilder\n        \u2514\u2500\u2500 AsyncPoolOpts\n```\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "High-performance MySQL driver for Python",
    "version": "0.1.7",
    "project_urls": {
        "Repository": "https://github.com/elbaro/pyro-mysql"
    },
    "split_keywords": [
        "mysql",
        " async",
        " asyncio",
        " database",
        " rust",
        " pyo3"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "b905db7f2bba69afa803f52d693c6498d0beeb80cd77db62eddb429d52cbc1d9",
                "md5": "b6b212058e724875e8f979920db43fde",
                "sha256": "f0626d61d23246d46fd85c29d65a255b8f1727d9c2da2cfdfc05f2d89458f534"
            },
            "downloads": -1,
            "filename": "pyro_mysql-0.1.7-cp310-abi3-manylinux_2_34_x86_64.whl",
            "has_sig": false,
            "md5_digest": "b6b212058e724875e8f979920db43fde",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.10",
            "size": 2452119,
            "upload_time": "2025-10-27T02:27:36",
            "upload_time_iso_8601": "2025-10-27T02:27:36.777407Z",
            "url": "https://files.pythonhosted.org/packages/b9/05/db7f2bba69afa803f52d693c6498d0beeb80cd77db62eddb429d52cbc1d9/pyro_mysql-0.1.7-cp310-abi3-manylinux_2_34_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "b55c5c39df89daa5f5b8d111cab02c693c3599789428f840066890cfe358dc50",
                "md5": "8c9b9d0a7792f61c3a3e32dd0ac2e680",
                "sha256": "bfc58c13f278a24b8a10b660f949ee81e06a0cd317ad89bca3e834bd67a0eb7b"
            },
            "downloads": -1,
            "filename": "pyro_mysql-0.1.7.tar.gz",
            "has_sig": false,
            "md5_digest": "8c9b9d0a7792f61c3a3e32dd0ac2e680",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 259378,
            "upload_time": "2025-10-27T02:27:39",
            "upload_time_iso_8601": "2025-10-27T02:27:39.350141Z",
            "url": "https://files.pythonhosted.org/packages/b5/5c/5c39df89daa5f5b8d111cab02c693c3599789428f840066890cfe358dc50/pyro_mysql-0.1.7.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-27 02:27:39",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "elbaro",
    "github_project": "pyro-mysql",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "pyro-mysql"
}
        
Elapsed time: 2.34333s