From c500155a97a281bf6394cd1da9333fd1a8cf313c Mon Sep 17 00:00:00 2001 From: euri10 Date: Mon, 2 Feb 2026 11:39:30 +0100 Subject: [PATCH 01/11] adding sqlite bench --- scripts/bench.py | 270 +++++++++++++++++++++++++++++++++++++++++++++++ uv.lock | 192 ++++++++++++++++----------------- 2 files changed, 366 insertions(+), 96 deletions(-) create mode 100644 scripts/bench.py diff --git a/scripts/bench.py b/scripts/bench.py new file mode 100644 index 00000000..d67acc95 --- /dev/null +++ b/scripts/bench.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python3 +import argparse +import time +import sqlite3 +import tempfile +from pathlib import Path +from dataclasses import dataclass +from typing import Callable, Dict, List, Tuple + +from sqlspec import SQLSpec +from sqlalchemy import create_engine, text + +# ========================== +# CLI +# ========================== + +def parse_args(): + p = argparse.ArgumentParser() + p.add_argument("--rows", type=int, default=10_000) + p.add_argument("--reads", type=int, default=5_000) + p.add_argument( + "--drivers", + nargs="+", + default=["sqlite"], + help="Drivers to test (sqlite, duckdb, ...)", + ) + p.add_argument( + "--modes", + nargs="+", + default=["session", "write", "read"], + choices=["session", "write", "read"], + ) + p.add_argument("--wal", action="store_true") + p.add_argument("--memory", action="store_true") + return p.parse_args() + +# ========================== +# UTIL +# ========================== + +def now(): + return time.perf_counter() + +def run(label: str, fn: Callable[[], None]) -> float: + fn() # warmup + t0 = now() + fn() + dt = now() - t0 + print(f"{label:<30} {dt:.5f}s") + return dt + +def slowdown(base: float, other: float) -> float: + return other / base if base else float("inf") + +# ========================== +# DRIVER REGISTRY +# ========================== + +@dataclass +class Driver: + name: str + raw_session: Callable[[str], Callable[[], None]] + raw_write: Callable[[str], Callable[[], None]] + raw_read: Callable[[str], Callable[[], None]] + + sqlspec_config: Callable[[SQLSpec, str], object] + sqlalchemy_url: Callable[[str], str] + +# ========================== +# SQLITE IMPLEMENTATION +# ========================== + +def sqlite_connect(db, wal=False): + conn = sqlite3.connect(db) + if wal: + conn.execute("pragma journal_mode=wal") + return conn + +def sqlite_raw_session(db, wal): + return lambda: sqlite_connect(db, wal).close() + +def sqlite_populate(conn, rows): + cur = conn.cursor() + cur.execute("drop table if exists notes") + cur.execute("create table notes (id integer primary key, body text)") + cur.executemany( + "insert into notes (body) values (?)", + [(f"note {i}",) for i in range(rows)], + ) + conn.commit() + +def sqlite_raw_write(db, rows, wal): + def fn(): + conn = sqlite_connect(db, wal) + sqlite_populate(conn, rows) + conn.close() + return fn + +def sqlite_raw_read(db, rows, reads, wal): + def fn(): + conn = sqlite_connect(db, wal) + sqlite_populate(conn, rows) + cur = conn.cursor() + for i in range(reads): + cur.execute( + "select body from notes where id = ?", + ((i % rows) + 1,), + ) + cur.fetchone() + conn.close() + return fn + +def make_sqlite_driver(args): + from sqlspec.adapters.sqlite import SqliteConfig + + return Driver( + name="sqlite", + raw_session=lambda db: sqlite_raw_session(db, args.wal), + raw_write=lambda db: sqlite_raw_write(db, args.rows, args.wal), + raw_read=lambda db: sqlite_raw_read(db, args.rows, args.reads, args.wal), + sqlspec_config=lambda spec, db: spec.add_config( + SqliteConfig(connection_config={"database": db}) + ), + sqlalchemy_url=lambda db: f"sqlite:///{db}", + ) + +# ========================== +# SQLSPEC BENCHES +# ========================== + +def sqlspec_session(spec, cfg): + def fn(): + with spec.provide_session(cfg): + pass + return fn + +def sqlspec_populate(spec, cfg, rows): + with spec.provide_session(cfg) as s: + s.execute("drop table if exists notes") + s.execute("create table notes (id integer primary key, body text)") + s.execute( + "insert into notes (body) values (?)", + [(f"note {i}",) for i in range(rows)], + ) + +def sqlspec_write(spec, cfg, rows): + return lambda: sqlspec_populate(spec, cfg, rows) + +def sqlspec_read(spec, cfg, rows, reads): + def fn(): + sqlspec_populate(spec, cfg, rows) + with spec.provide_session(cfg) as s: + for i in range(reads): + s.execute( + "select body from notes where id = ?", + ((i % rows) + 1,), + ).one() + return fn + +# ========================== +# SQLALCHEMY BENCHES +# ========================== + +def sa_session(url): + engine = create_engine(url, future=True) + return lambda: engine.connect().close() + +def sa_populate(conn, rows): + conn.execute(text("drop table if exists notes")) + conn.execute(text("create table notes (id integer primary key, body text)")) + conn.execute( + text("insert into notes (body) values (:body)"), + [{"body": f"note {i}"} for i in range(rows)], + ) + +def sa_write(url, rows): + engine = create_engine(url, future=True) + def fn(): + with engine.begin() as conn: + sa_populate(conn, rows) + return fn + +def sa_read(url, rows, reads): + engine = create_engine(url, future=True) + def fn(): + with engine.begin() as conn: + sa_populate(conn, rows) + for i in range(reads): + conn.execute( + text("select body from notes where id = :id"), + {"id": (i % rows) + 1}, + ).fetchone() + return fn + +# ========================== +# RUNNER +# ========================== + +def run_driver(driver: Driver, db: str, args): + print(f"\n--- DRIVER: {driver.name} ---") + + spec = SQLSpec() + cfg = driver.sqlspec_config(spec, db) + sa_url = driver.sqlalchemy_url(db) + + if "session" in args.modes: + print("\nSession / connection cost") + t0 = run("raw", driver.raw_session(db)) + t1 = run("sqlspec", sqlspec_session(spec, cfg)) + t2 = run("sqlalchemy", sa_session(sa_url)) + print( + f"Slowdowns vs raw: " + f"sqlspec {slowdown(t0, t1):.2f}x, " + f"sqlalchemy {slowdown(t0, t2):.2f}x" + ) + + if "write" in args.modes: + print("\nWrite-heavy workload") + t0 = run("raw", driver.raw_write(db)) + t1 = run("sqlspec", sqlspec_write(spec, cfg, args.rows)) + t2 = run("sqlalchemy", sa_write(sa_url, args.rows)) + print( + f"Slowdowns vs raw: " + f"sqlspec {slowdown(t0, t1):.2f}x, " + f"sqlalchemy {slowdown(t0, t2):.2f}x" + ) + + if "read" in args.modes: + print("\nRead-heavy workload") + t0 = run("raw", driver.raw_read(db)) + t1 = run("sqlspec", sqlspec_read(spec, cfg, args.rows, args.reads)) + t2 = run("sqlalchemy", sa_read(sa_url, args.rows, args.reads)) + print( + f"Slowdowns vs raw: " + f"sqlspec {slowdown(t0, t1):.2f}x, " + f"sqlalchemy {slowdown(t0, t2):.2f}x" + ) + +# ========================== +# MAIN +# ========================== + +def main(): + args = parse_args() + + print( + f"ROWS={args.rows:,}, READS={args.reads:,}, " + f"WAL={'on' if args.wal else 'off'}, " + f"DB={'memory' if args.memory else 'file'}" + ) + + drivers: Dict[str, Callable] = { + "sqlite": lambda: make_sqlite_driver(args), + } + + selected = [d for d in args.drivers if d in drivers] + + if args.memory: + db = ":memory:" + for name in selected: + run_driver(drivers[name](), db, args) + else: + with tempfile.TemporaryDirectory() as d: + db = str(Path(d) / "bench.db") + for name in selected: + run_driver(drivers[name](), db, args) + +if __name__ == "__main__": + main() + diff --git a/uv.lock b/uv.lock index 43bc5b82..bf209950 100644 --- a/uv.lock +++ b/uv.lock @@ -17,7 +17,7 @@ resolution-markers = [ [options] [options.exclude-newer-package] -mysql-connector-python = "2026-01-21T00:00:00Z" +mysql-connector-python = "2026-01-20T23:00:00Z" [[package]] name = "adbc-driver-bigquery" @@ -657,11 +657,11 @@ wheels = [ [[package]] name = "babel" -version = "2.17.0" +version = "2.18.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, + { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, ] [[package]] @@ -2706,7 +2706,7 @@ dependencies = [ { name = "comm" }, { name = "debugpy" }, { name = "ipython", version = "8.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "9.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyter-client" }, { name = "jupyter-core" }, { name = "matplotlib-inline" }, @@ -2749,7 +2749,7 @@ wheels = [ [[package]] name = "ipython" -version = "9.9.0" +version = "9.10.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -2775,9 +2775,9 @@ dependencies = [ { name = "traitlets", marker = "python_full_version >= '3.11'" }, { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/dd/fb08d22ec0c27e73c8bc8f71810709870d51cadaf27b7ddd3f011236c100/ipython-9.9.0.tar.gz", hash = "sha256:48fbed1b2de5e2c7177eefa144aba7fcb82dac514f09b57e2ac9da34ddb54220", size = 4425043, upload-time = "2026-01-05T12:36:46.233Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/60/2111715ea11f39b1535bed6024b7dec7918b71e5e5d30855a5b503056b50/ipython-9.10.0.tar.gz", hash = "sha256:cd9e656be97618a0676d058134cd44e6dc7012c0e5cb36a9ce96a8c904adaf77", size = 4426526, upload-time = "2026-02-02T10:00:33.594Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.0-py3-none-any.whl", hash = "sha256:b457fe9165df2b84e8ec909a97abcf2ed88f565970efba16b1f7229c283d252b", size = 621431, upload-time = "2026-01-05T12:36:44.669Z" }, + { url = "https://files.pythonhosted.org/packages/3d/aa/898dec789a05731cd5a9f50605b7b44a72bd198fd0d4528e11fc610177cc/ipython-9.10.0-py3-none-any.whl", hash = "sha256:c6ab68cc23bba8c7e18e9b932797014cc61ea7fd6f19de180ab9ba73e65ee58d", size = 622774, upload-time = "2026-02-02T10:00:31.503Z" }, ] [[package]] @@ -2799,7 +2799,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "comm" }, { name = "ipython", version = "8.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "9.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyterlab-widgets" }, { name = "traitlets" }, { name = "widgetsnbextension" }, @@ -2914,7 +2914,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ipykernel" }, { name = "ipython", version = "8.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "9.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "ipywidgets" }, { name = "nbconvert" }, { name = "nbformat" }, @@ -3962,7 +3962,7 @@ wheels = [ [[package]] name = "numpy" -version = "2.4.1" +version = "2.4.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -3975,79 +3975,79 @@ resolution-markers = [ "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] -sdist = { url = "https://files.pythonhosted.org/packages/24/62/ae72ff66c0f1fd959925b4c11f8c2dea61f47f6acaea75a08512cdfe3fed/numpy-2.4.1.tar.gz", hash = "sha256:a1ceafc5042451a858231588a104093474c6a5c57dcc724841f5c888d237d690", size = 20721320, upload-time = "2026-01-10T06:44:59.619Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/34/2b1bc18424f3ad9af577f6ce23600319968a70575bd7db31ce66731bbef9/numpy-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0cce2a669e3c8ba02ee563c7835f92c153cf02edff1ae05e1823f1dde21b16a5", size = 16944563, upload-time = "2026-01-10T06:42:14.615Z" }, - { url = "https://files.pythonhosted.org/packages/2c/57/26e5f97d075aef3794045a6ca9eada6a4ed70eb9a40e7a4a93f9ac80d704/numpy-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:899d2c18024984814ac7e83f8f49d8e8180e2fbe1b2e252f2e7f1d06bea92425", size = 12645658, upload-time = "2026-01-10T06:42:17.298Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ba/80fc0b1e3cb2fd5c6143f00f42eb67762aa043eaa05ca924ecc3222a7849/numpy-2.4.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:09aa8a87e45b55a1c2c205d42e2808849ece5c484b2aab11fecabec3841cafba", size = 5474132, upload-time = "2026-01-10T06:42:19.637Z" }, - { url = "https://files.pythonhosted.org/packages/40/ae/0a5b9a397f0e865ec171187c78d9b57e5588afc439a04ba9cab1ebb2c945/numpy-2.4.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:edee228f76ee2dab4579fad6f51f6a305de09d444280109e0f75df247ff21501", size = 6804159, upload-time = "2026-01-10T06:42:21.44Z" }, - { url = "https://files.pythonhosted.org/packages/86/9c/841c15e691c7085caa6fd162f063eff494099c8327aeccd509d1ab1e36ab/numpy-2.4.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a92f227dbcdc9e4c3e193add1a189a9909947d4f8504c576f4a732fd0b54240a", size = 14708058, upload-time = "2026-01-10T06:42:23.546Z" }, - { url = "https://files.pythonhosted.org/packages/5d/9d/7862db06743f489e6a502a3b93136d73aea27d97b2cf91504f70a27501d6/numpy-2.4.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:538bf4ec353709c765ff75ae616c34d3c3dca1a68312727e8f2676ea644f8509", size = 16651501, upload-time = "2026-01-10T06:42:25.909Z" }, - { url = "https://files.pythonhosted.org/packages/a6/9c/6fc34ebcbd4015c6e5f0c0ce38264010ce8a546cb6beacb457b84a75dfc8/numpy-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ac08c63cb7779b85e9d5318e6c3518b424bc1f364ac4cb2c6136f12e5ff2dccc", size = 16492627, upload-time = "2026-01-10T06:42:28.938Z" }, - { url = "https://files.pythonhosted.org/packages/aa/63/2494a8597502dacda439f61b3c0db4da59928150e62be0e99395c3ad23c5/numpy-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f9c360ecef085e5841c539a9a12b883dff005fbd7ce46722f5e9cef52634d82", size = 18585052, upload-time = "2026-01-10T06:42:31.312Z" }, - { url = "https://files.pythonhosted.org/packages/6a/93/098e1162ae7522fc9b618d6272b77404c4656c72432ecee3abc029aa3de0/numpy-2.4.1-cp311-cp311-win32.whl", hash = "sha256:0f118ce6b972080ba0758c6087c3617b5ba243d806268623dc34216d69099ba0", size = 6236575, upload-time = "2026-01-10T06:42:33.872Z" }, - { url = "https://files.pythonhosted.org/packages/8c/de/f5e79650d23d9e12f38a7bc6b03ea0835b9575494f8ec94c11c6e773b1b1/numpy-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:18e14c4d09d55eef39a6ab5b08406e84bc6869c1e34eef45564804f90b7e0574", size = 12604479, upload-time = "2026-01-10T06:42:35.778Z" }, - { url = "https://files.pythonhosted.org/packages/dd/65/e1097a7047cff12ce3369bd003811516b20ba1078dbdec135e1cd7c16c56/numpy-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:6461de5113088b399d655d45c3897fa188766415d0f568f175ab071c8873bd73", size = 10578325, upload-time = "2026-01-10T06:42:38.518Z" }, - { url = "https://files.pythonhosted.org/packages/78/7f/ec53e32bf10c813604edf07a3682616bd931d026fcde7b6d13195dfb684a/numpy-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d3703409aac693fa82c0aee023a1ae06a6e9d065dba10f5e8e80f642f1e9d0a2", size = 16656888, upload-time = "2026-01-10T06:42:40.913Z" }, - { url = "https://files.pythonhosted.org/packages/b8/e0/1f9585d7dae8f14864e948fd7fa86c6cb72dee2676ca2748e63b1c5acfe0/numpy-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7211b95ca365519d3596a1d8688a95874cc94219d417504d9ecb2df99fa7bfa8", size = 12373956, upload-time = "2026-01-10T06:42:43.091Z" }, - { url = "https://files.pythonhosted.org/packages/8e/43/9762e88909ff2326f5e7536fa8cb3c49fb03a7d92705f23e6e7f553d9cb3/numpy-2.4.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5adf01965456a664fc727ed69cc71848f28d063217c63e1a0e200a118d5eec9a", size = 5202567, upload-time = "2026-01-10T06:42:45.107Z" }, - { url = "https://files.pythonhosted.org/packages/4b/ee/34b7930eb61e79feb4478800a4b95b46566969d837546aa7c034c742ef98/numpy-2.4.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:26f0bcd9c79a00e339565b303badc74d3ea2bd6d52191eeca5f95936cad107d0", size = 6549459, upload-time = "2026-01-10T06:42:48.152Z" }, - { url = "https://files.pythonhosted.org/packages/79/e3/5f115fae982565771be994867c89bcd8d7208dbfe9469185497d70de5ddf/numpy-2.4.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0093e85df2960d7e4049664b26afc58b03236e967fb942354deef3208857a04c", size = 14404859, upload-time = "2026-01-10T06:42:49.947Z" }, - { url = "https://files.pythonhosted.org/packages/d9/7d/9c8a781c88933725445a859cac5d01b5871588a15969ee6aeb618ba99eee/numpy-2.4.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ad270f438cbdd402c364980317fb6b117d9ec5e226fff5b4148dd9aa9fc6e02", size = 16371419, upload-time = "2026-01-10T06:42:52.409Z" }, - { url = "https://files.pythonhosted.org/packages/a6/d2/8aa084818554543f17cf4162c42f162acbd3bb42688aefdba6628a859f77/numpy-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:297c72b1b98100c2e8f873d5d35fb551fce7040ade83d67dd51d38c8d42a2162", size = 16182131, upload-time = "2026-01-10T06:42:54.694Z" }, - { url = "https://files.pythonhosted.org/packages/60/db/0425216684297c58a8df35f3284ef56ec4a043e6d283f8a59c53562caf1b/numpy-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf6470d91d34bf669f61d515499859fa7a4c2f7c36434afb70e82df7217933f9", size = 18295342, upload-time = "2026-01-10T06:42:56.991Z" }, - { url = "https://files.pythonhosted.org/packages/31/4c/14cb9d86240bd8c386c881bafbe43f001284b7cce3bc01623ac9475da163/numpy-2.4.1-cp312-cp312-win32.whl", hash = "sha256:b6bcf39112e956594b3331316d90c90c90fb961e39696bda97b89462f5f3943f", size = 5959015, upload-time = "2026-01-10T06:42:59.631Z" }, - { url = "https://files.pythonhosted.org/packages/51/cf/52a703dbeb0c65807540d29699fef5fda073434ff61846a564d5c296420f/numpy-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:e1a27bb1b2dee45a2a53f5ca6ff2d1a7f135287883a1689e930d44d1ff296c87", size = 12310730, upload-time = "2026-01-10T06:43:01.627Z" }, - { url = "https://files.pythonhosted.org/packages/69/80/a828b2d0ade5e74a9fe0f4e0a17c30fdc26232ad2bc8c9f8b3197cf7cf18/numpy-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:0e6e8f9d9ecf95399982019c01223dc130542960a12edfa8edd1122dfa66a8a8", size = 10312166, upload-time = "2026-01-10T06:43:03.673Z" }, - { url = "https://files.pythonhosted.org/packages/04/68/732d4b7811c00775f3bd522a21e8dd5a23f77eb11acdeb663e4a4ebf0ef4/numpy-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d797454e37570cfd61143b73b8debd623c3c0952959adb817dd310a483d58a1b", size = 16652495, upload-time = "2026-01-10T06:43:06.283Z" }, - { url = "https://files.pythonhosted.org/packages/20/ca/857722353421a27f1465652b2c66813eeeccea9d76d5f7b74b99f298e60e/numpy-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c55962006156aeef1629b953fd359064aa47e4d82cfc8e67f0918f7da3344f", size = 12368657, upload-time = "2026-01-10T06:43:09.094Z" }, - { url = "https://files.pythonhosted.org/packages/81/0d/2377c917513449cc6240031a79d30eb9a163d32a91e79e0da47c43f2c0c8/numpy-2.4.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:71abbea030f2cfc3092a0ff9f8c8fdefdc5e0bf7d9d9c99663538bb0ecdac0b9", size = 5197256, upload-time = "2026-01-10T06:43:13.634Z" }, - { url = "https://files.pythonhosted.org/packages/17/39/569452228de3f5de9064ac75137082c6214be1f5c532016549a7923ab4b5/numpy-2.4.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5b55aa56165b17aaf15520beb9cbd33c9039810e0d9643dd4379e44294c7303e", size = 6545212, upload-time = "2026-01-10T06:43:15.661Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a4/77333f4d1e4dac4395385482557aeecf4826e6ff517e32ca48e1dafbe42a/numpy-2.4.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0faba4a331195bfa96f93dd9dfaa10b2c7aa8cda3a02b7fd635e588fe821bf5", size = 14402871, upload-time = "2026-01-10T06:43:17.324Z" }, - { url = "https://files.pythonhosted.org/packages/ba/87/d341e519956273b39d8d47969dd1eaa1af740615394fe67d06f1efa68773/numpy-2.4.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e3087f53e2b4428766b54932644d148613c5a595150533ae7f00dab2f319a8", size = 16359305, upload-time = "2026-01-10T06:43:19.376Z" }, - { url = "https://files.pythonhosted.org/packages/32/91/789132c6666288eaa20ae8066bb99eba1939362e8f1a534949a215246e97/numpy-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:49e792ec351315e16da54b543db06ca8a86985ab682602d90c60ef4ff4db2a9c", size = 16181909, upload-time = "2026-01-10T06:43:21.808Z" }, - { url = "https://files.pythonhosted.org/packages/cf/b8/090b8bd27b82a844bb22ff8fdf7935cb1980b48d6e439ae116f53cdc2143/numpy-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79e9e06c4c2379db47f3f6fc7a8652e7498251789bf8ff5bd43bf478ef314ca2", size = 18284380, upload-time = "2026-01-10T06:43:23.957Z" }, - { url = "https://files.pythonhosted.org/packages/67/78/722b62bd31842ff029412271556a1a27a98f45359dea78b1548a3a9996aa/numpy-2.4.1-cp313-cp313-win32.whl", hash = "sha256:3d1a100e48cb266090a031397863ff8a30050ceefd798f686ff92c67a486753d", size = 5957089, upload-time = "2026-01-10T06:43:27.535Z" }, - { url = "https://files.pythonhosted.org/packages/da/a6/cf32198b0b6e18d4fbfa9a21a992a7fca535b9bb2b0cdd217d4a3445b5ca/numpy-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:92a0e65272fd60bfa0d9278e0484c2f52fe03b97aedc02b357f33fe752c52ffb", size = 12307230, upload-time = "2026-01-10T06:43:29.298Z" }, - { url = "https://files.pythonhosted.org/packages/44/6c/534d692bfb7d0afe30611320c5fb713659dcb5104d7cc182aff2aea092f5/numpy-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:20d4649c773f66cc2fc36f663e091f57c3b7655f936a4c681b4250855d1da8f5", size = 10313125, upload-time = "2026-01-10T06:43:31.782Z" }, - { url = "https://files.pythonhosted.org/packages/da/a1/354583ac5c4caa566de6ddfbc42744409b515039e085fab6e0ff942e0df5/numpy-2.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f93bc6892fe7b0663e5ffa83b61aab510aacffd58c16e012bb9352d489d90cb7", size = 12496156, upload-time = "2026-01-10T06:43:34.237Z" }, - { url = "https://files.pythonhosted.org/packages/51/b0/42807c6e8cce58c00127b1dc24d365305189991f2a7917aa694a109c8d7d/numpy-2.4.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:178de8f87948163d98a4c9ab5bee4ce6519ca918926ec8df195af582de28544d", size = 5324663, upload-time = "2026-01-10T06:43:36.211Z" }, - { url = "https://files.pythonhosted.org/packages/fe/55/7a621694010d92375ed82f312b2f28017694ed784775269115323e37f5e2/numpy-2.4.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:98b35775e03ab7f868908b524fc0a84d38932d8daf7b7e1c3c3a1b6c7a2c9f15", size = 6645224, upload-time = "2026-01-10T06:43:37.884Z" }, - { url = "https://files.pythonhosted.org/packages/50/96/9fa8635ed9d7c847d87e30c834f7109fac5e88549d79ef3324ab5c20919f/numpy-2.4.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941c2a93313d030f219f3a71fd3d91a728b82979a5e8034eb2e60d394a2b83f9", size = 14462352, upload-time = "2026-01-10T06:43:39.479Z" }, - { url = "https://files.pythonhosted.org/packages/03/d1/8cf62d8bb2062da4fb82dd5d49e47c923f9c0738032f054e0a75342faba7/numpy-2.4.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:529050522e983e00a6c1c6b67411083630de8b57f65e853d7b03d9281b8694d2", size = 16407279, upload-time = "2026-01-10T06:43:41.93Z" }, - { url = "https://files.pythonhosted.org/packages/86/1c/95c86e17c6b0b31ce6ef219da00f71113b220bcb14938c8d9a05cee0ff53/numpy-2.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2302dc0224c1cbc49bb94f7064f3f923a971bfae45c33870dcbff63a2a550505", size = 16248316, upload-time = "2026-01-10T06:43:44.121Z" }, - { url = "https://files.pythonhosted.org/packages/30/b4/e7f5ff8697274c9d0fa82398b6a372a27e5cef069b37df6355ccb1f1db1a/numpy-2.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9171a42fcad32dcf3fa86f0a4faa5e9f8facefdb276f54b8b390d90447cff4e2", size = 18329884, upload-time = "2026-01-10T06:43:46.613Z" }, - { url = "https://files.pythonhosted.org/packages/37/a4/b073f3e9d77f9aec8debe8ca7f9f6a09e888ad1ba7488f0c3b36a94c03ac/numpy-2.4.1-cp313-cp313t-win32.whl", hash = "sha256:382ad67d99ef49024f11d1ce5dcb5ad8432446e4246a4b014418ba3a1175a1f4", size = 6081138, upload-time = "2026-01-10T06:43:48.854Z" }, - { url = "https://files.pythonhosted.org/packages/16/16/af42337b53844e67752a092481ab869c0523bc95c4e5c98e4dac4e9581ac/numpy-2.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:62fea415f83ad8fdb6c20840578e5fbaf5ddd65e0ec6c3c47eda0f69da172510", size = 12447478, upload-time = "2026-01-10T06:43:50.476Z" }, - { url = "https://files.pythonhosted.org/packages/6c/f8/fa85b2eac68ec631d0b631abc448552cb17d39afd17ec53dcbcc3537681a/numpy-2.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a7870e8c5fc11aef57d6fea4b4085e537a3a60ad2cdd14322ed531fdca68d261", size = 10382981, upload-time = "2026-01-10T06:43:52.575Z" }, - { url = "https://files.pythonhosted.org/packages/1b/a7/ef08d25698e0e4b4efbad8d55251d20fe2a15f6d9aa7c9b30cd03c165e6f/numpy-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3869ea1ee1a1edc16c29bbe3a2f2a4e515cc3a44d43903ad41e0cacdbaf733dc", size = 16652046, upload-time = "2026-01-10T06:43:54.797Z" }, - { url = "https://files.pythonhosted.org/packages/8f/39/e378b3e3ca13477e5ac70293ec027c438d1927f18637e396fe90b1addd72/numpy-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e867df947d427cdd7a60e3e271729090b0f0df80f5f10ab7dd436f40811699c3", size = 12378858, upload-time = "2026-01-10T06:43:57.099Z" }, - { url = "https://files.pythonhosted.org/packages/c3/74/7ec6154f0006910ed1fdbb7591cf4432307033102b8a22041599935f8969/numpy-2.4.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:e3bd2cb07841166420d2fa7146c96ce00cb3410664cbc1a6be028e456c4ee220", size = 5207417, upload-time = "2026-01-10T06:43:59.037Z" }, - { url = "https://files.pythonhosted.org/packages/f7/b7/053ac11820d84e42f8feea5cb81cc4fcd1091499b45b1ed8c7415b1bf831/numpy-2.4.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:f0a90aba7d521e6954670550e561a4cb925713bd944445dbe9e729b71f6cabee", size = 6542643, upload-time = "2026-01-10T06:44:01.852Z" }, - { url = "https://files.pythonhosted.org/packages/c0/c4/2e7908915c0e32ca636b92e4e4a3bdec4cb1e7eb0f8aedf1ed3c68a0d8cd/numpy-2.4.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d558123217a83b2d1ba316b986e9248a1ed1971ad495963d555ccd75dcb1556", size = 14418963, upload-time = "2026-01-10T06:44:04.047Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c0/3ed5083d94e7ffd7c404e54619c088e11f2e1939a9544f5397f4adb1b8ba/numpy-2.4.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f44de05659b67d20499cbc96d49f2650769afcb398b79b324bb6e297bfe3844", size = 16363811, upload-time = "2026-01-10T06:44:06.207Z" }, - { url = "https://files.pythonhosted.org/packages/0e/68/42b66f1852bf525050a67315a4fb94586ab7e9eaa541b1bef530fab0c5dd/numpy-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:69e7419c9012c4aaf695109564e3387f1259f001b4326dfa55907b098af082d3", size = 16197643, upload-time = "2026-01-10T06:44:08.33Z" }, - { url = "https://files.pythonhosted.org/packages/d2/40/e8714fc933d85f82c6bfc7b998a0649ad9769a32f3494ba86598aaf18a48/numpy-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2ffd257026eb1b34352e749d7cc1678b5eeec3e329ad8c9965a797e08ccba205", size = 18289601, upload-time = "2026-01-10T06:44:10.841Z" }, - { url = "https://files.pythonhosted.org/packages/80/9a/0d44b468cad50315127e884802351723daca7cf1c98d102929468c81d439/numpy-2.4.1-cp314-cp314-win32.whl", hash = "sha256:727c6c3275ddefa0dc078524a85e064c057b4f4e71ca5ca29a19163c607be745", size = 6005722, upload-time = "2026-01-10T06:44:13.332Z" }, - { url = "https://files.pythonhosted.org/packages/7e/bb/c6513edcce5a831810e2dddc0d3452ce84d208af92405a0c2e58fd8e7881/numpy-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:7d5d7999df434a038d75a748275cd6c0094b0ecdb0837342b332a82defc4dc4d", size = 12438590, upload-time = "2026-01-10T06:44:15.006Z" }, - { url = "https://files.pythonhosted.org/packages/e9/da/a598d5cb260780cf4d255102deba35c1d072dc028c4547832f45dd3323a8/numpy-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:ce9ce141a505053b3c7bce3216071f3bf5c182b8b28930f14cd24d43932cd2df", size = 10596180, upload-time = "2026-01-10T06:44:17.386Z" }, - { url = "https://files.pythonhosted.org/packages/de/bc/ea3f2c96fcb382311827231f911723aeff596364eb6e1b6d1d91128aa29b/numpy-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4e53170557d37ae404bf8d542ca5b7c629d6efa1117dac6a83e394142ea0a43f", size = 12498774, upload-time = "2026-01-10T06:44:19.467Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ab/ef9d939fe4a812648c7a712610b2ca6140b0853c5efea361301006c02ae5/numpy-2.4.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:a73044b752f5d34d4232f25f18160a1cc418ea4507f5f11e299d8ac36875f8a0", size = 5327274, upload-time = "2026-01-10T06:44:23.189Z" }, - { url = "https://files.pythonhosted.org/packages/bd/31/d381368e2a95c3b08b8cf7faac6004849e960f4a042d920337f71cef0cae/numpy-2.4.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:fb1461c99de4d040666ca0444057b06541e5642f800b71c56e6ea92d6a853a0c", size = 6648306, upload-time = "2026-01-10T06:44:25.012Z" }, - { url = "https://files.pythonhosted.org/packages/c8/e5/0989b44ade47430be6323d05c23207636d67d7362a1796ccbccac6773dd2/numpy-2.4.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423797bdab2eeefbe608d7c1ec7b2b4fd3c58d51460f1ee26c7500a1d9c9ee93", size = 14464653, upload-time = "2026-01-10T06:44:26.706Z" }, - { url = "https://files.pythonhosted.org/packages/10/a7/cfbe475c35371cae1358e61f20c5f075badc18c4797ab4354140e1d283cf/numpy-2.4.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:52b5f61bdb323b566b528899cc7db2ba5d1015bda7ea811a8bcf3c89c331fa42", size = 16405144, upload-time = "2026-01-10T06:44:29.378Z" }, - { url = "https://files.pythonhosted.org/packages/f8/a3/0c63fe66b534888fa5177cc7cef061541064dbe2b4b60dcc60ffaf0d2157/numpy-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42d7dd5fa36d16d52a84f821eb96031836fd405ee6955dd732f2023724d0aa01", size = 16247425, upload-time = "2026-01-10T06:44:31.721Z" }, - { url = "https://files.pythonhosted.org/packages/6b/2b/55d980cfa2c93bd40ff4c290bf824d792bd41d2fe3487b07707559071760/numpy-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7b6b5e28bbd47b7532698e5db2fe1db693d84b58c254e4389d99a27bb9b8f6b", size = 18330053, upload-time = "2026-01-10T06:44:34.617Z" }, - { url = "https://files.pythonhosted.org/packages/23/12/8b5fc6b9c487a09a7957188e0943c9ff08432c65e34567cabc1623b03a51/numpy-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:5de60946f14ebe15e713a6f22850c2372fa72f4ff9a432ab44aa90edcadaa65a", size = 6152482, upload-time = "2026-01-10T06:44:36.798Z" }, - { url = "https://files.pythonhosted.org/packages/00/a5/9f8ca5856b8940492fc24fbe13c1bc34d65ddf4079097cf9e53164d094e1/numpy-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8f085da926c0d491ffff3096f91078cc97ea67e7e6b65e490bc8dcda65663be2", size = 12627117, upload-time = "2026-01-10T06:44:38.828Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0d/eca3d962f9eef265f01a8e0d20085c6dd1f443cbffc11b6dede81fd82356/numpy-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:6436cffb4f2bf26c974344439439c95e152c9a527013f26b3577be6c2ca64295", size = 10667121, upload-time = "2026-01-10T06:44:41.644Z" }, - { url = "https://files.pythonhosted.org/packages/1e/48/d86f97919e79314a1cdee4c832178763e6e98e623e123d0bada19e92c15a/numpy-2.4.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8ad35f20be147a204e28b6a0575fbf3540c5e5f802634d4258d55b1ff5facce1", size = 16822202, upload-time = "2026-01-10T06:44:43.738Z" }, - { url = "https://files.pythonhosted.org/packages/51/e9/1e62a7f77e0f37dcfb0ad6a9744e65df00242b6ea37dfafb55debcbf5b55/numpy-2.4.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8097529164c0f3e32bb89412a0905d9100bf434d9692d9fc275e18dcf53c9344", size = 12569985, upload-time = "2026-01-10T06:44:45.945Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7e/914d54f0c801342306fdcdce3e994a56476f1b818c46c47fc21ae968088c/numpy-2.4.1-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:ea66d2b41ca4a1630aae5507ee0a71647d3124d1741980138aa8f28f44dac36e", size = 5398484, upload-time = "2026-01-10T06:44:48.012Z" }, - { url = "https://files.pythonhosted.org/packages/1c/d8/9570b68584e293a33474e7b5a77ca404f1dcc655e40050a600dee81d27fb/numpy-2.4.1-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d3f8f0df9f4b8be57b3bf74a1d087fec68f927a2fab68231fdb442bf2c12e426", size = 6713216, upload-time = "2026-01-10T06:44:49.725Z" }, - { url = "https://files.pythonhosted.org/packages/33/9b/9dd6e2db8d49eb24f86acaaa5258e5f4c8ed38209a4ee9de2d1a0ca25045/numpy-2.4.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2023ef86243690c2791fd6353e5b4848eedaa88ca8a2d129f462049f6d484696", size = 14538937, upload-time = "2026-01-10T06:44:51.498Z" }, - { url = "https://files.pythonhosted.org/packages/53/87/d5bd995b0f798a37105b876350d346eea5838bd8f77ea3d7a48392f3812b/numpy-2.4.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8361ea4220d763e54cff2fbe7d8c93526b744f7cd9ddab47afeff7e14e8503be", size = 16479830, upload-time = "2026-01-10T06:44:53.931Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c7/b801bf98514b6ae6475e941ac05c58e6411dd863ea92916bfd6d510b08c1/numpy-2.4.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:4f1b68ff47680c2925f8063402a693ede215f0257f02596b1318ecdfb1d79e33", size = 12492579, upload-time = "2026-01-10T06:44:57.094Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/44/71852273146957899753e69986246d6a176061ea183407e95418c2aa4d9a/numpy-2.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7e88598032542bd49af7c4747541422884219056c268823ef6e5e89851c8825", size = 16955478, upload-time = "2026-01-31T23:10:25.623Z" }, + { url = "https://files.pythonhosted.org/packages/74/41/5d17d4058bd0cd96bcbd4d9ff0fb2e21f52702aab9a72e4a594efa18692f/numpy-2.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7edc794af8b36ca37ef5fcb5e0d128c7e0595c7b96a2318d1badb6fcd8ee86b1", size = 14965467, upload-time = "2026-01-31T23:10:28.186Z" }, + { url = "https://files.pythonhosted.org/packages/49/48/fb1ce8136c19452ed15f033f8aee91d5defe515094e330ce368a0647846f/numpy-2.4.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:6e9f61981ace1360e42737e2bae58b27bf28a1b27e781721047d84bd754d32e7", size = 5475172, upload-time = "2026-01-31T23:10:30.848Z" }, + { url = "https://files.pythonhosted.org/packages/40/a9/3feb49f17bbd1300dd2570432961f5c8a4ffeff1db6f02c7273bd020a4c9/numpy-2.4.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cb7bbb88aa74908950d979eeaa24dbdf1a865e3c7e45ff0121d8f70387b55f73", size = 6805145, upload-time = "2026-01-31T23:10:32.352Z" }, + { url = "https://files.pythonhosted.org/packages/3f/39/fdf35cbd6d6e2fcad42fcf85ac04a85a0d0fbfbf34b30721c98d602fd70a/numpy-2.4.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f069069931240b3fc703f1e23df63443dbd6390614c8c44a87d96cd0ec81eb1", size = 15966084, upload-time = "2026-01-31T23:10:34.502Z" }, + { url = "https://files.pythonhosted.org/packages/1b/46/6fa4ea94f1ddf969b2ee941290cca6f1bfac92b53c76ae5f44afe17ceb69/numpy-2.4.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c02ef4401a506fb60b411467ad501e1429a3487abca4664871d9ae0b46c8ba32", size = 16899477, upload-time = "2026-01-31T23:10:37.075Z" }, + { url = "https://files.pythonhosted.org/packages/09/a1/2a424e162b1a14a5bd860a464ab4e07513916a64ab1683fae262f735ccd2/numpy-2.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2653de5c24910e49c2b106499803124dde62a5a1fe0eedeaecf4309a5f639390", size = 17323429, upload-time = "2026-01-31T23:10:39.704Z" }, + { url = "https://files.pythonhosted.org/packages/ce/a2/73014149ff250628df72c58204822ac01d768697913881aacf839ff78680/numpy-2.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1ae241bbfc6ae276f94a170b14785e561cb5e7f626b6688cf076af4110887413", size = 18635109, upload-time = "2026-01-31T23:10:41.924Z" }, + { url = "https://files.pythonhosted.org/packages/6c/0c/73e8be2f1accd56df74abc1c5e18527822067dced5ec0861b5bb882c2ce0/numpy-2.4.2-cp311-cp311-win32.whl", hash = "sha256:df1b10187212b198dd45fa943d8985a3c8cf854aed4923796e0e019e113a1bda", size = 6237915, upload-time = "2026-01-31T23:10:45.26Z" }, + { url = "https://files.pythonhosted.org/packages/76/ae/e0265e0163cf127c24c3969d29f1c4c64551a1e375d95a13d32eab25d364/numpy-2.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:b9c618d56a29c9cb1c4da979e9899be7578d2e0b3c24d52079c166324c9e8695", size = 12607972, upload-time = "2026-01-31T23:10:47.021Z" }, + { url = "https://files.pythonhosted.org/packages/29/a5/c43029af9b8014d6ea157f192652c50042e8911f4300f8f6ed3336bf437f/numpy-2.4.2-cp311-cp311-win_arm64.whl", hash = "sha256:47c5a6ed21d9452b10227e5e8a0e1c22979811cad7dcc19d8e3e2fb8fa03f1a3", size = 10485763, upload-time = "2026-01-31T23:10:50.087Z" }, + { url = "https://files.pythonhosted.org/packages/51/6e/6f394c9c77668153e14d4da83bcc247beb5952f6ead7699a1a2992613bea/numpy-2.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a", size = 16667963, upload-time = "2026-01-31T23:10:52.147Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f8/55483431f2b2fd015ae6ed4fe62288823ce908437ed49db5a03d15151678/numpy-2.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1", size = 14693571, upload-time = "2026-01-31T23:10:54.789Z" }, + { url = "https://files.pythonhosted.org/packages/2f/20/18026832b1845cdc82248208dd929ca14c9d8f2bac391f67440707fff27c/numpy-2.4.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e", size = 5203469, upload-time = "2026-01-31T23:10:57.343Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/2eb97c8a77daaba34eaa3fa7241a14ac5f51c46a6bd5911361b644c4a1e2/numpy-2.4.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:805cc8de9fd6e7a22da5aed858e0ab16be5a4db6c873dde1d7451c541553aa27", size = 6550820, upload-time = "2026-01-31T23:10:59.429Z" }, + { url = "https://files.pythonhosted.org/packages/b1/91/b97fdfd12dc75b02c44e26c6638241cc004d4079a0321a69c62f51470c4c/numpy-2.4.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d82351358ffbcdcd7b686b90742a9b86632d6c1c051016484fa0b326a0a1548", size = 15663067, upload-time = "2026-01-31T23:11:01.291Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c6/a18e59f3f0b8071cc85cbc8d80cd02d68aa9710170b2553a117203d46936/numpy-2.4.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e35d3e0144137d9fdae62912e869136164534d64a169f86438bc9561b6ad49f", size = 16619782, upload-time = "2026-01-31T23:11:03.669Z" }, + { url = "https://files.pythonhosted.org/packages/b7/83/9751502164601a79e18847309f5ceec0b1446d7b6aa12305759b72cf98b2/numpy-2.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adb6ed2ad29b9e15321d167d152ee909ec73395901b70936f029c3bc6d7f4460", size = 17013128, upload-time = "2026-01-31T23:11:05.913Z" }, + { url = "https://files.pythonhosted.org/packages/61/c4/c4066322256ec740acc1c8923a10047818691d2f8aec254798f3dd90f5f2/numpy-2.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8906e71fd8afcb76580404e2a950caef2685df3d2a57fe82a86ac8d33cc007ba", size = 18345324, upload-time = "2026-01-31T23:11:08.248Z" }, + { url = "https://files.pythonhosted.org/packages/ab/af/6157aa6da728fa4525a755bfad486ae7e3f76d4c1864138003eb84328497/numpy-2.4.2-cp312-cp312-win32.whl", hash = "sha256:ec055f6dae239a6299cace477b479cca2fc125c5675482daf1dd886933a1076f", size = 5960282, upload-time = "2026-01-31T23:11:10.497Z" }, + { url = "https://files.pythonhosted.org/packages/92/0f/7ceaaeaacb40567071e94dbf2c9480c0ae453d5bb4f52bea3892c39dc83c/numpy-2.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:209fae046e62d0ce6435fcfe3b1a10537e858249b3d9b05829e2a05218296a85", size = 12314210, upload-time = "2026-01-31T23:11:12.176Z" }, + { url = "https://files.pythonhosted.org/packages/2f/a3/56c5c604fae6dd40fa2ed3040d005fca97e91bd320d232ac9931d77ba13c/numpy-2.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:fbde1b0c6e81d56f5dccd95dd4a711d9b95df1ae4009a60887e56b27e8d903fa", size = 10220171, upload-time = "2026-01-31T23:11:14.684Z" }, + { url = "https://files.pythonhosted.org/packages/a1/22/815b9fe25d1d7ae7d492152adbc7226d3eff731dffc38fe970589fcaaa38/numpy-2.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c", size = 16663696, upload-time = "2026-01-31T23:11:17.516Z" }, + { url = "https://files.pythonhosted.org/packages/09/f0/817d03a03f93ba9c6c8993de509277d84e69f9453601915e4a69554102a1/numpy-2.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979", size = 14688322, upload-time = "2026-01-31T23:11:19.883Z" }, + { url = "https://files.pythonhosted.org/packages/da/b4/f805ab79293c728b9a99438775ce51885fd4f31b76178767cfc718701a39/numpy-2.4.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98", size = 5198157, upload-time = "2026-01-31T23:11:22.375Z" }, + { url = "https://files.pythonhosted.org/packages/74/09/826e4289844eccdcd64aac27d13b0fd3f32039915dd5b9ba01baae1f436c/numpy-2.4.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef", size = 6546330, upload-time = "2026-01-31T23:11:23.958Z" }, + { url = "https://files.pythonhosted.org/packages/19/fb/cbfdbfa3057a10aea5422c558ac57538e6acc87ec1669e666d32ac198da7/numpy-2.4.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7", size = 15660968, upload-time = "2026-01-31T23:11:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/04/dc/46066ce18d01645541f0186877377b9371b8fa8017fa8262002b4ef22612/numpy-2.4.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499", size = 16607311, upload-time = "2026-01-31T23:11:28.117Z" }, + { url = "https://files.pythonhosted.org/packages/14/d9/4b5adfc39a43fa6bf918c6d544bc60c05236cc2f6339847fc5b35e6cb5b0/numpy-2.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb", size = 17012850, upload-time = "2026-01-31T23:11:30.888Z" }, + { url = "https://files.pythonhosted.org/packages/b7/20/adb6e6adde6d0130046e6fdfb7675cc62bc2f6b7b02239a09eb58435753d/numpy-2.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7", size = 18334210, upload-time = "2026-01-31T23:11:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/78/0e/0a73b3dff26803a8c02baa76398015ea2a5434d9b8265a7898a6028c1591/numpy-2.4.2-cp313-cp313-win32.whl", hash = "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110", size = 5958199, upload-time = "2026-01-31T23:11:35.385Z" }, + { url = "https://files.pythonhosted.org/packages/43/bc/6352f343522fcb2c04dbaf94cb30cca6fd32c1a750c06ad6231b4293708c/numpy-2.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622", size = 12310848, upload-time = "2026-01-31T23:11:38.001Z" }, + { url = "https://files.pythonhosted.org/packages/6e/8d/6da186483e308da5da1cc6918ce913dcfe14ffde98e710bfeff2a6158d4e/numpy-2.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71", size = 10221082, upload-time = "2026-01-31T23:11:40.392Z" }, + { url = "https://files.pythonhosted.org/packages/25/a1/9510aa43555b44781968935c7548a8926274f815de42ad3997e9e83680dd/numpy-2.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262", size = 14815866, upload-time = "2026-01-31T23:11:42.495Z" }, + { url = "https://files.pythonhosted.org/packages/36/30/6bbb5e76631a5ae46e7923dd16ca9d3f1c93cfa8d4ed79a129814a9d8db3/numpy-2.4.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913", size = 5325631, upload-time = "2026-01-31T23:11:44.7Z" }, + { url = "https://files.pythonhosted.org/packages/46/00/3a490938800c1923b567b3a15cd17896e68052e2145d8662aaf3e1ffc58f/numpy-2.4.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab", size = 6646254, upload-time = "2026-01-31T23:11:46.341Z" }, + { url = "https://files.pythonhosted.org/packages/d3/e9/fac0890149898a9b609caa5af7455a948b544746e4b8fe7c212c8edd71f8/numpy-2.4.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82", size = 15720138, upload-time = "2026-01-31T23:11:48.082Z" }, + { url = "https://files.pythonhosted.org/packages/ea/5c/08887c54e68e1e28df53709f1893ce92932cc6f01f7c3d4dc952f61ffd4e/numpy-2.4.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f", size = 16655398, upload-time = "2026-01-31T23:11:50.293Z" }, + { url = "https://files.pythonhosted.org/packages/4d/89/253db0fa0e66e9129c745e4ef25631dc37d5f1314dad2b53e907b8538e6d/numpy-2.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554", size = 17079064, upload-time = "2026-01-31T23:11:52.927Z" }, + { url = "https://files.pythonhosted.org/packages/2a/d5/cbade46ce97c59c6c3da525e8d95b7abe8a42974a1dc5c1d489c10433e88/numpy-2.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257", size = 18379680, upload-time = "2026-01-31T23:11:55.22Z" }, + { url = "https://files.pythonhosted.org/packages/40/62/48f99ae172a4b63d981babe683685030e8a3df4f246c893ea5c6ef99f018/numpy-2.4.2-cp313-cp313t-win32.whl", hash = "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657", size = 6082433, upload-time = "2026-01-31T23:11:58.096Z" }, + { url = "https://files.pythonhosted.org/packages/07/38/e054a61cfe48ad9f1ed0d188e78b7e26859d0b60ef21cd9de4897cdb5326/numpy-2.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b", size = 12451181, upload-time = "2026-01-31T23:11:59.782Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a4/a05c3a6418575e185dd84d0b9680b6bb2e2dc3e4202f036b7b4e22d6e9dc/numpy-2.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1", size = 10290756, upload-time = "2026-01-31T23:12:02.438Z" }, + { url = "https://files.pythonhosted.org/packages/18/88/b7df6050bf18fdcfb7046286c6535cabbdd2064a3440fca3f069d319c16e/numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b", size = 16663092, upload-time = "2026-01-31T23:12:04.521Z" }, + { url = "https://files.pythonhosted.org/packages/25/7a/1fee4329abc705a469a4afe6e69b1ef7e915117747886327104a8493a955/numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000", size = 14698770, upload-time = "2026-01-31T23:12:06.96Z" }, + { url = "https://files.pythonhosted.org/packages/fb/0b/f9e49ba6c923678ad5bc38181c08ac5e53b7a5754dbca8e581aa1a56b1ff/numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1", size = 5208562, upload-time = "2026-01-31T23:12:09.632Z" }, + { url = "https://files.pythonhosted.org/packages/7d/12/d7de8f6f53f9bb76997e5e4c069eda2051e3fe134e9181671c4391677bb2/numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74", size = 6543710, upload-time = "2026-01-31T23:12:11.969Z" }, + { url = "https://files.pythonhosted.org/packages/09/63/c66418c2e0268a31a4cf8a8b512685748200f8e8e8ec6c507ce14e773529/numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a", size = 15677205, upload-time = "2026-01-31T23:12:14.33Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6c/7f237821c9642fb2a04d2f1e88b4295677144ca93285fd76eff3bcba858d/numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325", size = 16611738, upload-time = "2026-01-31T23:12:16.525Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a7/39c4cdda9f019b609b5c473899d87abff092fc908cfe4d1ecb2fcff453b0/numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909", size = 17028888, upload-time = "2026-01-31T23:12:19.306Z" }, + { url = "https://files.pythonhosted.org/packages/da/b3/e84bb64bdfea967cc10950d71090ec2d84b49bc691df0025dddb7c26e8e3/numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a", size = 18339556, upload-time = "2026-01-31T23:12:21.816Z" }, + { url = "https://files.pythonhosted.org/packages/88/f5/954a291bc1192a27081706862ac62bb5920fbecfbaa302f64682aa90beed/numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a", size = 6006899, upload-time = "2026-01-31T23:12:24.14Z" }, + { url = "https://files.pythonhosted.org/packages/05/cb/eff72a91b2efdd1bc98b3b8759f6a1654aa87612fc86e3d87d6fe4f948c4/numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75", size = 12443072, upload-time = "2026-01-31T23:12:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/37/75/62726948db36a56428fce4ba80a115716dc4fad6a3a4352487f8bb950966/numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05", size = 10494886, upload-time = "2026-01-31T23:12:28.488Z" }, + { url = "https://files.pythonhosted.org/packages/36/2f/ee93744f1e0661dc267e4b21940870cabfae187c092e1433b77b09b50ac4/numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308", size = 14818567, upload-time = "2026-01-31T23:12:30.709Z" }, + { url = "https://files.pythonhosted.org/packages/a7/24/6535212add7d76ff938d8bdc654f53f88d35cddedf807a599e180dcb8e66/numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef", size = 5328372, upload-time = "2026-01-31T23:12:32.962Z" }, + { url = "https://files.pythonhosted.org/packages/5e/9d/c48f0a035725f925634bf6b8994253b43f2047f6778a54147d7e213bc5a7/numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d", size = 6649306, upload-time = "2026-01-31T23:12:34.797Z" }, + { url = "https://files.pythonhosted.org/packages/81/05/7c73a9574cd4a53a25907bad38b59ac83919c0ddc8234ec157f344d57d9a/numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8", size = 15722394, upload-time = "2026-01-31T23:12:36.565Z" }, + { url = "https://files.pythonhosted.org/packages/35/fa/4de10089f21fc7d18442c4a767ab156b25c2a6eaf187c0db6d9ecdaeb43f/numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5", size = 16653343, upload-time = "2026-01-31T23:12:39.188Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f9/d33e4ffc857f3763a57aa85650f2e82486832d7492280ac21ba9efda80da/numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e", size = 17078045, upload-time = "2026-01-31T23:12:42.041Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b8/54bdb43b6225badbea6389fa038c4ef868c44f5890f95dd530a218706da3/numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a", size = 18380024, upload-time = "2026-01-31T23:12:44.331Z" }, + { url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" }, + { url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" }, + { url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f8/50e14d36d915ef64d8f8bc4a087fc8264d82c785eda6711f80ab7e620335/numpy-2.4.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:89f7268c009bc492f506abd6f5265defa7cb3f7487dc21d357c3d290add45082", size = 16833179, upload-time = "2026-01-31T23:12:53.5Z" }, + { url = "https://files.pythonhosted.org/packages/17/17/809b5cad63812058a8189e91a1e2d55a5a18fd04611dbad244e8aeae465c/numpy-2.4.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6dee3bb76aa4009d5a912180bf5b2de012532998d094acee25d9cb8dee3e44a", size = 14889755, upload-time = "2026-01-31T23:12:55.933Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ea/181b9bcf7627fc8371720316c24db888dcb9829b1c0270abf3d288b2e29b/numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:cd2bd2bbed13e213d6b55dc1d035a4f91748a7d3edc9480c13898b0353708920", size = 5399500, upload-time = "2026-01-31T23:12:58.671Z" }, + { url = "https://files.pythonhosted.org/packages/33/9f/413adf3fc955541ff5536b78fcf0754680b3c6d95103230252a2c9408d23/numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:cf28c0c1d4c4bf00f509fa7eb02c58d7caf221b50b467bcb0d9bbf1584d5c821", size = 6714252, upload-time = "2026-01-31T23:13:00.518Z" }, + { url = "https://files.pythonhosted.org/packages/91/da/643aad274e29ccbdf42ecd94dafe524b81c87bcb56b83872d54827f10543/numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e04ae107ac591763a47398bb45b568fc38f02dbc4aa44c063f67a131f99346cb", size = 15797142, upload-time = "2026-01-31T23:13:02.219Z" }, + { url = "https://files.pythonhosted.org/packages/66/27/965b8525e9cb5dc16481b30a1b3c21e50c7ebf6e9dbd48d0c4d0d5089c7e/numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:602f65afdef699cda27ec0b9224ae5dc43e328f4c24c689deaf77133dbee74d0", size = 16727979, upload-time = "2026-01-31T23:13:04.62Z" }, + { url = "https://files.pythonhosted.org/packages/de/e5/b7d20451657664b07986c2f6e3be564433f5dcaf3482d68eaecd79afaf03/numpy-2.4.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be71bf1edb48ebbbf7f6337b5bfd2f895d1902f6335a5830b20141fc126ffba0", size = 12502577, upload-time = "2026-01-31T23:13:07.08Z" }, ] [[package]] @@ -4515,7 +4515,7 @@ resolution-markers = [ "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] @@ -4576,7 +4576,7 @@ version = "2.3.3.260113" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "types-pytz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/92/5d/be23854a73fda69f1dbdda7bc10fbd6f930bd1fa87aaec389f00c901c1e8/pandas_stubs-2.3.3.260113.tar.gz", hash = "sha256:076e3724bcaa73de78932b012ec64b3010463d377fa63116f4e6850643d93800", size = 116131, upload-time = "2026-01-13T22:30:16.704Z" } @@ -4629,7 +4629,7 @@ version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/25/6c/6d8b4b03b958c02fa8687ec6063c49d952a189f8c91ebbe51e877dfab8f7/pgvector-0.4.2.tar.gz", hash = "sha256:322cac0c1dc5d41c9ecf782bd9991b7966685dee3a00bc873631391ed949513a", size = 31354, upload-time = "2025-12-05T01:07:17.87Z" } wheels = [ @@ -5955,21 +5955,21 @@ wheels = [ [[package]] name = "rich" -version = "14.3.1" +version = "14.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/84/4831f881aa6ff3c976f6d6809b58cdfa350593ffc0dc3c58f5f6586780fb/rich-14.3.1.tar.gz", hash = "sha256:b8c5f568a3a749f9290ec6bddedf835cec33696bfc1e48bcfecb276c7386e4b8", size = 230125, upload-time = "2026-01-24T21:40:44.847Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/2a/a1810c8627b9ec8c57ec5ec325d306701ae7be50235e8fd81266e002a3cc/rich-14.3.1-py3-none-any.whl", hash = "sha256:da750b1aebbff0b372557426fb3f35ba56de8ef954b3190315eb64076d6fb54e", size = 309952, upload-time = "2026-01-24T21:40:42.969Z" }, + { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" }, ] [[package]] name = "rich-click" -version = "1.9.6" +version = "1.9.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -5977,9 +5977,9 @@ dependencies = [ { name = "rich" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4b/50/1497dbc52297d6759451bf5a991e9b2d0a122a5d33ac8cd057f81cb9910a/rich_click-1.9.6.tar.gz", hash = "sha256:463bd3dbef54a812282bfa93dde80c471bce359823fc1301be368eab63391cb2", size = 74777, upload-time = "2026-01-22T02:43:58.374Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/27/091e140ea834272188e63f8dd6faac1f5c687582b687197b3e0ec3c78ebf/rich_click-1.9.7.tar.gz", hash = "sha256:022997c1e30731995bdbc8ec2f82819340d42543237f033a003c7b1f843fc5dc", size = 74838, upload-time = "2026-01-31T04:29:27.707Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/87/508930def644be9fb86fec63520151921061c152289b98798017a498d678/rich_click-1.9.6-py3-none-any.whl", hash = "sha256:e78d71e3f73a55548e573ccfd964e18503936e2e736a4a1f74c6c29479a2a054", size = 71430, upload-time = "2026-01-22T02:43:56.939Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e5/d708d262b600a352abe01c2ae360d8ff75b0af819b78e9af293191d928e6/rich_click-1.9.7-py3-none-any.whl", hash = "sha256:2f99120fca78f536e07b114d3b60333bc4bb2a0969053b1250869bcdc1b5351b", size = 71491, upload-time = "2026-01-31T04:29:26.777Z" }, ] [[package]] @@ -8009,11 +8009,11 @@ wheels = [ [[package]] name = "wcwidth" -version = "0.5.2" +version = "0.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5f/3e/3d456efe55d2d5e7938b5f9abd68333dd8dceb14e829f51f9a8deed2217e/wcwidth-0.5.2.tar.gz", hash = "sha256:c022c39a02a0134d1e10810da36d1f984c79648181efcc70a389f4569695f5ae", size = 152817, upload-time = "2026-01-29T19:32:52.22Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/62/a7c072fbfefb2980a00f99ca994279cb9ecf310cb2e6b2a4d2a28fe192b3/wcwidth-0.5.3.tar.gz", hash = "sha256:53123b7af053c74e9fe2e92ac810301f6139e64379031f7124574212fb3b4091", size = 157587, upload-time = "2026-01-31T03:52:10.92Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/72/da5a6f511a8267f962a08637464a70409736ac72f9f75b069e0e96d69b64/wcwidth-0.5.2-py3-none-any.whl", hash = "sha256:46912178a64217749bf3426b21e36e849fbc46e05c949407b3e364d9f7ffcadf", size = 90088, upload-time = "2026-01-29T19:32:50.592Z" }, + { url = "https://files.pythonhosted.org/packages/3c/c1/d73f12f8cdb1891334a2ccf7389eed244d3941e74d80dd220badb937f3fb/wcwidth-0.5.3-py3-none-any.whl", hash = "sha256:d584eff31cd4753e1e5ff6c12e1edfdb324c995713f75d26c29807bb84bf649e", size = 92981, upload-time = "2026-01-31T03:52:09.14Z" }, ] [[package]] From ab8938b53382bd35b1fad658c8e83ac0de82abf0 Mon Sep 17 00:00:00 2001 From: euri10 Date: Mon, 2 Feb 2026 11:58:24 +0100 Subject: [PATCH 02/11] some typing and use execute_many --- scripts/bench.py | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index d67acc95..eec4b66c 100644 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -5,9 +5,9 @@ import tempfile from pathlib import Path from dataclasses import dataclass -from typing import Callable, Dict, List, Tuple +from typing import Any, Callable -from sqlspec import SQLSpec +from sqlspec import SQLSpec, SyncDatabaseConfig from sqlalchemy import create_engine, text # ========================== @@ -79,7 +79,7 @@ def sqlite_connect(db, wal=False): def sqlite_raw_session(db, wal): return lambda: sqlite_connect(db, wal).close() -def sqlite_populate(conn, rows): +def sqlite_populate(conn: sqlite3.Connection, rows): cur = conn.cursor() cur.execute("drop table if exists notes") cur.execute("create table notes (id integer primary key, body text)") @@ -110,35 +110,22 @@ def fn(): conn.close() return fn -def make_sqlite_driver(args): - from sqlspec.adapters.sqlite import SqliteConfig - - return Driver( - name="sqlite", - raw_session=lambda db: sqlite_raw_session(db, args.wal), - raw_write=lambda db: sqlite_raw_write(db, args.rows, args.wal), - raw_read=lambda db: sqlite_raw_read(db, args.rows, args.reads, args.wal), - sqlspec_config=lambda spec, db: spec.add_config( - SqliteConfig(connection_config={"database": db}) - ), - sqlalchemy_url=lambda db: f"sqlite:///{db}", - ) # ========================== # SQLSPEC BENCHES # ========================== -def sqlspec_session(spec, cfg): +def sqlspec_session(spec: SQLSpec, cfg: SyncDatabaseConfig[Any, Any, Any]): def fn(): with spec.provide_session(cfg): pass return fn -def sqlspec_populate(spec, cfg, rows): +def sqlspec_populate(spec: SQLSpec, cfg: SyncDatabaseConfig[Any,Any,Any], rows): with spec.provide_session(cfg) as s: s.execute("drop table if exists notes") s.execute("create table notes (id integer primary key, body text)") - s.execute( + s.execute_many( "insert into notes (body) values (?)", [(f"note {i}",) for i in range(rows)], ) @@ -236,6 +223,24 @@ def run_driver(driver: Driver, db: str, args): f"sqlalchemy {slowdown(t0, t2):.2f}x" ) +# ========================== +# SQLITE DRIVER FACTORY +# ========================== +def make_sqlite_driver(args): + from sqlspec.adapters.sqlite import SqliteConfig + + return Driver( + name="sqlite", + raw_session=lambda db: sqlite_raw_session(db, args.wal), + raw_write=lambda db: sqlite_raw_write(db, args.rows, args.wal), + raw_read=lambda db: sqlite_raw_read(db, args.rows, args.reads, args.wal), + sqlspec_config=lambda spec, db: spec.add_config( + SqliteConfig(connection_config={"database": db}) + ), + sqlalchemy_url=lambda db: f"sqlite:///{db}", + ) + + # ========================== # MAIN # ========================== @@ -249,7 +254,7 @@ def main(): f"DB={'memory' if args.memory else 'file'}" ) - drivers: Dict[str, Callable] = { + drivers: dict[str, Callable[Any, Any]] = { "sqlite": lambda: make_sqlite_driver(args), } From 2065e408c30d836ae97da2e732c8a2f3e8f5bc05 Mon Sep 17 00:00:00 2001 From: euri10 Date: Mon, 2 Feb 2026 12:54:46 +0100 Subject: [PATCH 03/11] placeholder bench --- scripts/bench.py | 337 +++++++++++------------------------------------ sqlspec/cli.py | 2 + 2 files changed, 78 insertions(+), 261 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index eec4b66c..ccf29d31 100644 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -1,275 +1,90 @@ -#!/usr/bin/env python3 -import argparse -import time -import sqlite3 -import tempfile -from pathlib import Path -from dataclasses import dataclass -from typing import Any, Callable - -from sqlspec import SQLSpec, SyncDatabaseConfig -from sqlalchemy import create_engine, text - -# ========================== -# CLI -# ========================== - -def parse_args(): - p = argparse.ArgumentParser() - p.add_argument("--rows", type=int, default=10_000) - p.add_argument("--reads", type=int, default=5_000) - p.add_argument( - "--drivers", - nargs="+", - default=["sqlite"], - help="Drivers to test (sqlite, duckdb, ...)", - ) - p.add_argument( - "--modes", - nargs="+", - default=["session", "write", "read"], - choices=["session", "write", "read"], - ) - p.add_argument("--wal", action="store_true") - p.add_argument("--memory", action="store_true") - return p.parse_args() - -# ========================== -# UTIL -# ========================== - -def now(): - return time.perf_counter() - -def run(label: str, fn: Callable[[], None]) -> float: - fn() # warmup - t0 = now() - fn() - dt = now() - t0 - print(f"{label:<30} {dt:.5f}s") - return dt - -def slowdown(base: float, other: float) -> float: - return other / base if base else float("inf") - -# ========================== -# DRIVER REGISTRY -# ========================== - -@dataclass -class Driver: - name: str - raw_session: Callable[[str], Callable[[], None]] - raw_write: Callable[[str], Callable[[], None]] - raw_read: Callable[[str], Callable[[], None]] - - sqlspec_config: Callable[[SQLSpec, str], object] - sqlalchemy_url: Callable[[str], str] +#!/usr/bin/env python -# ========================== -# SQLITE IMPLEMENTATION -# ========================== - -def sqlite_connect(db, wal=False): - conn = sqlite3.connect(db) - if wal: - conn.execute("pragma journal_mode=wal") - return conn - -def sqlite_raw_session(db, wal): - return lambda: sqlite_connect(db, wal).close() - -def sqlite_populate(conn: sqlite3.Connection, rows): - cur = conn.cursor() - cur.execute("drop table if exists notes") - cur.execute("create table notes (id integer primary key, body text)") - cur.executemany( - "insert into notes (body) values (?)", - [(f"note {i}",) for i in range(rows)], - ) - conn.commit() - -def sqlite_raw_write(db, rows, wal): - def fn(): - conn = sqlite_connect(db, wal) - sqlite_populate(conn, rows) - conn.close() - return fn - -def sqlite_raw_read(db, rows, reads, wal): - def fn(): - conn = sqlite_connect(db, wal) - sqlite_populate(conn, rows) - cur = conn.cursor() - for i in range(reads): - cur.execute( - "select body from notes where id = ?", - ((i % rows) + 1,), - ) - cur.fetchone() - conn.close() - return fn - - -# ========================== -# SQLSPEC BENCHES -# ========================== - -def sqlspec_session(spec: SQLSpec, cfg: SyncDatabaseConfig[Any, Any, Any]): - def fn(): - with spec.provide_session(cfg): - pass - return fn - -def sqlspec_populate(spec: SQLSpec, cfg: SyncDatabaseConfig[Any,Any,Any], rows): - with spec.provide_session(cfg) as s: - s.execute("drop table if exists notes") - s.execute("create table notes (id integer primary key, body text)") - s.execute_many( - "insert into notes (body) values (?)", - [(f"note {i}",) for i in range(rows)], - ) -def sqlspec_write(spec, cfg, rows): - return lambda: sqlspec_populate(spec, cfg, rows) - -def sqlspec_read(spec, cfg, rows, reads): - def fn(): - sqlspec_populate(spec, cfg, rows) - with spec.provide_session(cfg) as s: - for i in range(reads): - s.execute( - "select body from notes where id = ?", - ((i % rows) + 1,), - ).one() - return fn - -# ========================== -# SQLALCHEMY BENCHES -# ========================== - -def sa_session(url): - engine = create_engine(url, future=True) - return lambda: engine.connect().close() - -def sa_populate(conn, rows): - conn.execute(text("drop table if exists notes")) - conn.execute(text("create table notes (id integer primary key, body text)")) - conn.execute( - text("insert into notes (body) values (:body)"), - [{"body": f"note {i}"} for i in range(rows)], - ) - -def sa_write(url, rows): - engine = create_engine(url, future=True) - def fn(): - with engine.begin() as conn: - sa_populate(conn, rows) - return fn - -def sa_read(url, rows, reads): - engine = create_engine(url, future=True) - def fn(): - with engine.begin() as conn: - sa_populate(conn, rows) - for i in range(reads): - conn.execute( - text("select body from notes where id = :id"), - {"id": (i % rows) + 1}, - ).fetchone() - return fn - -# ========================== -# RUNNER -# ========================== - -def run_driver(driver: Driver, db: str, args): - print(f"\n--- DRIVER: {driver.name} ---") - - spec = SQLSpec() - cfg = driver.sqlspec_config(spec, db) - sa_url = driver.sqlalchemy_url(db) - - if "session" in args.modes: - print("\nSession / connection cost") - t0 = run("raw", driver.raw_session(db)) - t1 = run("sqlspec", sqlspec_session(spec, cfg)) - t2 = run("sqlalchemy", sa_session(sa_url)) - print( - f"Slowdowns vs raw: " - f"sqlspec {slowdown(t0, t1):.2f}x, " - f"sqlalchemy {slowdown(t0, t2):.2f}x" - ) - - if "write" in args.modes: - print("\nWrite-heavy workload") - t0 = run("raw", driver.raw_write(db)) - t1 = run("sqlspec", sqlspec_write(spec, cfg, args.rows)) - t2 = run("sqlalchemy", sa_write(sa_url, args.rows)) - print( - f"Slowdowns vs raw: " - f"sqlspec {slowdown(t0, t1):.2f}x, " - f"sqlalchemy {slowdown(t0, t2):.2f}x" - ) - - if "read" in args.modes: - print("\nRead-heavy workload") - t0 = run("raw", driver.raw_read(db)) - t1 = run("sqlspec", sqlspec_read(spec, cfg, args.rows, args.reads)) - t2 = run("sqlalchemy", sa_read(sa_url, args.rows, args.reads)) - print( - f"Slowdowns vs raw: " - f"sqlspec {slowdown(t0, t1):.2f}x, " - f"sqlalchemy {slowdown(t0, t2):.2f}x" +import time +import click + +@click.command() +@click.option( + "--driver", + multiple=True, + default=["sqlite"], + show_default=True, + help="List of driver names to benchmark (default: sqlite)", +) +def main(driver: tuple[str, ...]) -> None: + """Run benchmarks for the specified drivers.""" + results = [] + for drv in driver: + click.echo(f"Running benchmark for driver: {drv}") + results.extend(run_benchmark(drv)) + if results: + print_benchmark_table(results) + else: + click.echo("No benchmark results to display.") + click.echo(f"Benchmarks complete for drivers: {', '.join(driver)}") + + + +def run_benchmark(driver: str): + """Benchmark three scenarios for a given driver and multiple libraries.""" + libraries = [ + ("raw", raw_driver_scenario), + ("sqlspec", sqlspec_scenario), + ("sqlalchemy", sqlalchemy_scenario), + ] + scenarios = [ + ("initialization", "initialization"), + ("write_heavy", "write_heavy"), + ("read_heavy", "read_heavy"), + ] + results = [] + for scenario_name, scenario_func_name in scenarios: + for lib_name, lib_func in libraries: + start = time.perf_counter() + lib_func(driver, scenario_func_name) + elapsed = time.perf_counter() - start + results.append({ + "driver": driver, + "library": lib_name, + "scenario": scenario_name, + "time": elapsed, + }) + return results + +def print_benchmark_table(results): + from rich.console import Console + from rich.table import Table + console = Console() + table = Table(title="Benchmark Results") + table.add_column("Driver", style="cyan", no_wrap=True) + table.add_column("Library", style="magenta") + table.add_column("Scenario", style="green") + table.add_column("Time (s)", justify="right", style="yellow") + + for row in results: + table.add_row( + row["driver"], + row["library"], + row["scenario"], + f"{row['time']:.4f}" ) + console.print(table) -# ========================== -# SQLITE DRIVER FACTORY -# ========================== -def make_sqlite_driver(args): - from sqlspec.adapters.sqlite import SqliteConfig - - return Driver( - name="sqlite", - raw_session=lambda db: sqlite_raw_session(db, args.wal), - raw_write=lambda db: sqlite_raw_write(db, args.rows, args.wal), - raw_read=lambda db: sqlite_raw_read(db, args.rows, args.reads, args.wal), - sqlspec_config=lambda spec, db: spec.add_config( - SqliteConfig(connection_config={"database": db}) - ), - sqlalchemy_url=lambda db: f"sqlite:///{db}", - ) +def raw_driver_scenario(driver: str, scenario: str): + time.sleep(0.05) # Placeholder +def sqlspec_scenario(driver: str, scenario: str): + time.sleep(0.07) # Placeholder -# ========================== -# MAIN -# ========================== +def sqlalchemy_scenario(driver: str, scenario: str): + time.sleep(0.09) # Placeholder -def main(): - args = parse_args() - print( - f"ROWS={args.rows:,}, READS={args.reads:,}, " - f"WAL={'on' if args.wal else 'off'}, " - f"DB={'memory' if args.memory else 'file'}" - ) - drivers: dict[str, Callable[Any, Any]] = { - "sqlite": lambda: make_sqlite_driver(args), - } - selected = [d for d in args.drivers if d in drivers] - - if args.memory: - db = ":memory:" - for name in selected: - run_driver(drivers[name](), db, args) - else: - with tempfile.TemporaryDirectory() as d: - db = str(Path(d) / "bench.db") - for name in selected: - run_driver(drivers[name](), db, args) if __name__ == "__main__": main() + diff --git a/sqlspec/cli.py b/sqlspec/cli.py index 2940d70f..77f79485 100644 --- a/sqlspec/cli.py +++ b/sqlspec/cli.py @@ -1092,4 +1092,6 @@ def verify_memory(bind_key: str | None) -> None: # pyright: ignore[reportUnused except Exception as exc: console.print(f"[red]✗[/] {config_name}: {exc}") + return database_group + From 2fd30dcba4ca0ec2eb56e168ad9774df68505ab4 Mon Sep 17 00:00:00 2001 From: euri10 Date: Mon, 2 Feb 2026 13:10:12 +0100 Subject: [PATCH 04/11] init --- scripts/bench.py | 68 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index ccf29d31..11761817 100644 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -1,6 +1,11 @@ #!/usr/bin/env python +from sqlalchemy import text +from sqlspec import SQLSpec +from sqlspec.adapters.sqlite import SqliteConfig +import sqlite3 +import tempfile import time import click @@ -61,27 +66,70 @@ def print_benchmark_table(results): table.add_column("Library", style="magenta") table.add_column("Scenario", style="green") table.add_column("Time (s)", justify="right", style="yellow") + table.add_column("% Slower vs Raw", justify="right", style="red") + # Build a lookup for raw times: {(driver, scenario): time} + raw_times = {} for row in results: + if row["library"] == "raw": + raw_times[(row["driver"], row["scenario"])] = row["time"] + + for row in results: + driver = row["driver"] + scenario = row["scenario"] + lib = row["library"] + t = row["time"] + if lib == "raw": + percent_slower = "—" + else: + raw_time = raw_times.get((driver, scenario)) + if raw_time and raw_time > 0: + percent_slower = f"{100 * (t - raw_time) / raw_time:.1f}%" + else: + percent_slower = "n/a" table.add_row( - row["driver"], - row["library"], - row["scenario"], - f"{row['time']:.4f}" + driver, + lib, + scenario, + f"{t:.4f}", + percent_slower ) console.print(table) + + def raw_driver_scenario(driver: str, scenario: str): - time.sleep(0.05) # Placeholder + if driver == "sqlite" and scenario == "initialization": + with tempfile.NamedTemporaryFile(suffix=".db") as tmp: + conn = sqlite3.connect(tmp.name) + # create a table to simulate some initialization work + conn.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);") + conn.close() + else: + time.sleep(0.01) # Placeholder for other drivers/scenarios def sqlspec_scenario(driver: str, scenario: str): - time.sleep(0.07) # Placeholder + if driver == "sqlite" and scenario == "initialization": + with tempfile.NamedTemporaryFile(suffix=".db") as tmp: + spec = SQLSpec() + config = SqliteConfig(database=tmp.name) + with spec.provide_session(config) as session: + # create a table to simulate some initialization work + session.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);") + else: + time.sleep(0.01) # Placeholder def sqlalchemy_scenario(driver: str, scenario: str): - time.sleep(0.09) # Placeholder - - - + if driver == "sqlite" and scenario == "initialization": + from sqlalchemy import create_engine + with tempfile.NamedTemporaryFile(suffix=".db") as tmp: + engine = create_engine(f"sqlite:///{tmp.name}") + conn = engine.connect() + # create a table to simulate some initialization work + conn.execute(text("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);")) + conn.close() + else: + time.sleep(0.01) # Placeholder if __name__ == "__main__": From 56d554159f47b0ba2082ca328554e92c6704b8f3 Mon Sep 17 00:00:00 2001 From: euri10 Date: Mon, 2 Feb 2026 13:22:58 +0100 Subject: [PATCH 05/11] refactor before implementation --- scripts/bench.py | 146 ++++++++++++++++++++++++++++++----------------- 1 file changed, 93 insertions(+), 53 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index 11761817..dd2c53fd 100644 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -1,5 +1,12 @@ #!/usr/bin/env python -from sqlalchemy import text +import asyncio +from asyncpg import connect +import inspect +import os +from typing import Any +from rich.console import Console +from rich.table import Table +from sqlalchemy import create_engine, text from sqlspec import SQLSpec from sqlspec.adapters.sqlite import SqliteConfig @@ -20,46 +27,114 @@ def main(driver: tuple[str, ...]) -> None: """Run benchmarks for the specified drivers.""" results = [] + errors = [] for drv in driver: click.echo(f"Running benchmark for driver: {drv}") - results.extend(run_benchmark(drv)) + results.extend(run_benchmark(drv, errors)) if results: print_benchmark_table(results) else: click.echo("No benchmark results to display.") + if errors: + for err in errors: + click.secho(f"Error: {err}", fg="red") click.echo(f"Benchmarks complete for drivers: {', '.join(driver)}") -def run_benchmark(driver: str): - """Benchmark three scenarios for a given driver and multiple libraries.""" + + +def run_benchmark(driver: str, errors: list[str]) -> list[dict[str, Any]]: + # List of (library, driver) pairs libraries = [ - ("raw", raw_driver_scenario), - ("sqlspec", sqlspec_scenario), - ("sqlalchemy", sqlalchemy_scenario), + ("raw", driver), + ("sqlspec", driver), + ("sqlalchemy", driver), ] scenarios = [ - ("initialization", "initialization"), - ("write_heavy", "write_heavy"), - ("read_heavy", "read_heavy"), + "initialization", + "write_heavy", + "read_heavy", ] results = [] - for scenario_name, scenario_func_name in scenarios: - for lib_name, lib_func in libraries: + for scenario in scenarios: + for lib, drv in libraries: + func = SCENARIO_REGISTRY.get((lib, drv, scenario)) + if func is None: + errors.append(f"No implementation for library={lib}, driver={drv}, scenario={scenario}") + continue start = time.perf_counter() - lib_func(driver, scenario_func_name) + if inspect.iscoroutinefunction(func): + asyncio.run(func()) + else: + func() elapsed = time.perf_counter() - start results.append({ - "driver": driver, - "library": lib_name, - "scenario": scenario_name, + "driver": drv, + "library": lib, + "scenario": scenario, "time": elapsed, }) return results + +# --- Scenario helpers and registry --- + + +def do_initialization_raw_sqlite(): + with tempfile.NamedTemporaryFile(suffix=".db") as tmp: + conn = sqlite3.connect(tmp.name) + conn.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);") + conn.close() + +def do_initialization_sqlspec_sqlite(): + with tempfile.NamedTemporaryFile(suffix=".db") as tmp: + spec = SQLSpec() + config = SqliteConfig(database=tmp.name) + with spec.provide_session(config) as session: + session.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);") + +def do_initialization_sqlalchemy_sqlite(): + with tempfile.NamedTemporaryFile(suffix=".db") as tmp: + engine = create_engine(f"sqlite:///{tmp.name}") + conn = engine.connect() + conn.execute(text("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);")) + conn.close() + +async def do_initialization_asyncpg(): + dsn = os.environ.get("ASYNC_PG_DSN", "postgresql://postgres:postgres@localhost/postgres") + conn = await connect(dsn=dsn) + await conn.execute("CREATE TABLE IF NOT EXISTS test (id serial PRIMARY KEY, value text);") + await conn.close() + + +def do_write_heavy_placeholder(): + time.sleep(0.01) + +def do_read_heavy_placeholder(): + time.sleep(0.01) + +SCENARIO_REGISTRY = { + ("raw", "sqlite", "initialization"): do_initialization_raw_sqlite, + ("sqlspec", "sqlite", "initialization"): do_initialization_sqlspec_sqlite, + ("sqlalchemy", "sqlite", "initialization"): do_initialization_sqlalchemy_sqlite, + ("asyncpg", "postgres", "initialization"): do_initialization_asyncpg, + # Add more as needed... + ("raw", "sqlite", "write_heavy"): do_write_heavy_placeholder, + ("sqlspec", "sqlite", "write_heavy"): do_write_heavy_placeholder, + ("sqlalchemy", "sqlite", "write_heavy"): do_write_heavy_placeholder, + ("asyncpg", "postgres", "write_heavy"): do_write_heavy_placeholder, + ("raw", "sqlite", "read_heavy"): do_read_heavy_placeholder, + ("sqlspec", "sqlite", "read_heavy"): do_read_heavy_placeholder, + ("sqlalchemy", "sqlite", "read_heavy"): do_read_heavy_placeholder, + ("asyncpg", "postgres", "read_heavy"): do_read_heavy_placeholder, +} + +def fallback_scenario(): + time.sleep(0.01) + + def print_benchmark_table(results): - from rich.console import Console - from rich.table import Table console = Console() table = Table(title="Benchmark Results") table.add_column("Driver", style="cyan", no_wrap=True) @@ -97,41 +172,6 @@ def print_benchmark_table(results): console.print(table) - -def raw_driver_scenario(driver: str, scenario: str): - if driver == "sqlite" and scenario == "initialization": - with tempfile.NamedTemporaryFile(suffix=".db") as tmp: - conn = sqlite3.connect(tmp.name) - # create a table to simulate some initialization work - conn.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);") - conn.close() - else: - time.sleep(0.01) # Placeholder for other drivers/scenarios - -def sqlspec_scenario(driver: str, scenario: str): - if driver == "sqlite" and scenario == "initialization": - with tempfile.NamedTemporaryFile(suffix=".db") as tmp: - spec = SQLSpec() - config = SqliteConfig(database=tmp.name) - with spec.provide_session(config) as session: - # create a table to simulate some initialization work - session.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);") - else: - time.sleep(0.01) # Placeholder - -def sqlalchemy_scenario(driver: str, scenario: str): - if driver == "sqlite" and scenario == "initialization": - from sqlalchemy import create_engine - with tempfile.NamedTemporaryFile(suffix=".db") as tmp: - engine = create_engine(f"sqlite:///{tmp.name}") - conn = engine.connect() - # create a table to simulate some initialization work - conn.execute(text("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);")) - conn.close() - else: - time.sleep(0.01) # Placeholder - - if __name__ == "__main__": main() From dd2f765ace838449633af16586955aacf641872f Mon Sep 17 00:00:00 2001 From: euri10 Date: Mon, 2 Feb 2026 13:23:42 +0100 Subject: [PATCH 06/11] unneeded --- scripts/bench.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index dd2c53fd..c47c661a 100644 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -130,9 +130,6 @@ def do_read_heavy_placeholder(): ("asyncpg", "postgres", "read_heavy"): do_read_heavy_placeholder, } -def fallback_scenario(): - time.sleep(0.01) - def print_benchmark_table(results): console = Console() From 5a21ec76db49bad3720bf124d155076a2c85dbee Mon Sep 17 00:00:00 2001 From: euri10 Date: Mon, 2 Feb 2026 13:40:48 +0100 Subject: [PATCH 07/11] better structure and naming --- scripts/bench.py | 62 +++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index c47c661a..a762ece5 100644 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -79,58 +79,72 @@ def run_benchmark(driver: str, errors: list[str]) -> list[dict[str, Any]]: # --- Scenario helpers and registry --- - - -def do_initialization_raw_sqlite(): +def raw_sqlite_initialization(): with tempfile.NamedTemporaryFile(suffix=".db") as tmp: conn = sqlite3.connect(tmp.name) conn.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);") conn.close() -def do_initialization_sqlspec_sqlite(): +def raw_sqlite_write_heavy(): + ... + +def raw_sqlite_read_heavy(): + ... + +def sqlspec_sqlite_initialization(): with tempfile.NamedTemporaryFile(suffix=".db") as tmp: spec = SQLSpec() config = SqliteConfig(database=tmp.name) with spec.provide_session(config) as session: session.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);") -def do_initialization_sqlalchemy_sqlite(): +def sqlspec_sqlite_write_heavy(): + ... + +def sqlspec_sqlite_read_heavy(): + ... + +def sqlalchemy_sqlite_initialization(): with tempfile.NamedTemporaryFile(suffix=".db") as tmp: engine = create_engine(f"sqlite:///{tmp.name}") conn = engine.connect() conn.execute(text("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);")) conn.close() -async def do_initialization_asyncpg(): +def sqlalchemy_sqlite_write_heavy(): + ... + +def sqlalchemy_sqlite_read_heavy(): + ... + +async def raw_asyncpg_initialization(): dsn = os.environ.get("ASYNC_PG_DSN", "postgresql://postgres:postgres@localhost/postgres") conn = await connect(dsn=dsn) await conn.execute("CREATE TABLE IF NOT EXISTS test (id serial PRIMARY KEY, value text);") await conn.close() -def do_write_heavy_placeholder(): - time.sleep(0.01) +async def raw_asyncpg_write_heavy(): + ... -def do_read_heavy_placeholder(): - time.sleep(0.01) +async def raw_asyncpg_read_heavy(): + ... SCENARIO_REGISTRY = { - ("raw", "sqlite", "initialization"): do_initialization_raw_sqlite, - ("sqlspec", "sqlite", "initialization"): do_initialization_sqlspec_sqlite, - ("sqlalchemy", "sqlite", "initialization"): do_initialization_sqlalchemy_sqlite, - ("asyncpg", "postgres", "initialization"): do_initialization_asyncpg, - # Add more as needed... - ("raw", "sqlite", "write_heavy"): do_write_heavy_placeholder, - ("sqlspec", "sqlite", "write_heavy"): do_write_heavy_placeholder, - ("sqlalchemy", "sqlite", "write_heavy"): do_write_heavy_placeholder, - ("asyncpg", "postgres", "write_heavy"): do_write_heavy_placeholder, - ("raw", "sqlite", "read_heavy"): do_read_heavy_placeholder, - ("sqlspec", "sqlite", "read_heavy"): do_read_heavy_placeholder, - ("sqlalchemy", "sqlite", "read_heavy"): do_read_heavy_placeholder, - ("asyncpg", "postgres", "read_heavy"): do_read_heavy_placeholder, + ("raw", "sqlite", "initialization"): raw_sqlite_initialization, + ("raw", "sqlite", "write_heavy"): raw_sqlite_write_heavy, + ("raw", "sqlite", "read_heavy"): raw_sqlite_read_heavy, + ("sqlspec", "sqlite", "initialization"): sqlspec_sqlite_initialization, + ("sqlspec", "sqlite", "write_heavy"): sqlspec_sqlite_write_heavy, + ("sqlspec", "sqlite", "read_heavy"): sqlspec_sqlite_read_heavy, + ("sqlalchemy", "sqlite", "initialization"): sqlalchemy_sqlite_initialization, + ("sqlalchemy", "sqlite", "write_heavy"): sqlalchemy_sqlite_write_heavy, + ("sqlalchemy", "sqlite", "read_heavy"): sqlalchemy_sqlite_read_heavy, + ("raw", "asyncpg", "initialization"): raw_asyncpg_initialization, + ("raw", "asyncpg", "write_heavy"): raw_asyncpg_write_heavy, + ("raw", "asyncpg", "read_heavy"): raw_asyncpg_read_heavy, } - def print_benchmark_table(results): console = Console() table = Table(title="Benchmark Results") From 5e22c5d3a274bf5458d2d8ef33c5ecd2ee3e0e41 Mon Sep 17 00:00:00 2001 From: euri10 Date: Mon, 2 Feb 2026 16:17:08 +0100 Subject: [PATCH 08/11] refactor sqlite bench --- scripts/bench.py | 142 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 125 insertions(+), 17 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index a762ece5..5f256051 100644 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -7,8 +7,10 @@ from rich.console import Console from rich.table import Table from sqlalchemy import create_engine, text -from sqlspec import SQLSpec +from sqlalchemy.ext.asyncio import create_async_engine +from sqlspec import SQLSpec from sqlspec.adapters.sqlite import SqliteConfig +from sqlspec.adapters.asyncpg import AsyncpgConfig import sqlite3 @@ -79,58 +81,157 @@ def run_benchmark(driver: str, errors: list[str]) -> list[dict[str, Any]]: # --- Scenario helpers and registry --- +# SQLite implementations +# ------------------------------ + +CREATE_TEST_TABLE = "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);" +INSERT_TEST_VALUE = "INSERT INTO test (value) VALUES (?);" +INSERT_TEST_VALUE_SQLA = "INSERT INTO test (value) VALUES (:value);" + def raw_sqlite_initialization(): with tempfile.NamedTemporaryFile(suffix=".db") as tmp: conn = sqlite3.connect(tmp.name) - conn.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);") + conn.execute(CREATE_TEST_TABLE) conn.close() def raw_sqlite_write_heavy(): - ... + with tempfile.NamedTemporaryFile(suffix=".db") as tmp: + conn = sqlite3.connect(tmp.name) + conn.execute(CREATE_TEST_TABLE) + for i in range(10000): + conn.execute(INSERT_TEST_VALUE, (f"value_{i}",)) + conn.commit() + conn.close() def raw_sqlite_read_heavy(): - ... + with tempfile.NamedTemporaryFile(suffix=".db") as tmp: + conn = sqlite3.connect(tmp.name) + conn.execute(CREATE_TEST_TABLE) + for i in range(10000): + conn.execute(INSERT_TEST_VALUE, (f"value_{i}",)) + conn.commit() + cursor = conn.execute("SELECT * FROM test;") + rows = cursor.fetchall() + assert len(rows) == 10000 + conn.close() def sqlspec_sqlite_initialization(): with tempfile.NamedTemporaryFile(suffix=".db") as tmp: spec = SQLSpec() config = SqliteConfig(database=tmp.name) with spec.provide_session(config) as session: - session.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);") + session.execute(CREATE_TEST_TABLE) def sqlspec_sqlite_write_heavy(): - ... + with tempfile.NamedTemporaryFile(suffix=".db") as tmp: + spec = SQLSpec() + config = SqliteConfig(database=tmp.name) + with spec.provide_session(config) as session: + session.execute(CREATE_TEST_TABLE) + for i in range(10000): + session.execute(INSERT_TEST_VALUE, f"value_{i}") def sqlspec_sqlite_read_heavy(): - ... + with tempfile.NamedTemporaryFile(suffix=".db") as tmp: + spec = SQLSpec() + config = SqliteConfig(database=tmp.name) + with spec.provide_session(config) as session: + session.execute(CREATE_TEST_TABLE) + for i in range(10000): + session.execute(INSERT_TEST_VALUE, f"value_{i}") + rows = session.fetch("SELECT * FROM test;") + assert len(rows) == 10000 def sqlalchemy_sqlite_initialization(): with tempfile.NamedTemporaryFile(suffix=".db") as tmp: engine = create_engine(f"sqlite:///{tmp.name}") conn = engine.connect() - conn.execute(text("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);")) + conn.execute(text(CREATE_TEST_TABLE)) conn.close() def sqlalchemy_sqlite_write_heavy(): - ... + with tempfile.NamedTemporaryFile(suffix=".db") as tmp: + engine = create_engine(f"sqlite:///{tmp.name}") + conn = engine.connect() + conn.execute(text(CREATE_TEST_TABLE)) + for i in range(10000): + conn.execute(text(INSERT_TEST_VALUE_SQLA), {"value": f"value_{i}"}) + conn.close() def sqlalchemy_sqlite_read_heavy(): - ... + with tempfile.NamedTemporaryFile(suffix=".db") as tmp: + engine = create_engine(f"sqlite:///{tmp.name}") + conn = engine.connect() + conn.execute(text(CREATE_TEST_TABLE)) + for i in range(10000): + conn.execute(text(INSERT_TEST_VALUE_SQLA), {"value": f"value_{i}"}) + result = conn.execute(text("SELECT * FROM test;")) + rows = result.fetchall() + assert len(rows) == 10000 + conn.close() +# Asyncpg implementations async def raw_asyncpg_initialization(): dsn = os.environ.get("ASYNC_PG_DSN", "postgresql://postgres:postgres@localhost/postgres") conn = await connect(dsn=dsn) - await conn.execute("CREATE TABLE IF NOT EXISTS test (id serial PRIMARY KEY, value text);") + await conn.execute(CREATE_TEST_TABLE) + # truncate table to ensure clean state await conn.close() - async def raw_asyncpg_write_heavy(): - ... + dsn = os.environ.get("ASYNC_PG_DSN", "postgresql://postgres:postgres@localhost/postgres") + conn = await connect(dsn=dsn) + for i in range(10000): + await conn.execute(INSERT_TEST_VALUE, f"value_{i}") + await conn.close() async def raw_asyncpg_read_heavy(): - ... + dsn = os.environ.get("ASYNC_PG_DSN", "postgresql://postgres:postgres@localhost/postgres") + conn = await connect(dsn=dsn) + rows = await conn.fetch("SELECT * FROM test;") + assert len(rows) == 20000 + await conn.close() + +async def sqlspec_asyncpg_initialization(): + sqlec = SQLSpec() + config = AsyncpgConfig(connection_config={"dsn": "postgresql://postgres:postgres@localhost/postgres"}) + async with sqlec.provide_session(config) as session: + await session.execute(CREATE_TEST_TABLE) +async def sqlspec_asyncpg_write_heavy(): + sqlec = SQLSpec() + config = AsyncpgConfig(connection_config={"dsn": "postgresql://postgres:postgres@localhost/postgres"}) + async with sqlec.provide_session(config) as session: + for i in range(10000): + await session.execute(INSERT_TEST_VALUE, f"value_{i}") + +async def sqlspec_asyncpg_read_heavy(): + sqlec = SQLSpec() + config = AsyncpgConfig(connection_config={"dsn": "postgresql://postgres:postgres@localhost/postgres"}) + async with sqlec.provide_session(config) as session: + rows = await session.fetch("SELECT * FROM test;") + print(len(rows)) + assert len(rows) == 0 + +async def sqlalchemy_asyncpg_initialization(): + engine = create_async_engine("postgresql+asyncpg://postgres:postgres@localhost/postgres") + async with engine.connect() as conn: + await conn.execute(text(CREATE_TEST_TABLE)) +async def sqlalchemy_asyncpg_write_heavy(): + engine = create_async_engine("postgresql+asyncpg://postgres:postgres@localhost/postgres") + async with engine.connect() as conn: + for i in range(10000): + await conn.execute(text(INSERT_TEST_VALUE), {"value": f"value_{i}"}) + +async def sqlalchemy_asyncpg_read_heavy(): + engine = create_async_engine("postgresql+asyncpg://postgres:postgres@localhost/postgres") + async with engine.begin() as conn: + result = await conn.execute(text("SELECT * FROM test;")) + rows = result.fetchall() + print(len(rows)) + assert len(rows) == 0 SCENARIO_REGISTRY = { + # SQLite scenarios ("raw", "sqlite", "initialization"): raw_sqlite_initialization, ("raw", "sqlite", "write_heavy"): raw_sqlite_write_heavy, ("raw", "sqlite", "read_heavy"): raw_sqlite_read_heavy, @@ -140,9 +241,16 @@ async def raw_asyncpg_read_heavy(): ("sqlalchemy", "sqlite", "initialization"): sqlalchemy_sqlite_initialization, ("sqlalchemy", "sqlite", "write_heavy"): sqlalchemy_sqlite_write_heavy, ("sqlalchemy", "sqlite", "read_heavy"): sqlalchemy_sqlite_read_heavy, - ("raw", "asyncpg", "initialization"): raw_asyncpg_initialization, - ("raw", "asyncpg", "write_heavy"): raw_asyncpg_write_heavy, - ("raw", "asyncpg", "read_heavy"): raw_asyncpg_read_heavy, + # Asyncpg scenarios + # ("raw", "asyncpg", "initialization"): raw_asyncpg_initialization, + # ("raw", "asyncpg", "write_heavy"): raw_asyncpg_write_heavy, + # ("raw", "asyncpg", "read_heavy"): raw_asyncpg_read_heavy, + # ("sqlspec", "asyncpg", "initialization"): sqlspec_asyncpg_initialization, + # ("sqlspec", "asyncpg", "write_heavy"): sqlspec_asyncpg_write_heavy, + # ("sqlspec", "asyncpg", "read_heavy"): sqlspec_asyncpg_read_heavy, + # ("sqlalchemy", "asyncpg", "initialization"): sqlalchemy_asyncpg_initialization, + # ("sqlalchemy", "asyncpg", "write_heavy"): sqlalchemy_asyncpg_write_heavy, + # ("sqlalchemy", "asyncpg", "read_heavy"): sqlalchemy_asyncpg_read_heavy, } def print_benchmark_table(results): From cd7d0a538a937583e04efcac32ff92241051e2ce Mon Sep 17 00:00:00 2001 From: euri10 Date: Mon, 2 Feb 2026 16:21:33 +0100 Subject: [PATCH 09/11] some ruff --- scripts/bench.py | 66 ++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index 5f256051..8f03184c 100644 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -1,23 +1,23 @@ -#!/usr/bin/env python import asyncio -from asyncpg import connect import inspect import os +import sqlite3 +import tempfile +import time from typing import Any + +import click +from asyncpg import connect from rich.console import Console from rich.table import Table from sqlalchemy import create_engine, text from sqlalchemy.ext.asyncio import create_async_engine -from sqlspec import SQLSpec -from sqlspec.adapters.sqlite import SqliteConfig + +from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.adapters.sqlite import SqliteConfig -import sqlite3 -import tempfile -import time -import click - @click.command() @click.option( "--driver", @@ -43,9 +43,6 @@ def main(driver: tuple[str, ...]) -> None: click.echo(f"Benchmarks complete for drivers: {', '.join(driver)}") - - - def run_benchmark(driver: str, errors: list[str]) -> list[dict[str, Any]]: # List of (library, driver) pairs libraries = [ @@ -88,13 +85,13 @@ def run_benchmark(driver: str, errors: list[str]) -> list[dict[str, Any]]: INSERT_TEST_VALUE = "INSERT INTO test (value) VALUES (?);" INSERT_TEST_VALUE_SQLA = "INSERT INTO test (value) VALUES (:value);" -def raw_sqlite_initialization(): +def raw_sqlite_initialization()-> None: with tempfile.NamedTemporaryFile(suffix=".db") as tmp: conn = sqlite3.connect(tmp.name) conn.execute(CREATE_TEST_TABLE) conn.close() -def raw_sqlite_write_heavy(): +def raw_sqlite_write_heavy()-> None: with tempfile.NamedTemporaryFile(suffix=".db") as tmp: conn = sqlite3.connect(tmp.name) conn.execute(CREATE_TEST_TABLE) @@ -103,7 +100,7 @@ def raw_sqlite_write_heavy(): conn.commit() conn.close() -def raw_sqlite_read_heavy(): +def raw_sqlite_read_heavy()-> None: with tempfile.NamedTemporaryFile(suffix=".db") as tmp: conn = sqlite3.connect(tmp.name) conn.execute(CREATE_TEST_TABLE) @@ -115,14 +112,14 @@ def raw_sqlite_read_heavy(): assert len(rows) == 10000 conn.close() -def sqlspec_sqlite_initialization(): +def sqlspec_sqlite_initialization()-> None: with tempfile.NamedTemporaryFile(suffix=".db") as tmp: spec = SQLSpec() config = SqliteConfig(database=tmp.name) with spec.provide_session(config) as session: session.execute(CREATE_TEST_TABLE) -def sqlspec_sqlite_write_heavy(): +def sqlspec_sqlite_write_heavy()-> None: with tempfile.NamedTemporaryFile(suffix=".db") as tmp: spec = SQLSpec() config = SqliteConfig(database=tmp.name) @@ -131,7 +128,7 @@ def sqlspec_sqlite_write_heavy(): for i in range(10000): session.execute(INSERT_TEST_VALUE, f"value_{i}") -def sqlspec_sqlite_read_heavy(): +def sqlspec_sqlite_read_heavy()-> None: with tempfile.NamedTemporaryFile(suffix=".db") as tmp: spec = SQLSpec() config = SqliteConfig(database=tmp.name) @@ -142,14 +139,14 @@ def sqlspec_sqlite_read_heavy(): rows = session.fetch("SELECT * FROM test;") assert len(rows) == 10000 -def sqlalchemy_sqlite_initialization(): +def sqlalchemy_sqlite_initialization()-> None: with tempfile.NamedTemporaryFile(suffix=".db") as tmp: engine = create_engine(f"sqlite:///{tmp.name}") conn = engine.connect() conn.execute(text(CREATE_TEST_TABLE)) conn.close() -def sqlalchemy_sqlite_write_heavy(): +def sqlalchemy_sqlite_write_heavy()-> None: with tempfile.NamedTemporaryFile(suffix=".db") as tmp: engine = create_engine(f"sqlite:///{tmp.name}") conn = engine.connect() @@ -158,7 +155,7 @@ def sqlalchemy_sqlite_write_heavy(): conn.execute(text(INSERT_TEST_VALUE_SQLA), {"value": f"value_{i}"}) conn.close() -def sqlalchemy_sqlite_read_heavy(): +def sqlalchemy_sqlite_read_heavy()-> None: with tempfile.NamedTemporaryFile(suffix=".db") as tmp: engine = create_engine(f"sqlite:///{tmp.name}") conn = engine.connect() @@ -171,14 +168,14 @@ def sqlalchemy_sqlite_read_heavy(): conn.close() # Asyncpg implementations -async def raw_asyncpg_initialization(): +async def raw_asyncpg_initialization()-> None: dsn = os.environ.get("ASYNC_PG_DSN", "postgresql://postgres:postgres@localhost/postgres") conn = await connect(dsn=dsn) await conn.execute(CREATE_TEST_TABLE) # truncate table to ensure clean state await conn.close() -async def raw_asyncpg_write_heavy(): +async def raw_asyncpg_write_heavy()-> None: dsn = os.environ.get("ASYNC_PG_DSN", "postgresql://postgres:postgres@localhost/postgres") conn = await connect(dsn=dsn) for i in range(10000): @@ -192,42 +189,42 @@ async def raw_asyncpg_read_heavy(): assert len(rows) == 20000 await conn.close() -async def sqlspec_asyncpg_initialization(): +async def sqlspec_asyncpg_initialization()-> None: sqlec = SQLSpec() config = AsyncpgConfig(connection_config={"dsn": "postgresql://postgres:postgres@localhost/postgres"}) async with sqlec.provide_session(config) as session: await session.execute(CREATE_TEST_TABLE) -async def sqlspec_asyncpg_write_heavy(): + +async def sqlspec_asyncpg_write_heavy()->None: sqlec = SQLSpec() config = AsyncpgConfig(connection_config={"dsn": "postgresql://postgres:postgres@localhost/postgres"}) async with sqlec.provide_session(config) as session: for i in range(10000): await session.execute(INSERT_TEST_VALUE, f"value_{i}") -async def sqlspec_asyncpg_read_heavy(): +async def sqlspec_asyncpg_read_heavy()->None: sqlec = SQLSpec() config = AsyncpgConfig(connection_config={"dsn": "postgresql://postgres:postgres@localhost/postgres"}) async with sqlec.provide_session(config) as session: rows = await session.fetch("SELECT * FROM test;") - print(len(rows)) assert len(rows) == 0 -async def sqlalchemy_asyncpg_initialization(): +async def sqlalchemy_asyncpg_initialization()->None: engine = create_async_engine("postgresql+asyncpg://postgres:postgres@localhost/postgres") async with engine.connect() as conn: await conn.execute(text(CREATE_TEST_TABLE)) -async def sqlalchemy_asyncpg_write_heavy(): + +async def sqlalchemy_asyncpg_write_heavy() -> None: engine = create_async_engine("postgresql+asyncpg://postgres:postgres@localhost/postgres") async with engine.connect() as conn: for i in range(10000): await conn.execute(text(INSERT_TEST_VALUE), {"value": f"value_{i}"}) -async def sqlalchemy_asyncpg_read_heavy(): +async def sqlalchemy_asyncpg_read_heavy()-> None: engine = create_async_engine("postgresql+asyncpg://postgres:postgres@localhost/postgres") async with engine.begin() as conn: result = await conn.execute(text("SELECT * FROM test;")) rows = result.fetchall() - print(len(rows)) assert len(rows) == 0 SCENARIO_REGISTRY = { @@ -253,7 +250,7 @@ async def sqlalchemy_asyncpg_read_heavy(): # ("sqlalchemy", "asyncpg", "read_heavy"): sqlalchemy_asyncpg_read_heavy, } -def print_benchmark_table(results): +def print_benchmark_table(results: list[dict[str, Any]]) -> None: console = Console() table = Table(title="Benchmark Results") table.add_column("Driver", style="cyan", no_wrap=True) @@ -277,10 +274,7 @@ def print_benchmark_table(results): percent_slower = "—" else: raw_time = raw_times.get((driver, scenario)) - if raw_time and raw_time > 0: - percent_slower = f"{100 * (t - raw_time) / raw_time:.1f}%" - else: - percent_slower = "n/a" + percent_slower = f"{100 * (t - raw_time) / raw_time:.1f}%" if raw_time and raw_time > 0 else "n/a" table.add_row( driver, lib, From cff7b3d093819648f9a239cc97f2cc7945e3dacb Mon Sep 17 00:00:00 2001 From: euri10 Date: Mon, 2 Feb 2026 17:15:56 +0100 Subject: [PATCH 10/11] asyncpg --- scripts/bench.py | 56 ++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index 8f03184c..214f8f87 100644 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -1,6 +1,5 @@ import asyncio import inspect -import os import sqlite3 import tempfile import time @@ -81,8 +80,11 @@ def run_benchmark(driver: str, errors: list[str]) -> list[dict[str, Any]]: # SQLite implementations # ------------------------------ -CREATE_TEST_TABLE = "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT);" +CREATE_TEST_TABLE = "CREATE TABLE test (value TEXT);" +DROP_TEST_TABLE = "DROP TABLE IF EXISTS test;" INSERT_TEST_VALUE = "INSERT INTO test (value) VALUES (?);" +INSERT_TEST_VALUE_ASYNCPG = "INSERT INTO test (value) VALUES ($1);" +SELECT_TEST_VALUES = "SELECT * FROM test;" INSERT_TEST_VALUE_SQLA = "INSERT INTO test (value) VALUES (:value);" def raw_sqlite_initialization()-> None: @@ -107,7 +109,7 @@ def raw_sqlite_read_heavy()-> None: for i in range(10000): conn.execute(INSERT_TEST_VALUE, (f"value_{i}",)) conn.commit() - cursor = conn.execute("SELECT * FROM test;") + cursor = conn.execute(SELECT_TEST_VALUES) rows = cursor.fetchall() assert len(rows) == 10000 conn.close() @@ -136,7 +138,7 @@ def sqlspec_sqlite_read_heavy()-> None: session.execute(CREATE_TEST_TABLE) for i in range(10000): session.execute(INSERT_TEST_VALUE, f"value_{i}") - rows = session.fetch("SELECT * FROM test;") + rows = session.fetch(SELECT_TEST_VALUES) assert len(rows) == 10000 def sqlalchemy_sqlite_initialization()-> None: @@ -162,30 +164,28 @@ def sqlalchemy_sqlite_read_heavy()-> None: conn.execute(text(CREATE_TEST_TABLE)) for i in range(10000): conn.execute(text(INSERT_TEST_VALUE_SQLA), {"value": f"value_{i}"}) - result = conn.execute(text("SELECT * FROM test;")) + result = conn.execute(text(SELECT_TEST_VALUES)) rows = result.fetchall() assert len(rows) == 10000 conn.close() # Asyncpg implementations async def raw_asyncpg_initialization()-> None: - dsn = os.environ.get("ASYNC_PG_DSN", "postgresql://postgres:postgres@localhost/postgres") - conn = await connect(dsn=dsn) + conn = await connect(dsn="postgresql://postgres:postgres@localhost/postgres") + await conn.execute(DROP_TEST_TABLE ) await conn.execute(CREATE_TEST_TABLE) # truncate table to ensure clean state await conn.close() async def raw_asyncpg_write_heavy()-> None: - dsn = os.environ.get("ASYNC_PG_DSN", "postgresql://postgres:postgres@localhost/postgres") - conn = await connect(dsn=dsn) + conn = await connect(dsn="postgresql://postgres:postgres@localhost/postgres") for i in range(10000): - await conn.execute(INSERT_TEST_VALUE, f"value_{i}") + await conn.execute(INSERT_TEST_VALUE_ASYNCPG, f"value_{i}") await conn.close() async def raw_asyncpg_read_heavy(): - dsn = os.environ.get("ASYNC_PG_DSN", "postgresql://postgres:postgres@localhost/postgres") - conn = await connect(dsn=dsn) - rows = await conn.fetch("SELECT * FROM test;") + conn = await connect(dsn="postgresql://postgres:postgres@localhost/postgres") + rows = await conn.fetch(SELECT_TEST_VALUES) assert len(rows) == 20000 await conn.close() @@ -193,6 +193,7 @@ async def sqlspec_asyncpg_initialization()-> None: sqlec = SQLSpec() config = AsyncpgConfig(connection_config={"dsn": "postgresql://postgres:postgres@localhost/postgres"}) async with sqlec.provide_session(config) as session: + await session.execute(DROP_TEST_TABLE) await session.execute(CREATE_TEST_TABLE) async def sqlspec_asyncpg_write_heavy()->None: @@ -200,32 +201,31 @@ async def sqlspec_asyncpg_write_heavy()->None: config = AsyncpgConfig(connection_config={"dsn": "postgresql://postgres:postgres@localhost/postgres"}) async with sqlec.provide_session(config) as session: for i in range(10000): - await session.execute(INSERT_TEST_VALUE, f"value_{i}") + await session.execute(INSERT_TEST_VALUE_ASYNCPG, f"value_{i}") async def sqlspec_asyncpg_read_heavy()->None: sqlec = SQLSpec() config = AsyncpgConfig(connection_config={"dsn": "postgresql://postgres:postgres@localhost/postgres"}) async with sqlec.provide_session(config) as session: - rows = await session.fetch("SELECT * FROM test;") - assert len(rows) == 0 + rows = await session.fetch(SELECT_TEST_VALUES) async def sqlalchemy_asyncpg_initialization()->None: engine = create_async_engine("postgresql+asyncpg://postgres:postgres@localhost/postgres") async with engine.connect() as conn: + await conn.execute(text(DROP_TEST_TABLE)) await conn.execute(text(CREATE_TEST_TABLE)) async def sqlalchemy_asyncpg_write_heavy() -> None: engine = create_async_engine("postgresql+asyncpg://postgres:postgres@localhost/postgres") async with engine.connect() as conn: for i in range(10000): - await conn.execute(text(INSERT_TEST_VALUE), {"value": f"value_{i}"}) + await conn.execute(text(INSERT_TEST_VALUE_SQLA), {"value": f"value_{i}"}) async def sqlalchemy_asyncpg_read_heavy()-> None: engine = create_async_engine("postgresql+asyncpg://postgres:postgres@localhost/postgres") async with engine.begin() as conn: - result = await conn.execute(text("SELECT * FROM test;")) + result = await conn.execute(text(SELECT_TEST_VALUES)) rows = result.fetchall() - assert len(rows) == 0 SCENARIO_REGISTRY = { # SQLite scenarios @@ -239,15 +239,15 @@ async def sqlalchemy_asyncpg_read_heavy()-> None: ("sqlalchemy", "sqlite", "write_heavy"): sqlalchemy_sqlite_write_heavy, ("sqlalchemy", "sqlite", "read_heavy"): sqlalchemy_sqlite_read_heavy, # Asyncpg scenarios - # ("raw", "asyncpg", "initialization"): raw_asyncpg_initialization, - # ("raw", "asyncpg", "write_heavy"): raw_asyncpg_write_heavy, - # ("raw", "asyncpg", "read_heavy"): raw_asyncpg_read_heavy, - # ("sqlspec", "asyncpg", "initialization"): sqlspec_asyncpg_initialization, - # ("sqlspec", "asyncpg", "write_heavy"): sqlspec_asyncpg_write_heavy, - # ("sqlspec", "asyncpg", "read_heavy"): sqlspec_asyncpg_read_heavy, - # ("sqlalchemy", "asyncpg", "initialization"): sqlalchemy_asyncpg_initialization, - # ("sqlalchemy", "asyncpg", "write_heavy"): sqlalchemy_asyncpg_write_heavy, - # ("sqlalchemy", "asyncpg", "read_heavy"): sqlalchemy_asyncpg_read_heavy, + ("raw", "asyncpg", "initialization"): raw_asyncpg_initialization, + ("raw", "asyncpg", "write_heavy"): raw_asyncpg_write_heavy, + ("raw", "asyncpg", "read_heavy"): raw_asyncpg_read_heavy, + ("sqlspec", "asyncpg", "initialization"): sqlspec_asyncpg_initialization, + ("sqlspec", "asyncpg", "write_heavy"): sqlspec_asyncpg_write_heavy, + ("sqlspec", "asyncpg", "read_heavy"): sqlspec_asyncpg_read_heavy, + ("sqlalchemy", "asyncpg", "initialization"): sqlalchemy_asyncpg_initialization, + ("sqlalchemy", "asyncpg", "write_heavy"): sqlalchemy_asyncpg_write_heavy, + ("sqlalchemy", "asyncpg", "read_heavy"): sqlalchemy_asyncpg_read_heavy, } def print_benchmark_table(results: list[dict[str, Any]]) -> None: From 817ea188d82f642a2f40a157b77c64220ed253e5 Mon Sep 17 00:00:00 2001 From: euri10 Date: Mon, 2 Feb 2026 17:23:46 +0100 Subject: [PATCH 11/11] number of rows --- scripts/bench.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index 214f8f87..a9772811 100644 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -17,6 +17,8 @@ from sqlspec.adapters.sqlite import SqliteConfig +ROWS_TO_INSERT = 100_000 + @click.command() @click.option( "--driver", @@ -97,7 +99,7 @@ def raw_sqlite_write_heavy()-> None: with tempfile.NamedTemporaryFile(suffix=".db") as tmp: conn = sqlite3.connect(tmp.name) conn.execute(CREATE_TEST_TABLE) - for i in range(10000): + for i in range(ROWS_TO_INSERT): conn.execute(INSERT_TEST_VALUE, (f"value_{i}",)) conn.commit() conn.close() @@ -106,12 +108,12 @@ def raw_sqlite_read_heavy()-> None: with tempfile.NamedTemporaryFile(suffix=".db") as tmp: conn = sqlite3.connect(tmp.name) conn.execute(CREATE_TEST_TABLE) - for i in range(10000): + for i in range(ROWS_TO_INSERT): conn.execute(INSERT_TEST_VALUE, (f"value_{i}",)) conn.commit() cursor = conn.execute(SELECT_TEST_VALUES) rows = cursor.fetchall() - assert len(rows) == 10000 + assert len(rows) == ROWS_TO_INSERT conn.close() def sqlspec_sqlite_initialization()-> None: @@ -127,7 +129,7 @@ def sqlspec_sqlite_write_heavy()-> None: config = SqliteConfig(database=tmp.name) with spec.provide_session(config) as session: session.execute(CREATE_TEST_TABLE) - for i in range(10000): + for i in range(ROWS_TO_INSERT): session.execute(INSERT_TEST_VALUE, f"value_{i}") def sqlspec_sqlite_read_heavy()-> None: @@ -136,10 +138,10 @@ def sqlspec_sqlite_read_heavy()-> None: config = SqliteConfig(database=tmp.name) with spec.provide_session(config) as session: session.execute(CREATE_TEST_TABLE) - for i in range(10000): + for i in range(ROWS_TO_INSERT): session.execute(INSERT_TEST_VALUE, f"value_{i}") rows = session.fetch(SELECT_TEST_VALUES) - assert len(rows) == 10000 + assert len(rows) == ROWS_TO_INSERT def sqlalchemy_sqlite_initialization()-> None: with tempfile.NamedTemporaryFile(suffix=".db") as tmp: @@ -153,7 +155,7 @@ def sqlalchemy_sqlite_write_heavy()-> None: engine = create_engine(f"sqlite:///{tmp.name}") conn = engine.connect() conn.execute(text(CREATE_TEST_TABLE)) - for i in range(10000): + for i in range(ROWS_TO_INSERT): conn.execute(text(INSERT_TEST_VALUE_SQLA), {"value": f"value_{i}"}) conn.close() @@ -162,11 +164,11 @@ def sqlalchemy_sqlite_read_heavy()-> None: engine = create_engine(f"sqlite:///{tmp.name}") conn = engine.connect() conn.execute(text(CREATE_TEST_TABLE)) - for i in range(10000): + for i in range(ROWS_TO_INSERT): conn.execute(text(INSERT_TEST_VALUE_SQLA), {"value": f"value_{i}"}) result = conn.execute(text(SELECT_TEST_VALUES)) rows = result.fetchall() - assert len(rows) == 10000 + assert len(rows) == ROWS_TO_INSERT conn.close() # Asyncpg implementations @@ -179,14 +181,13 @@ async def raw_asyncpg_initialization()-> None: async def raw_asyncpg_write_heavy()-> None: conn = await connect(dsn="postgresql://postgres:postgres@localhost/postgres") - for i in range(10000): + for i in range(ROWS_TO_INSERT): await conn.execute(INSERT_TEST_VALUE_ASYNCPG, f"value_{i}") await conn.close() async def raw_asyncpg_read_heavy(): conn = await connect(dsn="postgresql://postgres:postgres@localhost/postgres") rows = await conn.fetch(SELECT_TEST_VALUES) - assert len(rows) == 20000 await conn.close() async def sqlspec_asyncpg_initialization()-> None: @@ -200,7 +201,7 @@ async def sqlspec_asyncpg_write_heavy()->None: sqlec = SQLSpec() config = AsyncpgConfig(connection_config={"dsn": "postgresql://postgres:postgres@localhost/postgres"}) async with sqlec.provide_session(config) as session: - for i in range(10000): + for i in range(ROWS_TO_INSERT): await session.execute(INSERT_TEST_VALUE_ASYNCPG, f"value_{i}") async def sqlspec_asyncpg_read_heavy()->None: @@ -218,7 +219,7 @@ async def sqlalchemy_asyncpg_initialization()->None: async def sqlalchemy_asyncpg_write_heavy() -> None: engine = create_async_engine("postgresql+asyncpg://postgres:postgres@localhost/postgres") async with engine.connect() as conn: - for i in range(10000): + for i in range(ROWS_TO_INSERT): await conn.execute(text(INSERT_TEST_VALUE_SQLA), {"value": f"value_{i}"}) async def sqlalchemy_asyncpg_read_heavy()-> None: