Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support SQLite #166

Open
qwertyuiopasdfyhjkl opened this issue May 10, 2022 · 7 comments
Open

support SQLite #166

qwertyuiopasdfyhjkl opened this issue May 10, 2022 · 7 comments

Comments

@qwertyuiopasdfyhjkl
Copy link

qwertyuiopasdfyhjkl commented May 10, 2022

Will there be support SQLite?

@Regela
Copy link

Regela commented Oct 18, 2022

I need it too

@Regela
Copy link

Regela commented Nov 11, 2022

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

@blshkv
Copy link

blshkv commented Dec 1, 2022

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

@gdassori
Copy link

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).

@blshkv
Copy link

blshkv commented Dec 21, 2022

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

@gdassori
Copy link

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: #172

@kalombos
Copy link
Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants