peewee-async icon indicating copy to clipboard operation
peewee-async copied to clipboard

support SQLite

Open qwertyuiopasdfyhjkl opened this issue 3 years ago • 7 comments

Will there be support SQLite?

qwertyuiopasdfyhjkl avatar May 10 '22 16:05 qwertyuiopasdfyhjkl

I need it too

Regela avatar Oct 18 '22 15:10 Regela

https://gist.github.com/xsduan/09fb145da3da3a78f5ca844b155f27aa Maybe you should take the implementation from here?

Regela avatar Nov 11 '22 14:11 Regela

I adapted that script to the latest version, no idea how good it is, but it works

"""
Temporary module to allow for sqlite databases during development. Remove once
we get an actual database.

https://gist.github.com/xsduan/09fb145da3da3a78f5ca844b155f27aa
"""

import functools

import peewee
from peewee_async import AsyncDatabase
import playhouse.sqlite_ext as sqlite_ext

try:
    import aiosqlite
except ImportError:
    aiosqlite = None
    print("aiosqlite not found")

__all__ = ["SqliteDatabase", "SqliteExtDatabase"]

#do we need this?
#try:
#    asyncio_current_task = asyncio.current_task
#except AttributeError:
#    asyncio_current_task = asyncio.Task.current_task

class AsyncSqliteConnection:
    def __init__(self, *, database=None, loop=None, timeout=None, **kwargs):
        self._created_connections = []
        self.loop = loop
        self.database = database
        self.timeout = timeout
        self.connect_params = kwargs
        #print("init: ", self.connect_params)

    async def acquire(self):
        #print("acquire: ", self.database, **self.connect_params)
        conn = aiosqlite.connect(database=self.database, **self.connect_params)
        self._created_connections.append(conn)
        return await conn.__aenter__()

    async def release(self, conn):
        if conn in self._created_connections:
            self._created_connections.remove(conn)
        await conn.commit()
        await conn.__aexit__(None, None, None)

    async def connect(self):
        #print("connection: ", self.connect_params)
        pass

    async def close(self):
        for conn in self._created_connections:
            await conn.__aexit__(None, None, None)
        self._created_connections = []

    async def cursor(self, conn=None, *args, **kwargs):
        """Get a cursor for the specified transaction connection
        or acquire from the pool.
        """
        #print("cursor init: ", *args, **kwargs)

        in_transaction = conn is not None
        if not conn:
            conn = await self.acquire()
        #cursor = await conn.cursor()
        # cursor.release is a coroutine
        #cursor.release = self.release_cursor(  # pylint: disable = assignment-from-no-return
        #    cursor, in_transaction
        #)
        cursor = await conn.cursor(*args, **kwargs)
        #print("cursor ", cursor)
        cursor.release = functools.partial(
            self.release_cursor, cursor,
            in_transaction=in_transaction)
        return cursor

    async def release_cursor(self, cursor, in_transaction=False):
        conn = cursor._conn
        await cursor.__aexit__(None, None, None)
        if not in_transaction:
            await self.release(conn)


class AsyncSqliteMixin(AsyncDatabase):
    if aiosqlite:
        import sqlite3
        Error = sqlite3.Error

    def init_async(self, conn_class=AsyncSqliteConnection):
        #print("AsyncSqliteMixin: init_async")
        if not aiosqlite:
            raise Exception("Error, aiosqlite is not installed!")
        self._async_conn_cls = conn_class

    @property
    def connect_params_async(self):
        #print("connected_params_async")
        return {**self.connect_params}

    async def last_insert_id_async(self, cursor):
        """Get ID of last inserted row.
        """
        #print("last_insert_id_async")
        return cursor.lastrowid


class SqliteDatabase(AsyncSqliteMixin, peewee.SqliteDatabase):
    def init(self, database, **kwargs):
        #print("sqliteDatabase: ", database, **kwargs)

        super().init(database, **kwargs)
        self.init_async()


class SqliteExtDatabase(SqliteDatabase, sqlite_ext.SqliteExtDatabase):
    pass

blshkv avatar Dec 01 '22 01:12 blshkv

Hello @blshkv, there's a bug when using the atomic context manager:

peewee_async.py:915: RuntimeWarning: coroutine 'AsyncSqliteConnection.release' was never awaited
  self._async_conn.release(conn)
Object allocated at (most recent call last):
  File "/home/guido/socialtrading/etl/venv/lib/python3.9/site-packages/peewee_async.py", lineno 915
    self._async_conn.release(conn)

Actually, peewee_async calls release as a normal function and not as a coroutine (no await).

I can't think of a fix as the release behavior in the patch is deeply bounded to other coroutines (and obv different from the peewee-async connection interfaces, in which release is a function).

gdassori avatar Dec 20 '22 17:12 gdassori

The patch seems was adopted from postsql module, feel free to check it out and suggest the fix.

blshkv avatar Dec 21 '22 00:12 blshkv

There's a huge chain of coroutines under release, so the patch cannot be changed. Instead, I modified peewee_async

image

I encourage 05bit to release official SQLite support: it's crucial for unit testing.

My PR: https://github.com/05bit/peewee-async/pull/172

gdassori avatar Dec 21 '22 09:12 gdassori

If you need sync calls you can try to use conn._conn property:

conn._conn.close()
conn._conn.commit() # btw i doutbt this line should be in release function

kalombos avatar Dec 22 '22 10:12 kalombos