[{"data":1,"prerenderedAt":2955},["ShallowReactive",2],{"page-\u002Fasync-engines-dialects-and-connection-pooling\u002Fselecting-async-drivers-for-sqlite-mysql-and-postgres\u002Fusing-aiosqlite-for-async-tests-and-local-development\u002F":3},{"id":4,"title":5,"body":6,"description":2947,"extension":2948,"meta":2949,"navigation":97,"path":2951,"seo":2952,"stem":2953,"__hash__":2954},"content\u002Fasync-engines-dialects-and-connection-pooling\u002Fselecting-async-drivers-for-sqlite-mysql-and-postgres\u002Fusing-aiosqlite-for-async-tests-and-local-development\u002Findex.md","Using aiosqlite for Async Tests and Local Development",{"type":7,"value":8,"toc":2929},"minimark",[9,13,36,41,44,454,472,476,481,503,506,510,519,549,565,569,593,610,614,621,1065,1080,1102,1108,1144,1159,1162,1505,1509,1685,1689,1693,1696,2063,2085,2089,2092,2584,2591,2595,2598,2601,2676,2680,2708,2731,2737,2750,2893,2899,2903,2925],[10,11,5],"h1",{"id":12},"using-aiosqlite-for-async-tests-and-local-development",[14,15,16,17,21,22,25,26,29,30,35],"p",{},"Use ",[18,19,20],"code",{},"sqlite+aiosqlite:\u002F\u002F"," with ",[18,23,24],{},"poolclass=StaticPool"," and ",[18,27,28],{},"connect_args={\"check_same_thread\": False}"," to run fast, dependency-free async SQLAlchemy tests — this guide, part of the ",[31,32,34],"a",{"href":33},"\u002Fasync-engines-dialects-and-connection-pooling\u002Fselecting-async-drivers-for-sqlite-mysql-and-postgres\u002F","selecting async drivers for SQLite, MySQL, and Postgres"," reference, walks through the exact setup.",[37,38,40],"h2",{"id":39},"quick-answer","Quick Answer",[14,42,43],{},"The minimum viable async SQLite test engine paired with pytest-asyncio:",[45,46,51],"pre",{"className":47,"code":48,"language":49,"meta":50,"style":50},"language-python shiki shiki-themes github-light github-dark","# Before (sync, legacy SQLAlchemy 1.4 style)\nfrom sqlalchemy import create_engine\nfrom sqlalchemy.orm import Session, sessionmaker\n\nengine = create_engine(\"sqlite:\u002F\u002F\u002F:memory:\")\nBase.metadata.create_all(engine)\n\nSessionLocal = sessionmaker(bind=engine)\nwith SessionLocal() as session:\n    session.add(User(email=\"test@example.com\", tenant_id=1))\n    session.commit()\n\n# After (async, SQLAlchemy 2.0 + aiosqlite)\nimport asyncio\nfrom sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession\nfrom sqlalchemy.pool import StaticPool\n\nengine = create_async_engine(\n    \"sqlite+aiosqlite:\u002F\u002F\",                      # in-memory\n    connect_args={\"check_same_thread\": False},   # required for aiosqlite\n    poolclass=StaticPool,                        # share one connection across sessions\n)\n\nasync def setup_and_test():\n    async with engine.begin() as conn:\n        await conn.run_sync(Base.metadata.create_all)\n\n    factory = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)\n    async with factory() as session:\n        async with session.begin():\n            session.add(User(email=\"test@example.com\", tenant_id=1))\n\nasyncio.run(setup_and_test())\n","python","",[18,52,53,62,79,92,99,118,124,129,149,164,193,199,204,210,218,231,244,249,259,271,297,311,316,321,337,354,363,368,396,410,421,443,448],{"__ignoreMap":50},[54,55,58],"span",{"class":56,"line":57},"line",1,[54,59,61],{"class":60},"sJ8bj","# Before (sync, legacy SQLAlchemy 1.4 style)\n",[54,63,65,69,73,76],{"class":56,"line":64},2,[54,66,68],{"class":67},"szBVR","from",[54,70,72],{"class":71},"sVt8B"," sqlalchemy ",[54,74,75],{"class":67},"import",[54,77,78],{"class":71}," create_engine\n",[54,80,82,84,87,89],{"class":56,"line":81},3,[54,83,68],{"class":67},[54,85,86],{"class":71}," sqlalchemy.orm ",[54,88,75],{"class":67},[54,90,91],{"class":71}," Session, sessionmaker\n",[54,93,95],{"class":56,"line":94},4,[54,96,98],{"emptyLinePlaceholder":97},true,"\n",[54,100,102,105,108,111,115],{"class":56,"line":101},5,[54,103,104],{"class":71},"engine ",[54,106,107],{"class":67},"=",[54,109,110],{"class":71}," create_engine(",[54,112,114],{"class":113},"sZZnC","\"sqlite:\u002F\u002F\u002F:memory:\"",[54,116,117],{"class":71},")\n",[54,119,121],{"class":56,"line":120},6,[54,122,123],{"class":71},"Base.metadata.create_all(engine)\n",[54,125,127],{"class":56,"line":126},7,[54,128,98],{"emptyLinePlaceholder":97},[54,130,132,135,137,140,144,146],{"class":56,"line":131},8,[54,133,134],{"class":71},"SessionLocal ",[54,136,107],{"class":67},[54,138,139],{"class":71}," sessionmaker(",[54,141,143],{"class":142},"s4XuR","bind",[54,145,107],{"class":67},[54,147,148],{"class":71},"engine)\n",[54,150,152,155,158,161],{"class":56,"line":151},9,[54,153,154],{"class":67},"with",[54,156,157],{"class":71}," SessionLocal() ",[54,159,160],{"class":67},"as",[54,162,163],{"class":71}," session:\n",[54,165,167,170,173,175,178,181,184,186,190],{"class":56,"line":166},10,[54,168,169],{"class":71},"    session.add(User(",[54,171,172],{"class":142},"email",[54,174,107],{"class":67},[54,176,177],{"class":113},"\"test@example.com\"",[54,179,180],{"class":71},", ",[54,182,183],{"class":142},"tenant_id",[54,185,107],{"class":67},[54,187,189],{"class":188},"sj4cs","1",[54,191,192],{"class":71},"))\n",[54,194,196],{"class":56,"line":195},11,[54,197,198],{"class":71},"    session.commit()\n",[54,200,202],{"class":56,"line":201},12,[54,203,98],{"emptyLinePlaceholder":97},[54,205,207],{"class":56,"line":206},13,[54,208,209],{"class":60},"# After (async, SQLAlchemy 2.0 + aiosqlite)\n",[54,211,213,215],{"class":56,"line":212},14,[54,214,75],{"class":67},[54,216,217],{"class":71}," asyncio\n",[54,219,221,223,226,228],{"class":56,"line":220},15,[54,222,68],{"class":67},[54,224,225],{"class":71}," sqlalchemy.ext.asyncio ",[54,227,75],{"class":67},[54,229,230],{"class":71}," create_async_engine, async_sessionmaker, AsyncSession\n",[54,232,234,236,239,241],{"class":56,"line":233},16,[54,235,68],{"class":67},[54,237,238],{"class":71}," sqlalchemy.pool ",[54,240,75],{"class":67},[54,242,243],{"class":71}," StaticPool\n",[54,245,247],{"class":56,"line":246},17,[54,248,98],{"emptyLinePlaceholder":97},[54,250,252,254,256],{"class":56,"line":251},18,[54,253,104],{"class":71},[54,255,107],{"class":67},[54,257,258],{"class":71}," create_async_engine(\n",[54,260,262,265,268],{"class":56,"line":261},19,[54,263,264],{"class":113},"    \"sqlite+aiosqlite:\u002F\u002F\"",[54,266,267],{"class":71},",                      ",[54,269,270],{"class":60},"# in-memory\n",[54,272,274,277,279,282,285,288,291,294],{"class":56,"line":273},20,[54,275,276],{"class":142},"    connect_args",[54,278,107],{"class":67},[54,280,281],{"class":71},"{",[54,283,284],{"class":113},"\"check_same_thread\"",[54,286,287],{"class":71},": ",[54,289,290],{"class":188},"False",[54,292,293],{"class":71},"},   ",[54,295,296],{"class":60},"# required for aiosqlite\n",[54,298,300,303,305,308],{"class":56,"line":299},21,[54,301,302],{"class":142},"    poolclass",[54,304,107],{"class":67},[54,306,307],{"class":71},"StaticPool,                        ",[54,309,310],{"class":60},"# share one connection across sessions\n",[54,312,314],{"class":56,"line":313},22,[54,315,117],{"class":71},[54,317,319],{"class":56,"line":318},23,[54,320,98],{"emptyLinePlaceholder":97},[54,322,324,327,330,334],{"class":56,"line":323},24,[54,325,326],{"class":67},"async",[54,328,329],{"class":67}," def",[54,331,333],{"class":332},"sScJk"," setup_and_test",[54,335,336],{"class":71},"():\n",[54,338,340,343,346,349,351],{"class":56,"line":339},25,[54,341,342],{"class":67},"    async",[54,344,345],{"class":67}," with",[54,347,348],{"class":71}," engine.begin() ",[54,350,160],{"class":67},[54,352,353],{"class":71}," conn:\n",[54,355,357,360],{"class":56,"line":356},26,[54,358,359],{"class":67},"        await",[54,361,362],{"class":71}," conn.run_sync(Base.metadata.create_all)\n",[54,364,366],{"class":56,"line":365},27,[54,367,98],{"emptyLinePlaceholder":97},[54,369,371,374,376,379,382,384,387,390,392,394],{"class":56,"line":370},28,[54,372,373],{"class":71},"    factory ",[54,375,107],{"class":67},[54,377,378],{"class":71}," async_sessionmaker(engine, ",[54,380,381],{"class":142},"class_",[54,383,107],{"class":67},[54,385,386],{"class":71},"AsyncSession, ",[54,388,389],{"class":142},"expire_on_commit",[54,391,107],{"class":67},[54,393,290],{"class":188},[54,395,117],{"class":71},[54,397,399,401,403,406,408],{"class":56,"line":398},29,[54,400,342],{"class":67},[54,402,345],{"class":67},[54,404,405],{"class":71}," factory() ",[54,407,160],{"class":67},[54,409,163],{"class":71},[54,411,413,416,418],{"class":56,"line":412},30,[54,414,415],{"class":67},"        async",[54,417,345],{"class":67},[54,419,420],{"class":71}," session.begin():\n",[54,422,424,427,429,431,433,435,437,439,441],{"class":56,"line":423},31,[54,425,426],{"class":71},"            session.add(User(",[54,428,172],{"class":142},[54,430,107],{"class":67},[54,432,177],{"class":113},[54,434,180],{"class":71},[54,436,183],{"class":142},[54,438,107],{"class":67},[54,440,189],{"class":188},[54,442,192],{"class":71},[54,444,446],{"class":56,"line":445},32,[54,447,98],{"emptyLinePlaceholder":97},[54,449,451],{"class":56,"line":450},33,[54,452,453],{"class":71},"asyncio.run(setup_and_test())\n",[14,455,456,457,460,461,463,464,467,468,471],{},"The ",[18,458,459],{},"StaticPool"," is the critical piece. Without it, ",[18,462,20],{}," creates a new connection (and therefore a new, empty database) on every pool checkout. Your ",[18,465,466],{},"create_all"," runs on one connection, your test session gets a different connection, and you see ",[18,469,470],{},"OperationalError: no such table",".",[37,473,475],{"id":474},"execution-context-async-workflow-integration","Execution Context & Async Workflow Integration",[477,478,480],"h3",{"id":479},"why-aiosqlite-uses-a-background-thread","Why aiosqlite uses a background thread",[14,482,483,484,487,488,490,491,494,495,498,499,502],{},"aiosqlite is not a truly non-blocking async driver. Python's ",[18,485,486],{},"sqlite3"," module wraps a C library that performs synchronous file I\u002FO. aiosqlite's approach is to run all ",[18,489,486],{}," operations in a dedicated background thread and expose them as ",[18,492,493],{},"asyncio"," coroutines via ",[18,496,497],{},"loop.run_in_executor()",". Every ",[18,500,501],{},"await session.execute(stmt)"," dispatches work to that thread, suspends the coroutine, and resumes it when the thread signals completion.",[14,504,505],{},"For local development and test workloads, this is invisible — SQLite operations complete in microseconds. For production services where database I\u002FO latency could spike (network-attached storage, NFS-backed containers), the thread-dispatch overhead becomes measurable and the write-lock bottleneck (SQLite's database-wide write lock) dominates throughput at any meaningful concurrency level.",[477,507,509],{"id":508},"staticpool-and-shared-connection-semantics","StaticPool and shared connection semantics",[14,511,512,514,515,518],{},[18,513,459],{}," from ",[18,516,517],{},"sqlalchemy.pool"," maintains exactly one connection and hands the same connection to every checkout. This is the correct choice for in-memory SQLite because:",[520,521,522,530,536],"ol",{},[523,524,525,526,529],"li",{},"Each ",[18,527,528],{},"sqlite3.connect(\":memory:\")"," call creates a distinct database instance.",[523,531,532,535],{},[18,533,534],{},"QueuePool"," (the default) creates new connections on demand, each pointing at a different memory database.",[523,537,538,540,541,544,545,548],{},[18,539,459],{}," ensures that ",[18,542,543],{},"engine.begin()"," (used for schema creation) and subsequent ",[18,546,547],{},"async_sessionmaker"," checkouts all touch the same in-memory database.",[14,550,551,554,555,558,559,561,562,471],{},[18,552,553],{},"check_same_thread=False"," is required because aiosqlite's background thread and the asyncio thread that calls ",[18,556,557],{},"await"," are different OS threads. Without it, Python's ",[18,560,486],{}," module raises ",[18,563,564],{},"ProgrammingError: SQLite objects created in a thread can only be used in that same thread",[477,566,568],{"id":567},"installing-aiosqlite","Installing aiosqlite",[45,570,574],{"className":571,"code":572,"language":573,"meta":50,"style":50},"language-bash shiki shiki-themes github-light github-dark","pip install aiosqlite sqlalchemy[asyncio] pytest-asyncio\n","bash",[18,575,576],{"__ignoreMap":50},[54,577,578,581,584,587,590],{"class":56,"line":57},[54,579,580],{"class":332},"pip",[54,582,583],{"class":113}," install",[54,585,586],{"class":113}," aiosqlite",[54,588,589],{"class":113}," sqlalchemy[asyncio]",[54,591,592],{"class":113}," pytest-asyncio\n",[14,594,595,596,180,599,180,602,605,606,609],{},"Minimum versions: ",[18,597,598],{},"aiosqlite>=0.19.0",[18,600,601],{},"SQLAlchemy>=2.0",[18,603,604],{},"pytest-asyncio>=0.21",". Older aiosqlite versions had internal thread pool inconsistencies that caused sporadic ",[18,607,608],{},"RuntimeError: Event loop is closed"," failures in long test sessions.",[477,611,613],{"id":612},"pytest-asyncio-fixture-patterns","pytest-asyncio fixture patterns",[14,615,616,617,620],{},"The recommended fixture layout uses ",[18,618,619],{},"scope=\"session\""," for the engine and schema creation (expensive, runs once) and function-level scope for sessions with a rollback after each test:",[45,622,624],{"className":47,"code":623,"language":49,"meta":50,"style":50},"# conftest.py\nimport asyncio\nimport pytest\nimport pytest_asyncio\nfrom sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession\nfrom sqlalchemy.pool import StaticPool\n\nfrom myapp.models import Base, User, Order\n\n\nDATABASE_URL = \"sqlite+aiosqlite:\u002F\u002F\"\n\n\n@pytest.fixture(scope=\"session\")\ndef event_loop():\n    \"\"\"\n    Create a single event loop for the entire test session.\n    Required when using session-scoped async fixtures with pytest-asyncio.\n    \"\"\"\n    loop = asyncio.new_event_loop()\n    yield loop\n    loop.close()\n\n\n@pytest_asyncio.fixture(scope=\"session\")\nasync def engine():\n    \"\"\"\n    Session-scoped async engine. StaticPool keeps one connection alive\n    so all fixtures and tests see the same in-memory database.\n    \"\"\"\n    _engine = create_async_engine(\n        DATABASE_URL,\n        connect_args={\"check_same_thread\": False},\n        poolclass=StaticPool,\n    )\n    async with _engine.begin() as conn:\n        await conn.run_sync(Base.metadata.create_all)\n\n    yield _engine\n\n    async with _engine.begin() as conn:\n        await conn.run_sync(Base.metadata.drop_all)\n    await _engine.dispose()\n\n\n@pytest_asyncio.fixture\nasync def session(engine):\n    \"\"\"\n    Function-scoped session. Each test gets a fresh session;\n    all writes are rolled back after the test completes.\n    \"\"\"\n    factory = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)\n    async with factory() as _session:\n        yield _session\n        await _session.rollback()   # undo any changes made during the test\n",[18,625,626,631,637,644,651,661,671,675,687,691,695,706,710,714,732,742,747,752,757,761,771,779,784,788,792,807,818,822,827,832,836,845,853,871,882,888,902,909,914,922,927,940,948,957,962,967,973,986,991,997,1003,1008,1031,1045,1054],{"__ignoreMap":50},[54,627,628],{"class":56,"line":57},[54,629,630],{"class":60},"# conftest.py\n",[54,632,633,635],{"class":56,"line":64},[54,634,75],{"class":67},[54,636,217],{"class":71},[54,638,639,641],{"class":56,"line":81},[54,640,75],{"class":67},[54,642,643],{"class":71}," pytest\n",[54,645,646,648],{"class":56,"line":94},[54,647,75],{"class":67},[54,649,650],{"class":71}," pytest_asyncio\n",[54,652,653,655,657,659],{"class":56,"line":101},[54,654,68],{"class":67},[54,656,225],{"class":71},[54,658,75],{"class":67},[54,660,230],{"class":71},[54,662,663,665,667,669],{"class":56,"line":120},[54,664,68],{"class":67},[54,666,238],{"class":71},[54,668,75],{"class":67},[54,670,243],{"class":71},[54,672,673],{"class":56,"line":126},[54,674,98],{"emptyLinePlaceholder":97},[54,676,677,679,682,684],{"class":56,"line":131},[54,678,68],{"class":67},[54,680,681],{"class":71}," myapp.models ",[54,683,75],{"class":67},[54,685,686],{"class":71}," Base, User, Order\n",[54,688,689],{"class":56,"line":151},[54,690,98],{"emptyLinePlaceholder":97},[54,692,693],{"class":56,"line":166},[54,694,98],{"emptyLinePlaceholder":97},[54,696,697,700,703],{"class":56,"line":195},[54,698,699],{"class":188},"DATABASE_URL",[54,701,702],{"class":67}," =",[54,704,705],{"class":113}," \"sqlite+aiosqlite:\u002F\u002F\"\n",[54,707,708],{"class":56,"line":201},[54,709,98],{"emptyLinePlaceholder":97},[54,711,712],{"class":56,"line":206},[54,713,98],{"emptyLinePlaceholder":97},[54,715,716,719,722,725,727,730],{"class":56,"line":212},[54,717,718],{"class":332},"@pytest.fixture",[54,720,721],{"class":71},"(",[54,723,724],{"class":142},"scope",[54,726,107],{"class":67},[54,728,729],{"class":113},"\"session\"",[54,731,117],{"class":71},[54,733,734,737,740],{"class":56,"line":220},[54,735,736],{"class":67},"def",[54,738,739],{"class":332}," event_loop",[54,741,336],{"class":71},[54,743,744],{"class":56,"line":233},[54,745,746],{"class":113},"    \"\"\"\n",[54,748,749],{"class":56,"line":246},[54,750,751],{"class":113},"    Create a single event loop for the entire test session.\n",[54,753,754],{"class":56,"line":251},[54,755,756],{"class":113},"    Required when using session-scoped async fixtures with pytest-asyncio.\n",[54,758,759],{"class":56,"line":261},[54,760,746],{"class":113},[54,762,763,766,768],{"class":56,"line":273},[54,764,765],{"class":71},"    loop ",[54,767,107],{"class":67},[54,769,770],{"class":71}," asyncio.new_event_loop()\n",[54,772,773,776],{"class":56,"line":299},[54,774,775],{"class":67},"    yield",[54,777,778],{"class":71}," loop\n",[54,780,781],{"class":56,"line":313},[54,782,783],{"class":71},"    loop.close()\n",[54,785,786],{"class":56,"line":318},[54,787,98],{"emptyLinePlaceholder":97},[54,789,790],{"class":56,"line":323},[54,791,98],{"emptyLinePlaceholder":97},[54,793,794,797,799,801,803,805],{"class":56,"line":339},[54,795,796],{"class":332},"@pytest_asyncio.fixture",[54,798,721],{"class":71},[54,800,724],{"class":142},[54,802,107],{"class":67},[54,804,729],{"class":113},[54,806,117],{"class":71},[54,808,809,811,813,816],{"class":56,"line":356},[54,810,326],{"class":67},[54,812,329],{"class":67},[54,814,815],{"class":332}," engine",[54,817,336],{"class":71},[54,819,820],{"class":56,"line":365},[54,821,746],{"class":113},[54,823,824],{"class":56,"line":370},[54,825,826],{"class":113},"    Session-scoped async engine. StaticPool keeps one connection alive\n",[54,828,829],{"class":56,"line":398},[54,830,831],{"class":113},"    so all fixtures and tests see the same in-memory database.\n",[54,833,834],{"class":56,"line":412},[54,835,746],{"class":113},[54,837,838,841,843],{"class":56,"line":423},[54,839,840],{"class":71},"    _engine ",[54,842,107],{"class":67},[54,844,258],{"class":71},[54,846,847,850],{"class":56,"line":445},[54,848,849],{"class":188},"        DATABASE_URL",[54,851,852],{"class":71},",\n",[54,854,855,858,860,862,864,866,868],{"class":56,"line":450},[54,856,857],{"class":142},"        connect_args",[54,859,107],{"class":67},[54,861,281],{"class":71},[54,863,284],{"class":113},[54,865,287],{"class":71},[54,867,290],{"class":188},[54,869,870],{"class":71},"},\n",[54,872,874,877,879],{"class":56,"line":873},34,[54,875,876],{"class":142},"        poolclass",[54,878,107],{"class":67},[54,880,881],{"class":71},"StaticPool,\n",[54,883,885],{"class":56,"line":884},35,[54,886,887],{"class":71},"    )\n",[54,889,891,893,895,898,900],{"class":56,"line":890},36,[54,892,342],{"class":67},[54,894,345],{"class":67},[54,896,897],{"class":71}," _engine.begin() ",[54,899,160],{"class":67},[54,901,353],{"class":71},[54,903,905,907],{"class":56,"line":904},37,[54,906,359],{"class":67},[54,908,362],{"class":71},[54,910,912],{"class":56,"line":911},38,[54,913,98],{"emptyLinePlaceholder":97},[54,915,917,919],{"class":56,"line":916},39,[54,918,775],{"class":67},[54,920,921],{"class":71}," _engine\n",[54,923,925],{"class":56,"line":924},40,[54,926,98],{"emptyLinePlaceholder":97},[54,928,930,932,934,936,938],{"class":56,"line":929},41,[54,931,342],{"class":67},[54,933,345],{"class":67},[54,935,897],{"class":71},[54,937,160],{"class":67},[54,939,353],{"class":71},[54,941,943,945],{"class":56,"line":942},42,[54,944,359],{"class":67},[54,946,947],{"class":71}," conn.run_sync(Base.metadata.drop_all)\n",[54,949,951,954],{"class":56,"line":950},43,[54,952,953],{"class":67},"    await",[54,955,956],{"class":71}," _engine.dispose()\n",[54,958,960],{"class":56,"line":959},44,[54,961,98],{"emptyLinePlaceholder":97},[54,963,965],{"class":56,"line":964},45,[54,966,98],{"emptyLinePlaceholder":97},[54,968,970],{"class":56,"line":969},46,[54,971,972],{"class":332},"@pytest_asyncio.fixture\n",[54,974,976,978,980,983],{"class":56,"line":975},47,[54,977,326],{"class":67},[54,979,329],{"class":67},[54,981,982],{"class":332}," session",[54,984,985],{"class":71},"(engine):\n",[54,987,989],{"class":56,"line":988},48,[54,990,746],{"class":113},[54,992,994],{"class":56,"line":993},49,[54,995,996],{"class":113},"    Function-scoped session. Each test gets a fresh session;\n",[54,998,1000],{"class":56,"line":999},50,[54,1001,1002],{"class":113},"    all writes are rolled back after the test completes.\n",[54,1004,1006],{"class":56,"line":1005},51,[54,1007,746],{"class":113},[54,1009,1011,1013,1015,1017,1019,1021,1023,1025,1027,1029],{"class":56,"line":1010},52,[54,1012,373],{"class":71},[54,1014,107],{"class":67},[54,1016,378],{"class":71},[54,1018,381],{"class":142},[54,1020,107],{"class":67},[54,1022,386],{"class":71},[54,1024,389],{"class":142},[54,1026,107],{"class":67},[54,1028,290],{"class":188},[54,1030,117],{"class":71},[54,1032,1034,1036,1038,1040,1042],{"class":56,"line":1033},53,[54,1035,342],{"class":67},[54,1037,345],{"class":67},[54,1039,405],{"class":71},[54,1041,160],{"class":67},[54,1043,1044],{"class":71}," _session:\n",[54,1046,1048,1051],{"class":56,"line":1047},54,[54,1049,1050],{"class":67},"        yield",[54,1052,1053],{"class":71}," _session\n",[54,1055,1057,1059,1062],{"class":56,"line":1056},55,[54,1058,359],{"class":67},[54,1060,1061],{"class":71}," _session.rollback()   ",[54,1063,1064],{"class":60},"# undo any changes made during the test\n",[14,1066,1067,1068,1071,1072,1075,1076,1079],{},"You also need the ",[18,1069,1070],{},"asyncio_mode"," setting in ",[18,1073,1074],{},"pytest.ini"," (or ",[18,1077,1078],{},"pyproject.toml",") to avoid having to mark every async test individually:",[45,1081,1085],{"className":1082,"code":1083,"language":1084,"meta":50,"style":50},"language-ini shiki shiki-themes github-light github-dark","# pytest.ini\n[pytest]\nasyncio_mode = auto\n","ini",[18,1086,1087,1092,1097],{"__ignoreMap":50},[54,1088,1089],{"class":56,"line":57},[54,1090,1091],{},"# pytest.ini\n",[54,1093,1094],{"class":56,"line":64},[54,1095,1096],{},"[pytest]\n",[54,1098,1099],{"class":56,"line":81},[54,1100,1101],{},"asyncio_mode = auto\n",[14,1103,1104,1105,1107],{},"Or in ",[18,1106,1078],{},":",[45,1109,1113],{"className":1110,"code":1111,"language":1112,"meta":50,"style":50},"language-toml shiki shiki-themes github-light github-dark","[tool.pytest.ini_options]\nasyncio_mode = \"auto\"\n","toml",[18,1114,1115,1136],{"__ignoreMap":50},[54,1116,1117,1120,1123,1125,1128,1130,1133],{"class":56,"line":57},[54,1118,1119],{"class":71},"[",[54,1121,1122],{"class":332},"tool",[54,1124,471],{"class":71},[54,1126,1127],{"class":332},"pytest",[54,1129,471],{"class":71},[54,1131,1132],{"class":332},"ini_options",[54,1134,1135],{"class":71},"]\n",[54,1137,1138,1141],{"class":56,"line":64},[54,1139,1140],{"class":71},"asyncio_mode = ",[54,1142,1143],{"class":113},"\"auto\"\n",[14,1145,1146,1147,1150,1151,1154,1155,1158],{},"With ",[18,1148,1149],{},"asyncio_mode = \"auto\"",", pytest-asyncio detects ",[18,1152,1153],{},"async def"," test functions automatically. You no longer need ",[18,1156,1157],{},"@pytest.mark.asyncio"," on every test.",[14,1160,1161],{},"With this setup, individual tests are straightforward:",[45,1163,1165],{"className":47,"code":1164,"language":49,"meta":50,"style":50},"# test_users.py\nimport pytest\nimport pytest_asyncio\nfrom sqlalchemy import select\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nfrom myapp.models import User\n\n\n@pytest.mark.asyncio\nasync def test_create_and_retrieve_user(session: AsyncSession):\n    user = User(email=\"alice@example.com\", tenant_id=1)\n    session.add(user)\n    await session.flush()   # assigns id without committing\n\n    result = await session.execute(\n        select(User).where(User.email == \"alice@example.com\")\n    )\n    retrieved = result.scalar_one()\n    assert retrieved.id is not None\n    assert retrieved.tenant_id == 1\n\n\n@pytest.mark.asyncio\nasync def test_multiple_users_same_tenant(session: AsyncSession):\n    for i in range(5):\n        session.add(User(email=f\"user{i}@example.com\", tenant_id=42))\n    await session.flush()\n\n    result = await session.execute(\n        select(User).where(User.tenant_id == 42)\n    )\n    users = result.scalars().all()\n    assert len(users) == 5\n",[18,1166,1167,1172,1178,1184,1195,1206,1210,1221,1225,1229,1234,1246,1273,1278,1288,1292,1305,1318,1322,1332,1349,1361,1365,1369,1373,1384,1406,1443,1450,1454,1464,1476,1480,1490],{"__ignoreMap":50},[54,1168,1169],{"class":56,"line":57},[54,1170,1171],{"class":60},"# test_users.py\n",[54,1173,1174,1176],{"class":56,"line":64},[54,1175,75],{"class":67},[54,1177,643],{"class":71},[54,1179,1180,1182],{"class":56,"line":81},[54,1181,75],{"class":67},[54,1183,650],{"class":71},[54,1185,1186,1188,1190,1192],{"class":56,"line":94},[54,1187,68],{"class":67},[54,1189,72],{"class":71},[54,1191,75],{"class":67},[54,1193,1194],{"class":71}," select\n",[54,1196,1197,1199,1201,1203],{"class":56,"line":101},[54,1198,68],{"class":67},[54,1200,225],{"class":71},[54,1202,75],{"class":67},[54,1204,1205],{"class":71}," AsyncSession\n",[54,1207,1208],{"class":56,"line":120},[54,1209,98],{"emptyLinePlaceholder":97},[54,1211,1212,1214,1216,1218],{"class":56,"line":126},[54,1213,68],{"class":67},[54,1215,681],{"class":71},[54,1217,75],{"class":67},[54,1219,1220],{"class":71}," User\n",[54,1222,1223],{"class":56,"line":131},[54,1224,98],{"emptyLinePlaceholder":97},[54,1226,1227],{"class":56,"line":151},[54,1228,98],{"emptyLinePlaceholder":97},[54,1230,1231],{"class":56,"line":166},[54,1232,1233],{"class":332},"@pytest.mark.asyncio\n",[54,1235,1236,1238,1240,1243],{"class":56,"line":195},[54,1237,326],{"class":67},[54,1239,329],{"class":67},[54,1241,1242],{"class":332}," test_create_and_retrieve_user",[54,1244,1245],{"class":71},"(session: AsyncSession):\n",[54,1247,1248,1251,1253,1256,1258,1260,1263,1265,1267,1269,1271],{"class":56,"line":201},[54,1249,1250],{"class":71},"    user ",[54,1252,107],{"class":67},[54,1254,1255],{"class":71}," User(",[54,1257,172],{"class":142},[54,1259,107],{"class":67},[54,1261,1262],{"class":113},"\"alice@example.com\"",[54,1264,180],{"class":71},[54,1266,183],{"class":142},[54,1268,107],{"class":67},[54,1270,189],{"class":188},[54,1272,117],{"class":71},[54,1274,1275],{"class":56,"line":206},[54,1276,1277],{"class":71},"    session.add(user)\n",[54,1279,1280,1282,1285],{"class":56,"line":212},[54,1281,953],{"class":67},[54,1283,1284],{"class":71}," session.flush()   ",[54,1286,1287],{"class":60},"# assigns id without committing\n",[54,1289,1290],{"class":56,"line":220},[54,1291,98],{"emptyLinePlaceholder":97},[54,1293,1294,1297,1299,1302],{"class":56,"line":233},[54,1295,1296],{"class":71},"    result ",[54,1298,107],{"class":67},[54,1300,1301],{"class":67}," await",[54,1303,1304],{"class":71}," session.execute(\n",[54,1306,1307,1310,1313,1316],{"class":56,"line":246},[54,1308,1309],{"class":71},"        select(User).where(User.email ",[54,1311,1312],{"class":67},"==",[54,1314,1315],{"class":113}," \"alice@example.com\"",[54,1317,117],{"class":71},[54,1319,1320],{"class":56,"line":251},[54,1321,887],{"class":71},[54,1323,1324,1327,1329],{"class":56,"line":261},[54,1325,1326],{"class":71},"    retrieved ",[54,1328,107],{"class":67},[54,1330,1331],{"class":71}," result.scalar_one()\n",[54,1333,1334,1337,1340,1343,1346],{"class":56,"line":273},[54,1335,1336],{"class":67},"    assert",[54,1338,1339],{"class":71}," retrieved.id ",[54,1341,1342],{"class":67},"is",[54,1344,1345],{"class":67}," not",[54,1347,1348],{"class":188}," None\n",[54,1350,1351,1353,1356,1358],{"class":56,"line":299},[54,1352,1336],{"class":67},[54,1354,1355],{"class":71}," retrieved.tenant_id ",[54,1357,1312],{"class":67},[54,1359,1360],{"class":188}," 1\n",[54,1362,1363],{"class":56,"line":313},[54,1364,98],{"emptyLinePlaceholder":97},[54,1366,1367],{"class":56,"line":318},[54,1368,98],{"emptyLinePlaceholder":97},[54,1370,1371],{"class":56,"line":323},[54,1372,1233],{"class":332},[54,1374,1375,1377,1379,1382],{"class":56,"line":339},[54,1376,326],{"class":67},[54,1378,329],{"class":67},[54,1380,1381],{"class":332}," test_multiple_users_same_tenant",[54,1383,1245],{"class":71},[54,1385,1386,1389,1392,1395,1398,1400,1403],{"class":56,"line":356},[54,1387,1388],{"class":67},"    for",[54,1390,1391],{"class":71}," i ",[54,1393,1394],{"class":67},"in",[54,1396,1397],{"class":188}," range",[54,1399,721],{"class":71},[54,1401,1402],{"class":188},"5",[54,1404,1405],{"class":71},"):\n",[54,1407,1408,1411,1413,1415,1418,1421,1423,1426,1429,1432,1434,1436,1438,1441],{"class":56,"line":365},[54,1409,1410],{"class":71},"        session.add(User(",[54,1412,172],{"class":142},[54,1414,107],{"class":67},[54,1416,1417],{"class":67},"f",[54,1419,1420],{"class":113},"\"user",[54,1422,281],{"class":188},[54,1424,1425],{"class":71},"i",[54,1427,1428],{"class":188},"}",[54,1430,1431],{"class":113},"@example.com\"",[54,1433,180],{"class":71},[54,1435,183],{"class":142},[54,1437,107],{"class":67},[54,1439,1440],{"class":188},"42",[54,1442,192],{"class":71},[54,1444,1445,1447],{"class":56,"line":370},[54,1446,953],{"class":67},[54,1448,1449],{"class":71}," session.flush()\n",[54,1451,1452],{"class":56,"line":398},[54,1453,98],{"emptyLinePlaceholder":97},[54,1455,1456,1458,1460,1462],{"class":56,"line":412},[54,1457,1296],{"class":71},[54,1459,107],{"class":67},[54,1461,1301],{"class":67},[54,1463,1304],{"class":71},[54,1465,1466,1469,1471,1474],{"class":56,"line":423},[54,1467,1468],{"class":71},"        select(User).where(User.tenant_id ",[54,1470,1312],{"class":67},[54,1472,1473],{"class":188}," 42",[54,1475,117],{"class":71},[54,1477,1478],{"class":56,"line":445},[54,1479,887],{"class":71},[54,1481,1482,1485,1487],{"class":56,"line":450},[54,1483,1484],{"class":71},"    users ",[54,1486,107],{"class":67},[54,1488,1489],{"class":71}," result.scalars().all()\n",[54,1491,1492,1494,1497,1500,1502],{"class":56,"line":873},[54,1493,1336],{"class":67},[54,1495,1496],{"class":188}," len",[54,1498,1499],{"class":71},"(users) ",[54,1501,1312],{"class":67},[54,1503,1504],{"class":188}," 5\n",[37,1506,1508],{"id":1507},"resolving-warnings-errors-common-mistakes","Resolving Warnings, Errors & Common Mistakes",[1510,1511,1512,1528],"table",{},[1513,1514,1515],"thead",{},[1516,1517,1518,1522,1525],"tr",{},[1519,1520,1521],"th",{},"Error \u002F Warning",[1519,1523,1524],{},"Root Cause",[1519,1526,1527],{},"Production Fix",[1529,1530,1531,1554,1570,1586,1618,1643,1668],"tbody",{},[1516,1532,1533,1539,1545],{},[1534,1535,1536],"td",{},[18,1537,1538],{},"sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users",[1534,1540,1541,1542,1544],{},"Default ",[18,1543,534],{}," creates a new connection per checkout; the in-memory DB on that connection is empty.",[1534,1546,1547,1548,1550,1551,471],{},"Add ",[18,1549,24],{}," to ",[18,1552,1553],{},"create_async_engine()",[1516,1555,1556,1561,1564],{},[1534,1557,1558],{},[18,1559,1560],{},"sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread",[1534,1562,1563],{},"aiosqlite's thread and asyncio's thread differ; Python's sqlite3 enforces per-thread ownership.",[1534,1565,1547,1566,1550,1568,471],{},[18,1567,28],{},[18,1569,1553],{},[1516,1571,1572,1577,1580],{},[1534,1573,1574],{},[18,1575,1576],{},"ScopeMismatch: You tried to access the function scoped fixture ... with a session scoped request object",[1534,1578,1579],{},"Using a function-scoped fixture inside a session-scoped one in pytest-asyncio.",[1534,1581,1582,1583,1585],{},"Promote the fixture to ",[18,1584,619],{}," or restructure so session-scoped fixtures only depend on session-scoped ones.",[1516,1587,1588,1594,1605],{},[1534,1589,1590,1593],{},[18,1591,1592],{},"DeprecationWarning: There is no current event loop"," (pytest-asyncio >= 0.21)",[1534,1595,1596,1597,1600,1601,1604],{},"Using a bare ",[18,1598,1599],{},"asyncio.get_event_loop()"," without a custom ",[18,1602,1603],{},"event_loop"," fixture at session scope.",[1534,1606,1607,1608,1610,1611,1613,1614,1617],{},"Define an explicit ",[18,1609,1603],{}," fixture at ",[18,1612,619],{}," in ",[18,1615,1616],{},"conftest.py"," (see example above).",[1516,1619,1620,1625,1640],{},[1534,1621,1622],{},[18,1623,1624],{},"sqlalchemy.exc.InvalidRequestError: Can't operate on a closed transaction",[1534,1626,1627,1628,1631,1632,1635,1636,1639],{},"Accessing ",[18,1629,1630],{},"session"," after ",[18,1633,1634],{},"await session.rollback()"," or outside the ",[18,1637,1638],{},"async with"," block.",[1534,1641,1642],{},"Obtain a fresh session for each test via the fixture; never share session instances across tests.",[1516,1644,1645,1650,1657],{},[1534,1646,1647],{},[18,1648,1649],{},"RuntimeError: Task attached to a different loop",[1534,1651,1652,1653,1656],{},"Mixing session-scoped ",[18,1654,1655],{},"engine"," fixture and function-scoped event loop in pytest-asyncio.",[1534,1658,1659,1660,1662,1663,180,1665,1667],{},"Use the same ",[18,1661,724],{}," for ",[18,1664,1603],{},[18,1666,1655],{},", and any fixture that creates asyncio objects at the same level.",[1516,1669,1670,1676,1679],{},[1534,1671,1672,1675],{},[18,1673,1674],{},"aiofiles.threadpool.sync_to_async"," warning from aiosqlite internals",[1534,1677,1678],{},"aiosqlite version mismatch with the installed asyncio runtime.",[1534,1680,1681,1682,1684],{},"Pin ",[18,1683,598],{},"; this version unified the internal thread pool implementation.",[37,1686,1688],{"id":1687},"advanced-aiosqlite-optimization","Advanced aiosqlite Optimization",[477,1690,1692],{"id":1691},"seeding-reference-data-once-at-session-scope","Seeding reference data once at session scope",[14,1694,1695],{},"Test suites that need reference data (e.g., product categories, permission roles, tenant records) should seed that data in a session-scoped fixture rather than per-test. Combined with function-scoped rollback, this gives each test a clean slate for its own writes while avoiding the cost of re-inserting reference data on every test function:",[45,1697,1699],{"className":47,"code":1698,"language":49,"meta":50,"style":50},"# conftest.py (continued from above)\nfrom myapp.models import Tenant, Role\n\n\n@pytest_asyncio.fixture(scope=\"session\")\nasync def reference_data(engine):\n    \"\"\"\n    Insert reference data once. Because StaticPool keeps one connection,\n    this data persists for the entire test session and is visible to all tests.\n    Each test's session fixture rolls back only its own writes, not these rows.\n    \"\"\"\n    factory = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)\n    async with factory() as seed_session:\n        async with seed_session.begin():\n            seed_session.add_all([\n                Tenant(id=1, name=\"Acme Corp\", plan=\"enterprise\"),\n                Tenant(id=2, name=\"Beta Ltd\", plan=\"starter\"),\n                Role(id=1, name=\"admin\"),\n                Role(id=2, name=\"viewer\"),\n            ])\n    return  # data is now committed in the shared in-memory DB\n\n\n@pytest.mark.asyncio\nasync def test_order_for_existing_tenant(session: AsyncSession, reference_data):\n    \"\"\"reference_data fixture ensures Tenant(id=1) exists before this runs.\"\"\"\n    order = Order(tenant_id=1, reference=\"ORD-001\", status=\"pending\")\n    session.add(order)\n    await session.flush()\n\n    result = await session.execute(\n        select(Order).where(Order.reference == \"ORD-001\")\n    )\n    assert result.scalar_one().tenant_id == 1\n",[18,1700,1701,1706,1717,1721,1725,1739,1750,1754,1759,1764,1769,1773,1795,1808,1817,1822,1857,1888,1910,1931,1936,1944,1948,1952,1956,1968,1973,2011,2016,2022,2026,2036,2048,2052],{"__ignoreMap":50},[54,1702,1703],{"class":56,"line":57},[54,1704,1705],{"class":60},"# conftest.py (continued from above)\n",[54,1707,1708,1710,1712,1714],{"class":56,"line":64},[54,1709,68],{"class":67},[54,1711,681],{"class":71},[54,1713,75],{"class":67},[54,1715,1716],{"class":71}," Tenant, Role\n",[54,1718,1719],{"class":56,"line":81},[54,1720,98],{"emptyLinePlaceholder":97},[54,1722,1723],{"class":56,"line":94},[54,1724,98],{"emptyLinePlaceholder":97},[54,1726,1727,1729,1731,1733,1735,1737],{"class":56,"line":101},[54,1728,796],{"class":332},[54,1730,721],{"class":71},[54,1732,724],{"class":142},[54,1734,107],{"class":67},[54,1736,729],{"class":113},[54,1738,117],{"class":71},[54,1740,1741,1743,1745,1748],{"class":56,"line":120},[54,1742,326],{"class":67},[54,1744,329],{"class":67},[54,1746,1747],{"class":332}," reference_data",[54,1749,985],{"class":71},[54,1751,1752],{"class":56,"line":126},[54,1753,746],{"class":113},[54,1755,1756],{"class":56,"line":131},[54,1757,1758],{"class":113},"    Insert reference data once. Because StaticPool keeps one connection,\n",[54,1760,1761],{"class":56,"line":151},[54,1762,1763],{"class":113},"    this data persists for the entire test session and is visible to all tests.\n",[54,1765,1766],{"class":56,"line":166},[54,1767,1768],{"class":113},"    Each test's session fixture rolls back only its own writes, not these rows.\n",[54,1770,1771],{"class":56,"line":195},[54,1772,746],{"class":113},[54,1774,1775,1777,1779,1781,1783,1785,1787,1789,1791,1793],{"class":56,"line":201},[54,1776,373],{"class":71},[54,1778,107],{"class":67},[54,1780,378],{"class":71},[54,1782,381],{"class":142},[54,1784,107],{"class":67},[54,1786,386],{"class":71},[54,1788,389],{"class":142},[54,1790,107],{"class":67},[54,1792,290],{"class":188},[54,1794,117],{"class":71},[54,1796,1797,1799,1801,1803,1805],{"class":56,"line":206},[54,1798,342],{"class":67},[54,1800,345],{"class":67},[54,1802,405],{"class":71},[54,1804,160],{"class":67},[54,1806,1807],{"class":71}," seed_session:\n",[54,1809,1810,1812,1814],{"class":56,"line":212},[54,1811,415],{"class":67},[54,1813,345],{"class":67},[54,1815,1816],{"class":71}," seed_session.begin():\n",[54,1818,1819],{"class":56,"line":220},[54,1820,1821],{"class":71},"            seed_session.add_all([\n",[54,1823,1824,1827,1830,1832,1834,1836,1839,1841,1844,1846,1849,1851,1854],{"class":56,"line":233},[54,1825,1826],{"class":71},"                Tenant(",[54,1828,1829],{"class":142},"id",[54,1831,107],{"class":67},[54,1833,189],{"class":188},[54,1835,180],{"class":71},[54,1837,1838],{"class":142},"name",[54,1840,107],{"class":67},[54,1842,1843],{"class":113},"\"Acme Corp\"",[54,1845,180],{"class":71},[54,1847,1848],{"class":142},"plan",[54,1850,107],{"class":67},[54,1852,1853],{"class":113},"\"enterprise\"",[54,1855,1856],{"class":71},"),\n",[54,1858,1859,1861,1863,1865,1868,1870,1872,1874,1877,1879,1881,1883,1886],{"class":56,"line":246},[54,1860,1826],{"class":71},[54,1862,1829],{"class":142},[54,1864,107],{"class":67},[54,1866,1867],{"class":188},"2",[54,1869,180],{"class":71},[54,1871,1838],{"class":142},[54,1873,107],{"class":67},[54,1875,1876],{"class":113},"\"Beta Ltd\"",[54,1878,180],{"class":71},[54,1880,1848],{"class":142},[54,1882,107],{"class":67},[54,1884,1885],{"class":113},"\"starter\"",[54,1887,1856],{"class":71},[54,1889,1890,1893,1895,1897,1899,1901,1903,1905,1908],{"class":56,"line":251},[54,1891,1892],{"class":71},"                Role(",[54,1894,1829],{"class":142},[54,1896,107],{"class":67},[54,1898,189],{"class":188},[54,1900,180],{"class":71},[54,1902,1838],{"class":142},[54,1904,107],{"class":67},[54,1906,1907],{"class":113},"\"admin\"",[54,1909,1856],{"class":71},[54,1911,1912,1914,1916,1918,1920,1922,1924,1926,1929],{"class":56,"line":261},[54,1913,1892],{"class":71},[54,1915,1829],{"class":142},[54,1917,107],{"class":67},[54,1919,1867],{"class":188},[54,1921,180],{"class":71},[54,1923,1838],{"class":142},[54,1925,107],{"class":67},[54,1927,1928],{"class":113},"\"viewer\"",[54,1930,1856],{"class":71},[54,1932,1933],{"class":56,"line":273},[54,1934,1935],{"class":71},"            ])\n",[54,1937,1938,1941],{"class":56,"line":299},[54,1939,1940],{"class":67},"    return",[54,1942,1943],{"class":60},"  # data is now committed in the shared in-memory DB\n",[54,1945,1946],{"class":56,"line":313},[54,1947,98],{"emptyLinePlaceholder":97},[54,1949,1950],{"class":56,"line":318},[54,1951,98],{"emptyLinePlaceholder":97},[54,1953,1954],{"class":56,"line":323},[54,1955,1233],{"class":332},[54,1957,1958,1960,1962,1965],{"class":56,"line":339},[54,1959,326],{"class":67},[54,1961,329],{"class":67},[54,1963,1964],{"class":332}," test_order_for_existing_tenant",[54,1966,1967],{"class":71},"(session: AsyncSession, reference_data):\n",[54,1969,1970],{"class":56,"line":356},[54,1971,1972],{"class":113},"    \"\"\"reference_data fixture ensures Tenant(id=1) exists before this runs.\"\"\"\n",[54,1974,1975,1978,1980,1983,1985,1987,1989,1991,1994,1996,1999,2001,2004,2006,2009],{"class":56,"line":365},[54,1976,1977],{"class":71},"    order ",[54,1979,107],{"class":67},[54,1981,1982],{"class":71}," Order(",[54,1984,183],{"class":142},[54,1986,107],{"class":67},[54,1988,189],{"class":188},[54,1990,180],{"class":71},[54,1992,1993],{"class":142},"reference",[54,1995,107],{"class":67},[54,1997,1998],{"class":113},"\"ORD-001\"",[54,2000,180],{"class":71},[54,2002,2003],{"class":142},"status",[54,2005,107],{"class":67},[54,2007,2008],{"class":113},"\"pending\"",[54,2010,117],{"class":71},[54,2012,2013],{"class":56,"line":370},[54,2014,2015],{"class":71},"    session.add(order)\n",[54,2017,2018,2020],{"class":56,"line":398},[54,2019,953],{"class":67},[54,2021,1449],{"class":71},[54,2023,2024],{"class":56,"line":412},[54,2025,98],{"emptyLinePlaceholder":97},[54,2027,2028,2030,2032,2034],{"class":56,"line":423},[54,2029,1296],{"class":71},[54,2031,107],{"class":67},[54,2033,1301],{"class":67},[54,2035,1304],{"class":71},[54,2037,2038,2041,2043,2046],{"class":56,"line":445},[54,2039,2040],{"class":71},"        select(Order).where(Order.reference ",[54,2042,1312],{"class":67},[54,2044,2045],{"class":113}," \"ORD-001\"",[54,2047,117],{"class":71},[54,2049,2050],{"class":56,"line":450},[54,2051,887],{"class":71},[54,2053,2054,2056,2059,2061],{"class":56,"line":873},[54,2055,1336],{"class":67},[54,2057,2058],{"class":71}," result.scalar_one().tenant_id ",[54,2060,1312],{"class":67},[54,2062,1360],{"class":188},[14,2064,2065,2066,2068,2069,2072,2073,2076,2077,2080,2081,2084],{},"The key insight: because the per-test ",[18,2067,1630],{}," fixture calls ",[18,2070,2071],{},"await _session.rollback()"," at teardown, the ",[18,2074,2075],{},"Order"," inserted in the test is undone — but the ",[18,2078,2079],{},"Tenant"," rows inserted by ",[18,2082,2083],{},"reference_data"," (committed in a separate session before the rollback) survive across tests. This mirrors the standard Django test-case pattern but in fully async form.",[477,2086,2088],{"id":2087},"using-aiosqlite-for-local-cli-tooling","Using aiosqlite for local CLI tooling",[14,2090,2091],{},"Beyond tests, aiosqlite is useful in CLI tools and data-pipeline scripts where spinning up a Postgres instance is overkill. A local SQLite file behaves identically to the ORM session interface:",[45,2093,2095],{"className":47,"code":2094,"language":49,"meta":50,"style":50},"#!\u002Fusr\u002Fbin\u002Fenv python3\n\"\"\"\nLocal development seed script — populates a SQLite file for offline work.\nSwitch DATABASE_URL to postgresql+asyncpg:\u002F\u002F for staging\u002Fproduction runs.\n\"\"\"\nimport asyncio\nimport os\nfrom sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession\nfrom sqlalchemy.pool import StaticPool\n\nfrom myapp.models import Base, User, Product, Tenant\n\nDATABASE_URL = os.environ.get(\"DATABASE_URL\", \"sqlite+aiosqlite:\u002F\u002F\u002F.\u002Fdev.db\")\n\n\nasync def seed_local_db() -> None:\n    is_memory = DATABASE_URL == \"sqlite+aiosqlite:\u002F\u002F\"\n    engine = create_async_engine(\n        DATABASE_URL,\n        connect_args={\"check_same_thread\": False} if \"sqlite\" in DATABASE_URL else {},\n        poolclass=StaticPool if is_memory else None,\n        echo=True,\n    )\n\n    async with engine.begin() as conn:\n        await conn.run_sync(Base.metadata.create_all)\n\n    factory = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)\n    async with factory() as session:\n        async with session.begin():\n            session.add_all([\n                Tenant(id=1, name=\"Dev Tenant\", plan=\"developer\"),\n                User(email=\"dev@example.com\", tenant_id=1),\n                Product(sku=\"DEV-001\", name=\"Widget Alpha\", price=9.99, tenant_id=1),\n                Product(sku=\"DEV-002\", name=\"Widget Beta\", price=19.99, tenant_id=1),\n            ])\n    print(f\"Seeded development database at {DATABASE_URL}\")\n    await engine.dispose()\n\n\nif __name__ == \"__main__\":\n    asyncio.run(seed_local_db())\n",[18,2096,2097,2102,2107,2112,2117,2121,2127,2134,2144,2154,2158,2169,2173,2192,2196,2200,2218,2233,2242,2248,2282,2304,2316,2320,2324,2336,2342,2346,2368,2380,2388,2393,2423,2445,2487,2526,2530,2550,2557,2561,2565,2579],{"__ignoreMap":50},[54,2098,2099],{"class":56,"line":57},[54,2100,2101],{"class":60},"#!\u002Fusr\u002Fbin\u002Fenv python3\n",[54,2103,2104],{"class":56,"line":64},[54,2105,2106],{"class":113},"\"\"\"\n",[54,2108,2109],{"class":56,"line":81},[54,2110,2111],{"class":113},"Local development seed script — populates a SQLite file for offline work.\n",[54,2113,2114],{"class":56,"line":94},[54,2115,2116],{"class":113},"Switch DATABASE_URL to postgresql+asyncpg:\u002F\u002F for staging\u002Fproduction runs.\n",[54,2118,2119],{"class":56,"line":101},[54,2120,2106],{"class":113},[54,2122,2123,2125],{"class":56,"line":120},[54,2124,75],{"class":67},[54,2126,217],{"class":71},[54,2128,2129,2131],{"class":56,"line":126},[54,2130,75],{"class":67},[54,2132,2133],{"class":71}," os\n",[54,2135,2136,2138,2140,2142],{"class":56,"line":131},[54,2137,68],{"class":67},[54,2139,225],{"class":71},[54,2141,75],{"class":67},[54,2143,230],{"class":71},[54,2145,2146,2148,2150,2152],{"class":56,"line":151},[54,2147,68],{"class":67},[54,2149,238],{"class":71},[54,2151,75],{"class":67},[54,2153,243],{"class":71},[54,2155,2156],{"class":56,"line":166},[54,2157,98],{"emptyLinePlaceholder":97},[54,2159,2160,2162,2164,2166],{"class":56,"line":195},[54,2161,68],{"class":67},[54,2163,681],{"class":71},[54,2165,75],{"class":67},[54,2167,2168],{"class":71}," Base, User, Product, Tenant\n",[54,2170,2171],{"class":56,"line":201},[54,2172,98],{"emptyLinePlaceholder":97},[54,2174,2175,2177,2179,2182,2185,2187,2190],{"class":56,"line":206},[54,2176,699],{"class":188},[54,2178,702],{"class":67},[54,2180,2181],{"class":71}," os.environ.get(",[54,2183,2184],{"class":113},"\"DATABASE_URL\"",[54,2186,180],{"class":71},[54,2188,2189],{"class":113},"\"sqlite+aiosqlite:\u002F\u002F\u002F.\u002Fdev.db\"",[54,2191,117],{"class":71},[54,2193,2194],{"class":56,"line":212},[54,2195,98],{"emptyLinePlaceholder":97},[54,2197,2198],{"class":56,"line":220},[54,2199,98],{"emptyLinePlaceholder":97},[54,2201,2202,2204,2206,2209,2212,2215],{"class":56,"line":233},[54,2203,326],{"class":67},[54,2205,329],{"class":67},[54,2207,2208],{"class":332}," seed_local_db",[54,2210,2211],{"class":71},"() -> ",[54,2213,2214],{"class":188},"None",[54,2216,2217],{"class":71},":\n",[54,2219,2220,2223,2225,2228,2231],{"class":56,"line":246},[54,2221,2222],{"class":71},"    is_memory ",[54,2224,107],{"class":67},[54,2226,2227],{"class":188}," DATABASE_URL",[54,2229,2230],{"class":67}," ==",[54,2232,705],{"class":113},[54,2234,2235,2238,2240],{"class":56,"line":251},[54,2236,2237],{"class":71},"    engine ",[54,2239,107],{"class":67},[54,2241,258],{"class":71},[54,2243,2244,2246],{"class":56,"line":261},[54,2245,849],{"class":188},[54,2247,852],{"class":71},[54,2249,2250,2252,2254,2256,2258,2260,2262,2265,2268,2271,2274,2276,2279],{"class":56,"line":273},[54,2251,857],{"class":142},[54,2253,107],{"class":67},[54,2255,281],{"class":71},[54,2257,284],{"class":113},[54,2259,287],{"class":71},[54,2261,290],{"class":188},[54,2263,2264],{"class":71},"} ",[54,2266,2267],{"class":67},"if",[54,2269,2270],{"class":113}," \"sqlite\"",[54,2272,2273],{"class":67}," in",[54,2275,2227],{"class":188},[54,2277,2278],{"class":67}," else",[54,2280,2281],{"class":71}," {},\n",[54,2283,2284,2286,2288,2291,2293,2296,2299,2302],{"class":56,"line":299},[54,2285,876],{"class":142},[54,2287,107],{"class":67},[54,2289,2290],{"class":71},"StaticPool ",[54,2292,2267],{"class":67},[54,2294,2295],{"class":71}," is_memory ",[54,2297,2298],{"class":67},"else",[54,2300,2301],{"class":188}," None",[54,2303,852],{"class":71},[54,2305,2306,2309,2311,2314],{"class":56,"line":313},[54,2307,2308],{"class":142},"        echo",[54,2310,107],{"class":67},[54,2312,2313],{"class":188},"True",[54,2315,852],{"class":71},[54,2317,2318],{"class":56,"line":318},[54,2319,887],{"class":71},[54,2321,2322],{"class":56,"line":323},[54,2323,98],{"emptyLinePlaceholder":97},[54,2325,2326,2328,2330,2332,2334],{"class":56,"line":339},[54,2327,342],{"class":67},[54,2329,345],{"class":67},[54,2331,348],{"class":71},[54,2333,160],{"class":67},[54,2335,353],{"class":71},[54,2337,2338,2340],{"class":56,"line":356},[54,2339,359],{"class":67},[54,2341,362],{"class":71},[54,2343,2344],{"class":56,"line":365},[54,2345,98],{"emptyLinePlaceholder":97},[54,2347,2348,2350,2352,2354,2356,2358,2360,2362,2364,2366],{"class":56,"line":370},[54,2349,373],{"class":71},[54,2351,107],{"class":67},[54,2353,378],{"class":71},[54,2355,381],{"class":142},[54,2357,107],{"class":67},[54,2359,386],{"class":71},[54,2361,389],{"class":142},[54,2363,107],{"class":67},[54,2365,290],{"class":188},[54,2367,117],{"class":71},[54,2369,2370,2372,2374,2376,2378],{"class":56,"line":398},[54,2371,342],{"class":67},[54,2373,345],{"class":67},[54,2375,405],{"class":71},[54,2377,160],{"class":67},[54,2379,163],{"class":71},[54,2381,2382,2384,2386],{"class":56,"line":412},[54,2383,415],{"class":67},[54,2385,345],{"class":67},[54,2387,420],{"class":71},[54,2389,2390],{"class":56,"line":423},[54,2391,2392],{"class":71},"            session.add_all([\n",[54,2394,2395,2397,2399,2401,2403,2405,2407,2409,2412,2414,2416,2418,2421],{"class":56,"line":445},[54,2396,1826],{"class":71},[54,2398,1829],{"class":142},[54,2400,107],{"class":67},[54,2402,189],{"class":188},[54,2404,180],{"class":71},[54,2406,1838],{"class":142},[54,2408,107],{"class":67},[54,2410,2411],{"class":113},"\"Dev Tenant\"",[54,2413,180],{"class":71},[54,2415,1848],{"class":142},[54,2417,107],{"class":67},[54,2419,2420],{"class":113},"\"developer\"",[54,2422,1856],{"class":71},[54,2424,2425,2428,2430,2432,2435,2437,2439,2441,2443],{"class":56,"line":450},[54,2426,2427],{"class":71},"                User(",[54,2429,172],{"class":142},[54,2431,107],{"class":67},[54,2433,2434],{"class":113},"\"dev@example.com\"",[54,2436,180],{"class":71},[54,2438,183],{"class":142},[54,2440,107],{"class":67},[54,2442,189],{"class":188},[54,2444,1856],{"class":71},[54,2446,2447,2450,2453,2455,2458,2460,2462,2464,2467,2469,2472,2474,2477,2479,2481,2483,2485],{"class":56,"line":873},[54,2448,2449],{"class":71},"                Product(",[54,2451,2452],{"class":142},"sku",[54,2454,107],{"class":67},[54,2456,2457],{"class":113},"\"DEV-001\"",[54,2459,180],{"class":71},[54,2461,1838],{"class":142},[54,2463,107],{"class":67},[54,2465,2466],{"class":113},"\"Widget Alpha\"",[54,2468,180],{"class":71},[54,2470,2471],{"class":142},"price",[54,2473,107],{"class":67},[54,2475,2476],{"class":188},"9.99",[54,2478,180],{"class":71},[54,2480,183],{"class":142},[54,2482,107],{"class":67},[54,2484,189],{"class":188},[54,2486,1856],{"class":71},[54,2488,2489,2491,2493,2495,2498,2500,2502,2504,2507,2509,2511,2513,2516,2518,2520,2522,2524],{"class":56,"line":884},[54,2490,2449],{"class":71},[54,2492,2452],{"class":142},[54,2494,107],{"class":67},[54,2496,2497],{"class":113},"\"DEV-002\"",[54,2499,180],{"class":71},[54,2501,1838],{"class":142},[54,2503,107],{"class":67},[54,2505,2506],{"class":113},"\"Widget Beta\"",[54,2508,180],{"class":71},[54,2510,2471],{"class":142},[54,2512,107],{"class":67},[54,2514,2515],{"class":188},"19.99",[54,2517,180],{"class":71},[54,2519,183],{"class":142},[54,2521,107],{"class":67},[54,2523,189],{"class":188},[54,2525,1856],{"class":71},[54,2527,2528],{"class":56,"line":890},[54,2529,1935],{"class":71},[54,2531,2532,2535,2537,2539,2542,2545,2548],{"class":56,"line":904},[54,2533,2534],{"class":188},"    print",[54,2536,721],{"class":71},[54,2538,1417],{"class":67},[54,2540,2541],{"class":113},"\"Seeded development database at ",[54,2543,2544],{"class":188},"{DATABASE_URL}",[54,2546,2547],{"class":113},"\"",[54,2549,117],{"class":71},[54,2551,2552,2554],{"class":56,"line":911},[54,2553,953],{"class":67},[54,2555,2556],{"class":71}," engine.dispose()\n",[54,2558,2559],{"class":56,"line":916},[54,2560,98],{"emptyLinePlaceholder":97},[54,2562,2563],{"class":56,"line":924},[54,2564,98],{"emptyLinePlaceholder":97},[54,2566,2567,2569,2572,2574,2577],{"class":56,"line":929},[54,2568,2267],{"class":67},[54,2570,2571],{"class":188}," __name__",[54,2573,2230],{"class":67},[54,2575,2576],{"class":113}," \"__main__\"",[54,2578,2217],{"class":71},[54,2580,2581],{"class":56,"line":942},[54,2582,2583],{"class":71},"    asyncio.run(seed_local_db())\n",[14,2585,2586,2587,2590],{},"Running this with ",[18,2588,2589],{},"DATABASE_URL=sqlite+aiosqlite:\u002F\u002F\u002F.\u002Fdev.db python seed.py"," creates a persistent local file. Deleting and re-running it resets the database, giving you a reproducible starting state without touching any shared database.",[477,2592,2594],{"id":2593},"differences-from-postgres-to-watch-in-tests","Differences from Postgres to watch in tests",[477,2596,2594],{"id":2597},"differences-from-postgres-to-watch-in-tests-1",[14,2599,2600],{},"aiosqlite is not a Postgres-compatible database. Tests that pass against aiosqlite can fail against Postgres (and vice versa) for the following reasons. Treat this list as a checklist when writing tests you want to run against both backends:",[2602,2603,2604,2619,2645,2659],"ul",{},[523,2605,2606,2610,2611,2614,2615,2618],{},[2607,2608,2609],"strong",{},"Case sensitivity:"," SQLite column comparisons are case-insensitive for ASCII text by default. ",[18,2612,2613],{},"WHERE email = 'ALICE@example.com'"," matches ",[18,2616,2617],{},"alice@example.com"," in SQLite but not in Postgres.",[523,2620,2621,2624,2625,1662,2628,2631,2632,2634,2635,1631,2638,2641,2642,2644],{},[2607,2622,2623],{},"RETURNING clause:"," SQLAlchemy 2.0 uses ",[18,2626,2627],{},"RETURNING",[18,2629,2630],{},"session.add()"," on Postgres to retrieve server-generated values. SQLite supports ",[18,2633,2627],{}," from version 3.35.0 (2021). If you are on an older Python installation with a bundled older SQLite, ",[18,2636,2637],{},"flush()",[18,2639,2640],{},"add()"," may not populate ",[18,2643,1829],{}," fields correctly.",[523,2646,2647,2650,2651,2654,2655,2658],{},[2607,2648,2649],{},"Foreign key enforcement:"," SQLite does not enforce foreign key constraints by default. Rows that would fail a ",[18,2652,2653],{},"FOREIGN KEY"," constraint on Postgres insert silently in SQLite unless you explicitly run ",[18,2656,2657],{},"PRAGMA foreign_keys = ON"," on the connection.",[523,2660,2661,2667,2668,2671,2672,2675],{},[2607,2662,2663,2666],{},[18,2664,2665],{},"ON CONFLICT DO UPDATE"," (upserts):"," Supported in SQLite 3.24+ and Postgres, but the SQLAlchemy API for Postgres upserts (",[18,2669,2670],{},"insert().on_conflict_do_update()",") uses ",[18,2673,2674],{},"sqlalchemy.dialects.postgresql.insert",", which is Postgres-only and will fail against SQLite.",[37,2677,2679],{"id":2678},"frequently-asked-questions","Frequently Asked Questions",[14,2681,2682,2692,2694,2695,2697,2698,2701,2702,2704,2705,2707],{},[2607,2683,2684,2685,2687,2688,2691],{},"Why must I use ",[18,2686,459],{}," and not ",[18,2689,2690],{},"NullPool"," for in-memory SQLite?",[18,2693,2690],{}," creates a new connection for every checkout and closes it immediately after release — which destroys the in-memory database on each session close. ",[18,2696,459],{}," maintains exactly one persistent connection. For file-based SQLite (",[18,2699,2700],{},"sqlite+aiosqlite:\u002F\u002F\u002Fpath\u002Fto\u002Ffile.db","), ",[18,2703,2690],{}," works correctly because the database is persisted on disk, but ",[18,2706,459],{}," is still safer in test contexts to avoid the overhead of opening and closing the file on every fixture checkout.",[14,2709,2710,2713,2714,2717,2718,2720,2721,2723,2724,2727,2728,471],{},[2607,2711,2712],{},"Can I run pytest-xdist parallel tests with an aiosqlite in-memory database?","\nNo. Each ",[18,2715,2716],{},"pytest-xdist"," worker is a separate process, and ",[18,2719,459],{}," connections are not shared across process boundaries. Use one in-memory database per worker by passing a worker-specific URL via ",[18,2722,2716],{},"'s ",[18,2725,2726],{},"worker_id"," fixture, or use a temporary file per worker: ",[18,2729,2730],{},"sqlite+aiosqlite:\u002F\u002F\u002Ftmp\u002Ftest_{worker_id}.db",[14,2732,2733,2736],{},[2607,2734,2735],{},"Should I test against aiosqlite or run my tests against a real Postgres instance?","\nBoth. Use aiosqlite for unit tests and fast CI feedback loops — they run without any network dependency and complete in milliseconds. Run your integration and migration tests against a real Postgres instance (a Docker container in CI is sufficient) to catch Postgres-specific behaviour: case sensitivity, constraint enforcement, and dialect-specific SQL. The cost of maintaining both fixture modes is low compared to the production confidence a real-Postgres integration suite provides.",[14,2738,2739,2742,2743,2746,2747,2749],{},[2607,2740,2741],{},"How do I enable foreign key enforcement in my aiosqlite test engine?","\nListen to the ",[18,2744,2745],{},"connect"," event and issue ",[18,2748,2657],{}," on each new connection:",[45,2751,2753],{"className":47,"code":2752,"language":49,"meta":50,"style":50},"from sqlalchemy import event\nfrom sqlalchemy.ext.asyncio import create_async_engine\nfrom sqlalchemy.pool import StaticPool\n\n\nengine = create_async_engine(\n    \"sqlite+aiosqlite:\u002F\u002F\",\n    connect_args={\"check_same_thread\": False},\n    poolclass=StaticPool,\n)\n\n\n@event.listens_for(engine.sync_engine, \"connect\")\ndef enable_foreign_keys(dbapi_connection, connection_record):\n    cursor = dbapi_connection.cursor()\n    cursor.execute(\"PRAGMA foreign_keys=ON\")\n    cursor.close()\n",[18,2754,2755,2766,2777,2787,2791,2795,2803,2809,2825,2833,2837,2841,2845,2858,2868,2878,2888],{"__ignoreMap":50},[54,2756,2757,2759,2761,2763],{"class":56,"line":57},[54,2758,68],{"class":67},[54,2760,72],{"class":71},[54,2762,75],{"class":67},[54,2764,2765],{"class":71}," event\n",[54,2767,2768,2770,2772,2774],{"class":56,"line":64},[54,2769,68],{"class":67},[54,2771,225],{"class":71},[54,2773,75],{"class":67},[54,2775,2776],{"class":71}," create_async_engine\n",[54,2778,2779,2781,2783,2785],{"class":56,"line":81},[54,2780,68],{"class":67},[54,2782,238],{"class":71},[54,2784,75],{"class":67},[54,2786,243],{"class":71},[54,2788,2789],{"class":56,"line":94},[54,2790,98],{"emptyLinePlaceholder":97},[54,2792,2793],{"class":56,"line":101},[54,2794,98],{"emptyLinePlaceholder":97},[54,2796,2797,2799,2801],{"class":56,"line":120},[54,2798,104],{"class":71},[54,2800,107],{"class":67},[54,2802,258],{"class":71},[54,2804,2805,2807],{"class":56,"line":126},[54,2806,264],{"class":113},[54,2808,852],{"class":71},[54,2810,2811,2813,2815,2817,2819,2821,2823],{"class":56,"line":131},[54,2812,276],{"class":142},[54,2814,107],{"class":67},[54,2816,281],{"class":71},[54,2818,284],{"class":113},[54,2820,287],{"class":71},[54,2822,290],{"class":188},[54,2824,870],{"class":71},[54,2826,2827,2829,2831],{"class":56,"line":151},[54,2828,302],{"class":142},[54,2830,107],{"class":67},[54,2832,881],{"class":71},[54,2834,2835],{"class":56,"line":166},[54,2836,117],{"class":71},[54,2838,2839],{"class":56,"line":195},[54,2840,98],{"emptyLinePlaceholder":97},[54,2842,2843],{"class":56,"line":201},[54,2844,98],{"emptyLinePlaceholder":97},[54,2846,2847,2850,2853,2856],{"class":56,"line":206},[54,2848,2849],{"class":332},"@event.listens_for",[54,2851,2852],{"class":71},"(engine.sync_engine, ",[54,2854,2855],{"class":113},"\"connect\"",[54,2857,117],{"class":71},[54,2859,2860,2862,2865],{"class":56,"line":212},[54,2861,736],{"class":67},[54,2863,2864],{"class":332}," enable_foreign_keys",[54,2866,2867],{"class":71},"(dbapi_connection, connection_record):\n",[54,2869,2870,2873,2875],{"class":56,"line":220},[54,2871,2872],{"class":71},"    cursor ",[54,2874,107],{"class":67},[54,2876,2877],{"class":71}," dbapi_connection.cursor()\n",[54,2879,2880,2883,2886],{"class":56,"line":233},[54,2881,2882],{"class":71},"    cursor.execute(",[54,2884,2885],{"class":113},"\"PRAGMA foreign_keys=ON\"",[54,2887,117],{"class":71},[54,2889,2890],{"class":56,"line":246},[54,2891,2892],{"class":71},"    cursor.close()\n",[14,2894,2895,2896,2898],{},"This runs on every physical connection establishment. With ",[18,2897,459],{}," that means once per engine lifetime — exactly what you want.",[37,2900,2902],{"id":2901},"related","Related",[2602,2904,2905,2911,2918],{},[523,2906,2907,2910],{},[31,2908,2909],{"href":33},"Selecting Async Drivers for SQLite, MySQL, and Postgres"," — parent guide covering the full driver landscape across all three backends with production configuration.",[523,2912,2913,2917],{},[31,2914,2916],{"href":2915},"\u002Fasync-engines-dialects-and-connection-pooling\u002Fconfiguring-async-engines-and-connection-pools\u002F","Configuring Async Engines and Connection Pools"," — pool_size, max_overflow, StaticPool vs QueuePool vs NullPool explained for all environments.",[523,2919,2920,2924],{},[31,2921,2923],{"href":2922},"\u002Fasync-engines-dialects-and-connection-pooling\u002Fdialect-specific-gotchas-and-driver-quirks\u002F","Dialect-Specific Gotchas and Driver Quirks"," — SQLite foreign-key enforcement, MySQL charset issues, and Postgres prepared-statement conflicts.",[2926,2927,2928],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":50,"searchDepth":64,"depth":64,"links":2930},[2931,2932,2938,2939,2945,2946],{"id":39,"depth":64,"text":40},{"id":474,"depth":64,"text":475,"children":2933},[2934,2935,2936,2937],{"id":479,"depth":81,"text":480},{"id":508,"depth":81,"text":509},{"id":567,"depth":81,"text":568},{"id":612,"depth":81,"text":613},{"id":1507,"depth":64,"text":1508},{"id":1687,"depth":64,"text":1688,"children":2940},[2941,2942,2943,2944],{"id":1691,"depth":81,"text":1692},{"id":2087,"depth":81,"text":2088},{"id":2593,"depth":81,"text":2594},{"id":2597,"depth":81,"text":2594},{"id":2678,"depth":64,"text":2679},{"id":2901,"depth":64,"text":2902},"Use sqlite+aiosqlite:\u002F\u002F with poolclass=StaticPool and connect_args={\"check_same_thread\": False} to run fast, dependency-free async SQLAlchemy tests — this guide, part of the selecting async drivers for SQLite, MySQL, and Postgres reference, walks through the exact setup.","md",{"date":2950},"2026-06-18","\u002Fasync-engines-dialects-and-connection-pooling\u002Fselecting-async-drivers-for-sqlite-mysql-and-postgres\u002Fusing-aiosqlite-for-async-tests-and-local-development",{"title":5,"description":2947},"async-engines-dialects-and-connection-pooling\u002Fselecting-async-drivers-for-sqlite-mysql-and-postgres\u002Fusing-aiosqlite-for-async-tests-and-local-development\u002Findex","YZsF_nkuVUkVjRZCKvq7YlXbaLPFJ4YuOrJqGVgBPKA",1781810028984]