Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ lib64/
parts/
sdist/
var/
test.sqlite
*.egg-info/
*.egg*
*.ini
Expand Down
16 changes: 8 additions & 8 deletions peewee_async/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,8 @@

from peewee_async.aio_model import AioModel, aio_prefetch
from peewee_async.connection import connection_context
from peewee_async.databases import (
MySQLDatabase,
PostgresqlDatabase,
Psycopg3Database,
)
from peewee_async.pool import MysqlPoolBackend, PostgresqlPoolBackend
from peewee_async.databases import MySQLDatabase, PostgresqlDatabase, Psycopg3Database, SqliteDatabase
from peewee_async.pool import AioMysqlPoolBackend, AioPgPoolBackend, AioSqlitePoolBackend, PsycopgPoolBackend
from peewee_async.transactions import Transaction

__version__ = version("peewee-async")
Expand All @@ -39,10 +35,14 @@
"AioModel",
"aio_prefetch",
"connection_context",
"PostgresqlPoolBackend",
"MysqlPoolBackend",
"AioPgPoolBackend",
"AioMysqlPoolBackend",
"PsycopgPoolBackend",
"AioSqlitePoolBackend",
"SqliteDatabase",
]

register_database(PostgresqlDatabase, "postgres+pool+async", "postgresql+pool+async")
register_database(Psycopg3Database, "psycopg+pool+async", "psycopg+pool+async")
register_database(MySQLDatabase, "mysql+pool+async")
register_database(SqliteDatabase, "sqlite+pool+async")
15 changes: 12 additions & 3 deletions peewee_async/databases.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from peewee_async.result_wrappers import fetch_models

from .connection import ConnectionContextManager, connection_context
from .pool import MysqlPoolBackend, PoolBackend, PostgresqlPoolBackend, PsycopgPoolBackend
from .pool import AioMysqlPoolBackend, AioPgPoolBackend, AioSqlitePoolBackend, PoolBackend, PsycopgPoolBackend
from .transactions import Transaction
from .utils import CursorProtocol, __log__

Expand Down Expand Up @@ -358,12 +358,21 @@ class PostgresqlDatabase(AioPostgresDatabase, ext.PostgresqlExtDatabase):
https://aiopg.readthedocs.io/en/stable/
"""

pool_backend_cls = PostgresqlPoolBackend
pool_backend_cls = AioPgPoolBackend

def init_pool_params_defaults(self) -> None:
self.pool_params.update({"enable_json": True, "enable_hstore": self._register_hstore})


class SqliteDatabase(AioDatabase, peewee.SqliteDatabase):
pool_backend_cls = AioSqlitePoolBackend

async def aio_get_tables(self, schema: str | None = None) -> list[str]:
schema = schema or "main"
query = f'SELECT name FROM "{schema}".sqlite_master WHERE type=? ORDER BY name'
return [row for (row,) in await self.aio_execute_sql(query, ("table",), fetch_results=fetchall)]


class MySQLDatabase(AioDatabase, peewee.MySQLDatabase):
"""MySQL database driver providing **single drop-in sync**
connection and **async connections pool** interface.
Expand All @@ -389,7 +398,7 @@ class MySQLDatabase(AioDatabase, peewee.MySQLDatabase):
https://aiomysql.readthedocs.io/en/stable/
"""

pool_backend_cls = MysqlPoolBackend
pool_backend_cls = AioMysqlPoolBackend

def init_pool_params_defaults(self) -> None:
self.pool_params.update({"autocommit": True})
Expand Down
62 changes: 60 additions & 2 deletions peewee_async/pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import asyncio
from typing import Any, cast

import aiosqlite

from .utils import ConnectionProtocol, ModuleRequired, aiomysql, aiopg, format_dsn, psycopg, psycopg_pool


Expand Down Expand Up @@ -61,7 +63,7 @@ async def close(self) -> None:
...


class PostgresqlPoolBackend(PoolBackend):
class AioPgPoolBackend(PoolBackend):
"""Asynchronous database connection pool based on aiopg."""

required_modules = ["aiopg"]
Expand Down Expand Up @@ -141,7 +143,7 @@ async def close(self) -> None:
await self.pool.close()


class MysqlPoolBackend(PoolBackend):
class AioMysqlPoolBackend(PoolBackend):
"""Asynchronous database connection pool based on aiomysql."""

required_modules = ["aiomysql"]
Expand All @@ -168,3 +170,59 @@ async def close(self) -> None:
if self.pool is not None:
self.pool.terminate()
await self.pool.wait_closed()


class AioSqlitePool:
def __init__(self, database: str, **connect_params: Any) -> None:
self._opened_connections: set[aiosqlite.Connection] = set()
self.database = database
self.connect_params = connect_params
self._closed = False

async def acquire(self) -> aiosqlite.Connection:
if self._closed:
raise RuntimeError("Cannot acquire connection after closing pool")
return await aiosqlite.connect(database=self.database, isolation_level=None, **self.connect_params)

async def release(self, conn: aiosqlite.Connection) -> None:
await conn.close()

def has_acquired_connections(self) -> bool:
return len(self._opened_connections) > 0

async def close(self) -> None:
for c in self._opened_connections:
await self.release(c)
self._closed = True

@property
def closed(self) -> bool:
return self._closed


class AioSqlitePoolBackend(PoolBackend):
"""Asynchronous database connection pool based on aiosqlite."""

required_modules = ["aiosqlite"]

async def create(self) -> None:
self.pool: AioSqlitePool = AioSqlitePool(database=self.database, **self.connect_params)

async def acquire(self) -> ConnectionProtocol:
if self.pool is None:
await self.connect()
assert self.pool is not None, "Pool is not connected"
return cast("ConnectionProtocol", await self.pool.acquire())

async def release(self, conn: ConnectionProtocol) -> None:
assert self.pool is not None, "Pool is not connected"
await self.pool.release(cast("aiosqlite.Connection", conn))

def has_acquired_connections(self) -> bool:
if self.pool is not None:
return self.pool.has_acquired_connections()
return False

async def close(self) -> None:
if self.pool is not None:
await self.pool.close()
6 changes: 6 additions & 0 deletions peewee_async/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
except ImportError:
aiomysql = None


try:
import aiosqlite
except ImportError:
aiosqlite = None # type: ignore

__log__ = logging.getLogger("peewee.async")
__log__.addHandler(logging.NullHandler())

Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ mysql = [
psycopg = [
"psycopg[binary,pool]>=3.3.0",
]
sqlite = [
"aiosqlite>=0.22.1"
]
docs = [
"Sphinx>=8.1.3",
"sphinx-rtd-theme>=3.1.0",
Expand All @@ -36,6 +39,7 @@ dev = [
"peewee-async[postgresql]",
"peewee-async[mysql]",
"peewee-async[psycopg]",
"peewee-async[sqlite]",
"mypy>=1.19.0",
"ruff==0.15.7",
"types-PyMySQL>=1.1.0",
Expand Down
5 changes: 3 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def bound_models(database: AioDatabase) -> Generator[None]:
@pytest.fixture(scope="session", autouse=True)
async def create_tables() -> AsyncGenerator[None, None]:

databases = [_get_db(name) for name in ("psycopg-pool", "mysql-pool")]
databases = [_get_db(name) for name in ("psycopg-pool", "mysql-pool", "sqlite-pool")]
for database in databases:
with bound_models(database), database.allow_sync():
for model in ALL_MODELS:
Expand Down Expand Up @@ -95,6 +95,7 @@ async def db(request: pytest.FixtureRequest) -> AsyncGenerator[AioDatabase, None
]

MYSQL_DBS = ["mysql-pool"]
SQLITE_DBS = ["sqlite-pool"]


dbs_mysql = pytest.mark.parametrize("db", MYSQL_DBS, indirect=["db"])
Expand All @@ -103,5 +104,5 @@ async def db(request: pytest.FixtureRequest) -> AsyncGenerator[AioDatabase, None
dbs_postgres = pytest.mark.parametrize("db", PG_DBS, indirect=["db"])


dbs_all = pytest.mark.parametrize("db", PG_DBS + MYSQL_DBS, indirect=["db"])
dbs_all = pytest.mark.parametrize("db", PG_DBS + MYSQL_DBS + SQLITE_DBS, indirect=["db"])
transaction_methods = pytest.mark.parametrize("transaction_method", ["aio_transaction", "aio_atomic"])
8 changes: 8 additions & 0 deletions tests/db_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
from typing import Any

import peewee_async

Expand Down Expand Up @@ -30,19 +31,26 @@
"pool_params": {"minsize": 0, "maxsize": 5, "pool_recycle": 2},
}

SQLITE_DEFAULTS: dict[str, Any] = {
"database": "test.sqlite",
}

AIOPG_POOL = "aiopg-pool"
PSYCOPG_POOL = "psycopg-pool"
MYSQL_POOL = "mysql-pool"
SQLITE_POOL = "sqlite-pool"


DB_DEFAULTS = {
AIOPG_POOL: PG_DEFAULTS,
PSYCOPG_POOL: PSYCOPG_DEFAULTS,
MYSQL_POOL: MYSQL_DEFAULTS,
SQLITE_POOL: SQLITE_DEFAULTS,
}

DB_CLASSES = {
AIOPG_POOL: peewee_async.PostgresqlDatabase,
PSYCOPG_POOL: peewee_async.Psycopg3Database,
MYSQL_POOL: peewee_async.MySQLDatabase,
SQLITE_POOL: peewee_async.SqliteDatabase,
}
Loading