summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/aiosqlite
diff options
context:
space:
mode:
authorcyfraeviolae <cyfraeviolae>2024-04-03 03:10:44 -0400
committercyfraeviolae <cyfraeviolae>2024-04-03 03:10:44 -0400
commit6d7ba58f880be618ade07f8ea080fe8c4bf8a896 (patch)
treeb1c931051ffcebd2bd9d61d98d6233ffa289bbce /venv/lib/python3.11/site-packages/aiosqlite
parent4f884c9abc32990b4061a1bb6997b4b37e58ea0b (diff)
venv
Diffstat (limited to 'venv/lib/python3.11/site-packages/aiosqlite')
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/__init__.py44
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/__pycache__/__init__.cpython-311.pycbin0 -> 1054 bytes
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/__pycache__/__version__.cpython-311.pycbin0 -> 370 bytes
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/__pycache__/context.cpython-311.pycbin0 -> 3486 bytes
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/__pycache__/core.cpython-311.pycbin0 -> 21080 bytes
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/__pycache__/cursor.cpython-311.pycbin0 -> 7080 bytes
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/__version__.py7
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/context.py54
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/core.py394
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/cursor.py118
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/py.typed0
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/tests/__init__.py4
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/tests/__main__.py7
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/__init__.cpython-311.pycbin0 -> 254 bytes
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/__main__.cpython-311.pycbin0 -> 390 bytes
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/helpers.cpython-311.pycbin0 -> 1482 bytes
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/perf.cpython-311.pycbin0 -> 14885 bytes
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/smoke.cpython-311.pycbin0 -> 44531 bytes
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/tests/helpers.py29
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/tests/perf.py203
-rw-r--r--venv/lib/python3.11/site-packages/aiosqlite/tests/smoke.py452
21 files changed, 1312 insertions, 0 deletions
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/__init__.py b/venv/lib/python3.11/site-packages/aiosqlite/__init__.py
new file mode 100644
index 0000000..be7b7bd
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/__init__.py
@@ -0,0 +1,44 @@
+# Copyright 2022 Amethyst Reese
+# Licensed under the MIT license
+
+"""asyncio bridge to the standard sqlite3 module"""
+
+from sqlite3 import ( # pylint: disable=redefined-builtin
+ DatabaseError,
+ Error,
+ IntegrityError,
+ NotSupportedError,
+ OperationalError,
+ paramstyle,
+ ProgrammingError,
+ register_adapter,
+ register_converter,
+ Row,
+ sqlite_version,
+ sqlite_version_info,
+ Warning,
+)
+
+__author__ = "Amethyst Reese"
+from .__version__ import __version__
+from .core import connect, Connection, Cursor
+
+__all__ = [
+ "__version__",
+ "paramstyle",
+ "register_adapter",
+ "register_converter",
+ "sqlite_version",
+ "sqlite_version_info",
+ "connect",
+ "Connection",
+ "Cursor",
+ "Row",
+ "Warning",
+ "Error",
+ "DatabaseError",
+ "IntegrityError",
+ "ProgrammingError",
+ "OperationalError",
+ "NotSupportedError",
+]
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000..6def304
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/__init__.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/__version__.cpython-311.pyc b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/__version__.cpython-311.pyc
new file mode 100644
index 0000000..ddd888a
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/__version__.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/context.cpython-311.pyc b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/context.cpython-311.pyc
new file mode 100644
index 0000000..63efa3e
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/context.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/core.cpython-311.pyc b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/core.cpython-311.pyc
new file mode 100644
index 0000000..8814c50
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/core.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/cursor.cpython-311.pyc b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/cursor.cpython-311.pyc
new file mode 100644
index 0000000..020ac40
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/cursor.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/__version__.py b/venv/lib/python3.11/site-packages/aiosqlite/__version__.py
new file mode 100644
index 0000000..aabb3db
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/__version__.py
@@ -0,0 +1,7 @@
+"""
+This file is automatically generated by attribution.
+
+Do not edit manually. Get more info at https://attribution.omnilib.dev
+"""
+
+__version__ = "0.20.0"
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/context.py b/venv/lib/python3.11/site-packages/aiosqlite/context.py
new file mode 100644
index 0000000..316845f
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/context.py
@@ -0,0 +1,54 @@
+# Copyright 2018
+# Licensed under the MIT license
+
+
+from functools import wraps
+from typing import Any, AsyncContextManager, Callable, Coroutine, Generator, TypeVar
+
+from .cursor import Cursor
+
+_T = TypeVar("_T")
+
+
+class Result(AsyncContextManager[_T], Coroutine[Any, Any, _T]):
+ __slots__ = ("_coro", "_obj")
+
+ def __init__(self, coro: Coroutine[Any, Any, _T]):
+ self._coro = coro
+ self._obj: _T
+
+ def send(self, value) -> None:
+ return self._coro.send(value)
+
+ def throw(self, typ, val=None, tb=None) -> None:
+ if val is None:
+ return self._coro.throw(typ)
+
+ if tb is None:
+ return self._coro.throw(typ, val)
+
+ return self._coro.throw(typ, val, tb)
+
+ def close(self) -> None:
+ return self._coro.close()
+
+ def __await__(self) -> Generator[Any, None, _T]:
+ return self._coro.__await__()
+
+ async def __aenter__(self) -> _T:
+ self._obj = await self._coro
+ return self._obj
+
+ async def __aexit__(self, exc_type, exc, tb) -> None:
+ if isinstance(self._obj, Cursor):
+ await self._obj.close()
+
+
+def contextmanager(
+ method: Callable[..., Coroutine[Any, Any, _T]]
+) -> Callable[..., Result[_T]]:
+ @wraps(method)
+ def wrapper(self, *args, **kwargs) -> Result[_T]:
+ return Result(method(self, *args, **kwargs))
+
+ return wrapper
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/core.py b/venv/lib/python3.11/site-packages/aiosqlite/core.py
new file mode 100644
index 0000000..58c3fec
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/core.py
@@ -0,0 +1,394 @@
+# Copyright 2022 Amethyst Reese
+# Licensed under the MIT license
+
+"""
+Core implementation of aiosqlite proxies
+"""
+
+import asyncio
+import logging
+import sqlite3
+from functools import partial
+from pathlib import Path
+from queue import Empty, Queue, SimpleQueue
+from threading import Thread
+from typing import (
+ Any,
+ AsyncIterator,
+ Callable,
+ Generator,
+ Iterable,
+ Literal,
+ Optional,
+ Tuple,
+ Type,
+ Union,
+)
+from warnings import warn
+
+from .context import contextmanager
+from .cursor import Cursor
+
+__all__ = ["connect", "Connection", "Cursor"]
+
+LOG = logging.getLogger("aiosqlite")
+
+
+IsolationLevel = Optional[Literal["DEFERRED", "IMMEDIATE", "EXCLUSIVE"]]
+
+
+def set_result(fut: asyncio.Future, result: Any) -> None:
+ """Set the result of a future if it hasn't been set already."""
+ if not fut.done():
+ fut.set_result(result)
+
+
+def set_exception(fut: asyncio.Future, e: BaseException) -> None:
+ """Set the exception of a future if it hasn't been set already."""
+ if not fut.done():
+ fut.set_exception(e)
+
+
+_STOP_RUNNING_SENTINEL = object()
+
+
+class Connection(Thread):
+ def __init__(
+ self,
+ connector: Callable[[], sqlite3.Connection],
+ iter_chunk_size: int,
+ loop: Optional[asyncio.AbstractEventLoop] = None,
+ ) -> None:
+ super().__init__()
+ self._running = True
+ self._connection: Optional[sqlite3.Connection] = None
+ self._connector = connector
+ self._tx: SimpleQueue[Tuple[asyncio.Future, Callable[[], Any]]] = SimpleQueue()
+ self._iter_chunk_size = iter_chunk_size
+
+ if loop is not None:
+ warn(
+ "aiosqlite.Connection no longer uses the `loop` parameter",
+ DeprecationWarning,
+ )
+
+ def _stop_running(self):
+ self._running = False
+ # PEP 661 is not accepted yet, so we cannot type a sentinel
+ self._tx.put_nowait(_STOP_RUNNING_SENTINEL) # type: ignore[arg-type]
+
+ @property
+ def _conn(self) -> sqlite3.Connection:
+ if self._connection is None:
+ raise ValueError("no active connection")
+
+ return self._connection
+
+ def _execute_insert(self, sql: str, parameters: Any) -> Optional[sqlite3.Row]:
+ cursor = self._conn.execute(sql, parameters)
+ cursor.execute("SELECT last_insert_rowid()")
+ return cursor.fetchone()
+
+ def _execute_fetchall(self, sql: str, parameters: Any) -> Iterable[sqlite3.Row]:
+ cursor = self._conn.execute(sql, parameters)
+ return cursor.fetchall()
+
+ def run(self) -> None:
+ """
+ Execute function calls on a separate thread.
+
+ :meta private:
+ """
+ while True:
+ # Continues running until all queue items are processed,
+ # even after connection is closed (so we can finalize all
+ # futures)
+
+ tx_item = self._tx.get()
+ if tx_item is _STOP_RUNNING_SENTINEL:
+ break
+
+ future, function = tx_item
+
+ try:
+ LOG.debug("executing %s", function)
+ result = function()
+ LOG.debug("operation %s completed", function)
+ future.get_loop().call_soon_threadsafe(set_result, future, result)
+ except BaseException as e: # noqa B036
+ LOG.debug("returning exception %s", e)
+ future.get_loop().call_soon_threadsafe(set_exception, future, e)
+
+ async def _execute(self, fn, *args, **kwargs):
+ """Queue a function with the given arguments for execution."""
+ if not self._running or not self._connection:
+ raise ValueError("Connection closed")
+
+ function = partial(fn, *args, **kwargs)
+ future = asyncio.get_event_loop().create_future()
+
+ self._tx.put_nowait((future, function))
+
+ return await future
+
+ async def _connect(self) -> "Connection":
+ """Connect to the actual sqlite database."""
+ if self._connection is None:
+ try:
+ future = asyncio.get_event_loop().create_future()
+ self._tx.put_nowait((future, self._connector))
+ self._connection = await future
+ except Exception:
+ self._stop_running()
+ self._connection = None
+ raise
+
+ return self
+
+ def __await__(self) -> Generator[Any, None, "Connection"]:
+ self.start()
+ return self._connect().__await__()
+
+ async def __aenter__(self) -> "Connection":
+ return await self
+
+ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
+ await self.close()
+
+ @contextmanager
+ async def cursor(self) -> Cursor:
+ """Create an aiosqlite cursor wrapping a sqlite3 cursor object."""
+ return Cursor(self, await self._execute(self._conn.cursor))
+
+ async def commit(self) -> None:
+ """Commit the current transaction."""
+ await self._execute(self._conn.commit)
+
+ async def rollback(self) -> None:
+ """Roll back the current transaction."""
+ await self._execute(self._conn.rollback)
+
+ async def close(self) -> None:
+ """Complete queued queries/cursors and close the connection."""
+
+ if self._connection is None:
+ return
+
+ try:
+ await self._execute(self._conn.close)
+ except Exception:
+ LOG.info("exception occurred while closing connection")
+ raise
+ finally:
+ self._stop_running()
+ self._connection = None
+
+ @contextmanager
+ async def execute(
+ self, sql: str, parameters: Optional[Iterable[Any]] = None
+ ) -> Cursor:
+ """Helper to create a cursor and execute the given query."""
+ if parameters is None:
+ parameters = []
+ cursor = await self._execute(self._conn.execute, sql, parameters)
+ return Cursor(self, cursor)
+
+ @contextmanager
+ async def execute_insert(
+ self, sql: str, parameters: Optional[Iterable[Any]] = None
+ ) -> Optional[sqlite3.Row]:
+ """Helper to insert and get the last_insert_rowid."""
+ if parameters is None:
+ parameters = []
+ return await self._execute(self._execute_insert, sql, parameters)
+
+ @contextmanager
+ async def execute_fetchall(
+ self, sql: str, parameters: Optional[Iterable[Any]] = None
+ ) -> Iterable[sqlite3.Row]:
+ """Helper to execute a query and return all the data."""
+ if parameters is None:
+ parameters = []
+ return await self._execute(self._execute_fetchall, sql, parameters)
+
+ @contextmanager
+ async def executemany(
+ self, sql: str, parameters: Iterable[Iterable[Any]]
+ ) -> Cursor:
+ """Helper to create a cursor and execute the given multiquery."""
+ cursor = await self._execute(self._conn.executemany, sql, parameters)
+ return Cursor(self, cursor)
+
+ @contextmanager
+ async def executescript(self, sql_script: str) -> Cursor:
+ """Helper to create a cursor and execute a user script."""
+ cursor = await self._execute(self._conn.executescript, sql_script)
+ return Cursor(self, cursor)
+
+ async def interrupt(self) -> None:
+ """Interrupt pending queries."""
+ return self._conn.interrupt()
+
+ async def create_function(
+ self, name: str, num_params: int, func: Callable, deterministic: bool = False
+ ) -> None:
+ """
+ Create user-defined function that can be later used
+ within SQL statements. Must be run within the same thread
+ that query executions take place so instead of executing directly
+ against the connection, we defer this to `run` function.
+
+ If ``deterministic`` is true, the created function is marked as deterministic,
+ which allows SQLite to perform additional optimizations. This flag is supported
+ by SQLite 3.8.3 or higher, ``NotSupportedError`` will be raised if used with
+ older versions.
+ """
+ await self._execute(
+ self._conn.create_function,
+ name,
+ num_params,
+ func,
+ deterministic=deterministic,
+ )
+
+ @property
+ def in_transaction(self) -> bool:
+ return self._conn.in_transaction
+
+ @property
+ def isolation_level(self) -> Optional[str]:
+ return self._conn.isolation_level
+
+ @isolation_level.setter
+ def isolation_level(self, value: IsolationLevel) -> None:
+ self._conn.isolation_level = value
+
+ @property
+ def row_factory(self) -> Optional[Type]:
+ return self._conn.row_factory
+
+ @row_factory.setter
+ def row_factory(self, factory: Optional[Type]) -> None:
+ self._conn.row_factory = factory
+
+ @property
+ def text_factory(self) -> Callable[[bytes], Any]:
+ return self._conn.text_factory
+
+ @text_factory.setter
+ def text_factory(self, factory: Callable[[bytes], Any]) -> None:
+ self._conn.text_factory = factory
+
+ @property
+ def total_changes(self) -> int:
+ return self._conn.total_changes
+
+ async def enable_load_extension(self, value: bool) -> None:
+ await self._execute(self._conn.enable_load_extension, value) # type: ignore
+
+ async def load_extension(self, path: str):
+ await self._execute(self._conn.load_extension, path) # type: ignore
+
+ async def set_progress_handler(
+ self, handler: Callable[[], Optional[int]], n: int
+ ) -> None:
+ await self._execute(self._conn.set_progress_handler, handler, n)
+
+ async def set_trace_callback(self, handler: Callable) -> None:
+ await self._execute(self._conn.set_trace_callback, handler)
+
+ async def iterdump(self) -> AsyncIterator[str]:
+ """
+ Return an async iterator to dump the database in SQL text format.
+
+ Example::
+
+ async for line in db.iterdump():
+ ...
+
+ """
+ dump_queue: Queue = Queue()
+
+ def dumper():
+ try:
+ for line in self._conn.iterdump():
+ dump_queue.put_nowait(line)
+ dump_queue.put_nowait(None)
+
+ except Exception:
+ LOG.exception("exception while dumping db")
+ dump_queue.put_nowait(None)
+ raise
+
+ fut = self._execute(dumper)
+ task = asyncio.ensure_future(fut)
+
+ while True:
+ try:
+ line: Optional[str] = dump_queue.get_nowait()
+ if line is None:
+ break
+ yield line
+
+ except Empty:
+ if task.done():
+ LOG.warning("iterdump completed unexpectedly")
+ break
+
+ await asyncio.sleep(0.01)
+
+ await task
+
+ async def backup(
+ self,
+ target: Union["Connection", sqlite3.Connection],
+ *,
+ pages: int = 0,
+ progress: Optional[Callable[[int, int, int], None]] = None,
+ name: str = "main",
+ sleep: float = 0.250,
+ ) -> None:
+ """
+ Make a backup of the current database to the target database.
+
+ Takes either a standard sqlite3 or aiosqlite Connection object as the target.
+ """
+ if isinstance(target, Connection):
+ target = target._conn
+
+ await self._execute(
+ self._conn.backup,
+ target,
+ pages=pages,
+ progress=progress,
+ name=name,
+ sleep=sleep,
+ )
+
+
+def connect(
+ database: Union[str, Path],
+ *,
+ iter_chunk_size=64,
+ loop: Optional[asyncio.AbstractEventLoop] = None,
+ **kwargs: Any,
+) -> Connection:
+ """Create and return a connection proxy to the sqlite database."""
+
+ if loop is not None:
+ warn(
+ "aiosqlite.connect() no longer uses the `loop` parameter",
+ DeprecationWarning,
+ )
+
+ def connector() -> sqlite3.Connection:
+ if isinstance(database, str):
+ loc = database
+ elif isinstance(database, bytes):
+ loc = database.decode("utf-8")
+ else:
+ loc = str(database)
+
+ return sqlite3.connect(loc, **kwargs)
+
+ return Connection(connector, iter_chunk_size)
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/cursor.py b/venv/lib/python3.11/site-packages/aiosqlite/cursor.py
new file mode 100644
index 0000000..197938f
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/cursor.py
@@ -0,0 +1,118 @@
+# Copyright 2022 Amethyst Reese
+# Licensed under the MIT license
+
+import sqlite3
+from typing import (
+ Any,
+ AsyncIterator,
+ Callable,
+ Iterable,
+ Optional,
+ Tuple,
+ Type,
+ TYPE_CHECKING,
+)
+
+if TYPE_CHECKING:
+ from .core import Connection
+
+
+class Cursor:
+ def __init__(self, conn: "Connection", cursor: sqlite3.Cursor) -> None:
+ self.iter_chunk_size = conn._iter_chunk_size
+ self._conn = conn
+ self._cursor = cursor
+
+ def __aiter__(self) -> AsyncIterator[sqlite3.Row]:
+ """The cursor proxy is also an async iterator."""
+ return self._fetch_chunked()
+
+ async def _fetch_chunked(self):
+ while True:
+ rows = await self.fetchmany(self.iter_chunk_size)
+ if not rows:
+ return
+ for row in rows:
+ yield row
+
+ async def _execute(self, fn, *args, **kwargs):
+ """Execute the given function on the shared connection's thread."""
+ return await self._conn._execute(fn, *args, **kwargs)
+
+ async def execute(
+ self, sql: str, parameters: Optional[Iterable[Any]] = None
+ ) -> "Cursor":
+ """Execute the given query."""
+ if parameters is None:
+ parameters = []
+ await self._execute(self._cursor.execute, sql, parameters)
+ return self
+
+ async def executemany(
+ self, sql: str, parameters: Iterable[Iterable[Any]]
+ ) -> "Cursor":
+ """Execute the given multiquery."""
+ await self._execute(self._cursor.executemany, sql, parameters)
+ return self
+
+ async def executescript(self, sql_script: str) -> "Cursor":
+ """Execute a user script."""
+ await self._execute(self._cursor.executescript, sql_script)
+ return self
+
+ async def fetchone(self) -> Optional[sqlite3.Row]:
+ """Fetch a single row."""
+ return await self._execute(self._cursor.fetchone)
+
+ async def fetchmany(self, size: Optional[int] = None) -> Iterable[sqlite3.Row]:
+ """Fetch up to `cursor.arraysize` number of rows."""
+ args: Tuple[int, ...] = ()
+ if size is not None:
+ args = (size,)
+ return await self._execute(self._cursor.fetchmany, *args)
+
+ async def fetchall(self) -> Iterable[sqlite3.Row]:
+ """Fetch all remaining rows."""
+ return await self._execute(self._cursor.fetchall)
+
+ async def close(self) -> None:
+ """Close the cursor."""
+ await self._execute(self._cursor.close)
+
+ @property
+ def rowcount(self) -> int:
+ return self._cursor.rowcount
+
+ @property
+ def lastrowid(self) -> Optional[int]:
+ return self._cursor.lastrowid
+
+ @property
+ def arraysize(self) -> int:
+ return self._cursor.arraysize
+
+ @arraysize.setter
+ def arraysize(self, value: int) -> None:
+ self._cursor.arraysize = value
+
+ @property
+ def description(self) -> Tuple[Tuple[str, None, None, None, None, None, None], ...]:
+ return self._cursor.description
+
+ @property
+ def row_factory(self) -> Optional[Callable[[sqlite3.Cursor, sqlite3.Row], object]]:
+ return self._cursor.row_factory
+
+ @row_factory.setter
+ def row_factory(self, factory: Optional[Type]) -> None:
+ self._cursor.row_factory = factory
+
+ @property
+ def connection(self) -> sqlite3.Connection:
+ return self._cursor.connection
+
+ async def __aenter__(self):
+ return self
+
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
+ await self.close()
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/py.typed b/venv/lib/python3.11/site-packages/aiosqlite/py.typed
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/py.typed
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/tests/__init__.py b/venv/lib/python3.11/site-packages/aiosqlite/tests/__init__.py
new file mode 100644
index 0000000..4b173f6
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/tests/__init__.py
@@ -0,0 +1,4 @@
+# Copyright 2022 Amethyst Reese
+# Licensed under the MIT license
+
+from .smoke import SmokeTest
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/tests/__main__.py b/venv/lib/python3.11/site-packages/aiosqlite/tests/__main__.py
new file mode 100644
index 0000000..648131e
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/tests/__main__.py
@@ -0,0 +1,7 @@
+# Copyright 2022 Amethyst Reese
+# Licensed under the MIT license
+
+import unittest
+
+if __name__ == "__main__":
+ unittest.main(module="aiosqlite.tests", verbosity=2)
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000..63612cd
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/__init__.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/__main__.cpython-311.pyc b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/__main__.cpython-311.pyc
new file mode 100644
index 0000000..cf58dbd
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/__main__.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/helpers.cpython-311.pyc b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/helpers.cpython-311.pyc
new file mode 100644
index 0000000..271f6d7
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/helpers.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/perf.cpython-311.pyc b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/perf.cpython-311.pyc
new file mode 100644
index 0000000..f4cedc0
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/perf.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/smoke.cpython-311.pyc b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/smoke.cpython-311.pyc
new file mode 100644
index 0000000..1d4c26a
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/smoke.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/tests/helpers.py b/venv/lib/python3.11/site-packages/aiosqlite/tests/helpers.py
new file mode 100644
index 0000000..f7b53fe
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/tests/helpers.py
@@ -0,0 +1,29 @@
+# Copyright 2022 Amethyst Reese
+# Licensed under the MIT license
+
+import logging
+import sys
+
+
+def setup_logger():
+ log = logging.getLogger("")
+ log.setLevel(logging.INFO)
+
+ logging.addLevelName(logging.ERROR, "E")
+ logging.addLevelName(logging.WARNING, "W")
+ logging.addLevelName(logging.INFO, "I")
+ logging.addLevelName(logging.DEBUG, "V")
+
+ date_fmt = r"%H:%M:%S"
+ verbose_fmt = (
+ "%(asctime)s,%(msecs)d %(levelname)s "
+ "%(module)s:%(funcName)s():%(lineno)d "
+ "%(message)s"
+ )
+
+ handler = logging.StreamHandler(sys.stdout)
+ handler.setLevel(logging.INFO)
+ handler.setFormatter(logging.Formatter(verbose_fmt, date_fmt))
+ log.addHandler(handler)
+
+ return log
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/tests/perf.py b/venv/lib/python3.11/site-packages/aiosqlite/tests/perf.py
new file mode 100644
index 0000000..08f0335
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/tests/perf.py
@@ -0,0 +1,203 @@
+# Copyright 2022 Amethyst Reese
+# Licensed under the MIT license
+
+"""
+Simple perf tests for aiosqlite and the asyncio run loop.
+"""
+import string
+import tempfile
+import time
+
+from unittest import IsolatedAsyncioTestCase as TestCase
+
+import aiosqlite
+from .smoke import setup_logger
+
+TEST_DB = ":memory:"
+TARGET = 2.0
+RESULTS = {}
+
+
+def timed(fn, name=None):
+ """
+ Decorator for perf testing a block of async code.
+
+ Expects the wrapped function to return an async generator.
+ The generator should do setup, then yield when ready to start perf testing.
+ The decorator will then pump the generator repeatedly until the target
+ time has been reached, then close the generator and print perf results.
+ """
+
+ name = name or fn.__name__
+
+ async def wrapper(*args, **kwargs):
+ gen = fn(*args, **kwargs)
+
+ await gen.asend(None)
+ count = 0
+ before = time.time()
+
+ while True:
+ count += 1
+ value = time.time() - before < TARGET
+ try:
+ if value:
+ await gen.asend(value)
+ else:
+ await gen.aclose()
+ break
+
+ except StopAsyncIteration:
+ break
+
+ except Exception as e:
+ print(f"exception occurred: {e}")
+ return
+
+ duration = time.time() - before
+
+ RESULTS[name] = (count, duration)
+
+ return wrapper
+
+
+class PerfTest(TestCase):
+ @classmethod
+ def setUpClass(cls):
+ print(f"Running perf tests for at least {TARGET:.1f}s each...")
+ setup_logger()
+
+ @classmethod
+ def tearDownClass(cls):
+ print(f"\n{'Perf Test':<25} Iterations Duration {'Rate':>11}")
+ for name in sorted(RESULTS):
+ count, duration = RESULTS[name]
+ rate = count / duration
+ name = name.replace("test_", "")
+ print(f"{name:<25} {count:>10} {duration:>7.1f}s {rate:>9.1f}/s")
+
+ @timed
+ async def test_connection_memory(self):
+ while True:
+ yield
+ async with aiosqlite.connect(TEST_DB):
+ pass
+
+ @timed
+ async def test_connection_file(self):
+ with tempfile.NamedTemporaryFile(delete=False) as tf:
+ path = tf.name
+ tf.close()
+
+ async with aiosqlite.connect(path) as db:
+ await db.execute(
+ "create table perf (i integer primary key asc, k integer)"
+ )
+ await db.execute("insert into perf (k) values (2), (3)")
+ await db.commit()
+
+ while True:
+ yield
+ async with aiosqlite.connect(path):
+ pass
+
+ @timed
+ async def test_atomics(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.execute("create table perf (i integer primary key asc, k integer)")
+ await db.execute("insert into perf (k) values (2), (3)")
+ await db.commit()
+
+ while True:
+ yield
+ async with db.execute("select last_insert_rowid()") as cursor:
+ await cursor.fetchone()
+
+ @timed
+ async def test_inserts(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.execute("create table perf (i integer primary key asc, k integer)")
+ await db.commit()
+
+ while True:
+ yield
+ await db.execute("insert into perf (k) values (1), (2), (3)")
+ await db.commit()
+
+ @timed
+ async def test_insert_ids(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.execute("create table perf (i integer primary key asc, k integer)")
+ await db.commit()
+
+ while True:
+ yield
+ cursor = await db.execute("insert into perf (k) values (1)")
+ await cursor.execute("select last_insert_rowid()")
+ await cursor.fetchone()
+ await db.commit()
+
+ @timed
+ async def test_insert_macro_ids(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.execute("create table perf (i integer primary key asc, k integer)")
+ await db.commit()
+
+ while True:
+ yield
+ await db.execute_insert("insert into perf (k) values (1)")
+ await db.commit()
+
+ @timed
+ async def test_select(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.execute("create table perf (i integer primary key asc, k integer)")
+ for i in range(100):
+ await db.execute("insert into perf (k) values (%d)" % (i,))
+ await db.commit()
+
+ while True:
+ yield
+ cursor = await db.execute("select i, k from perf")
+ assert len(await cursor.fetchall()) == 100
+
+ @timed
+ async def test_select_macro(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.execute("create table perf (i integer primary key asc, k integer)")
+ for i in range(100):
+ await db.execute("insert into perf (k) values (%d)" % (i,))
+ await db.commit()
+
+ while True:
+ yield
+ assert len(await db.execute_fetchall("select i, k from perf")) == 100
+
+ async def test_iterable_cursor_perf(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.execute(
+ "create table ic_perf ("
+ "i integer primary key asc, k integer, a integer, b integer, c char(16))"
+ )
+ for batch in range(128): # add 128k rows
+ r_start = batch * 1024
+ await db.executemany(
+ "insert into ic_perf (k, a, b, c) values(?, 1, 2, ?)",
+ [
+ *[
+ (i, string.ascii_lowercase)
+ for i in range(r_start, r_start + 1024)
+ ]
+ ],
+ )
+ await db.commit()
+
+ async def test_perf(chunk_size: int):
+ while True:
+ async with db.execute("SELECT * FROM ic_perf") as cursor:
+ cursor.iter_chunk_size = chunk_size
+ async for _ in cursor:
+ yield
+
+ for chunk_size in [2**i for i in range(4, 11)]:
+ await timed(test_perf, f"iterable_cursor @ {chunk_size}")(chunk_size)
diff --git a/venv/lib/python3.11/site-packages/aiosqlite/tests/smoke.py b/venv/lib/python3.11/site-packages/aiosqlite/tests/smoke.py
new file mode 100644
index 0000000..f42106c
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/aiosqlite/tests/smoke.py
@@ -0,0 +1,452 @@
+# Copyright 2022 Amethyst Reese
+# Licensed under the MIT license
+import asyncio
+import sqlite3
+from pathlib import Path
+from sqlite3 import OperationalError
+from threading import Thread
+from unittest import IsolatedAsyncioTestCase as TestCase, SkipTest
+
+import aiosqlite
+from .helpers import setup_logger
+
+TEST_DB = Path("test.db")
+
+# pypy uses non-standard text factory for low-level sqlite implementation
+try:
+ from _sqlite3 import _unicode_text_factory as default_text_factory
+except ImportError:
+ default_text_factory = str
+
+
+class SmokeTest(TestCase):
+ @classmethod
+ def setUpClass(cls):
+ setup_logger()
+
+ def setUp(self):
+ if TEST_DB.exists():
+ TEST_DB.unlink()
+
+ def tearDown(self):
+ if TEST_DB.exists():
+ TEST_DB.unlink()
+
+ async def test_connection_await(self):
+ db = await aiosqlite.connect(TEST_DB)
+ self.assertIsInstance(db, aiosqlite.Connection)
+
+ async with db.execute("select 1, 2") as cursor:
+ rows = await cursor.fetchall()
+ self.assertEqual(rows, [(1, 2)])
+
+ await db.close()
+
+ async def test_connection_context(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ self.assertIsInstance(db, aiosqlite.Connection)
+
+ async with db.execute("select 1, 2") as cursor:
+ rows = await cursor.fetchall()
+ self.assertEqual(rows, [(1, 2)])
+
+ async def test_connection_locations(self):
+ class Fake: # pylint: disable=too-few-public-methods
+ def __str__(self):
+ return str(TEST_DB)
+
+ locs = ("test.db", b"test.db", Path("test.db"), Fake())
+
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.execute("create table foo (i integer, k integer)")
+ await db.execute("insert into foo (i, k) values (1, 5)")
+ await db.commit()
+
+ cursor = await db.execute("select * from foo")
+ rows = await cursor.fetchall()
+
+ for loc in locs:
+ async with aiosqlite.connect(loc) as db:
+ cursor = await db.execute("select * from foo")
+ self.assertEqual(await cursor.fetchall(), rows)
+
+ async def test_multiple_connections(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.execute(
+ "create table multiple_connections "
+ "(i integer primary key asc, k integer)"
+ )
+
+ async def do_one_conn(i):
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.execute("insert into multiple_connections (k) values (?)", [i])
+ await db.commit()
+
+ await asyncio.gather(*[do_one_conn(i) for i in range(10)])
+
+ async with aiosqlite.connect(TEST_DB) as db:
+ cursor = await db.execute("select * from multiple_connections")
+ rows = await cursor.fetchall()
+
+ assert len(rows) == 10
+
+ async def test_multiple_queries(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.execute(
+ "create table multiple_queries "
+ "(i integer primary key asc, k integer)"
+ )
+
+ await asyncio.gather(
+ *[
+ db.execute("insert into multiple_queries (k) values (?)", [i])
+ for i in range(10)
+ ]
+ )
+
+ await db.commit()
+
+ async with aiosqlite.connect(TEST_DB) as db:
+ cursor = await db.execute("select * from multiple_queries")
+ rows = await cursor.fetchall()
+
+ assert len(rows) == 10
+
+ async def test_iterable_cursor(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ cursor = await db.cursor()
+ await cursor.execute(
+ "create table iterable_cursor " "(i integer primary key asc, k integer)"
+ )
+ await cursor.executemany(
+ "insert into iterable_cursor (k) values (?)", [[i] for i in range(10)]
+ )
+ await db.commit()
+
+ async with aiosqlite.connect(TEST_DB) as db:
+ cursor = await db.execute("select * from iterable_cursor")
+ rows = []
+ async for row in cursor:
+ rows.append(row)
+
+ assert len(rows) == 10
+
+ async def test_multi_loop_usage(self):
+ results = {}
+
+ def runner(k, conn):
+ async def query():
+ async with conn.execute("select * from foo") as cursor:
+ rows = await cursor.fetchall()
+ self.assertEqual(len(rows), 2)
+ return rows
+
+ with self.subTest(k):
+ loop = asyncio.new_event_loop()
+ rows = loop.run_until_complete(query())
+ loop.close()
+ results[k] = rows
+
+ async with aiosqlite.connect(":memory:") as db:
+ await db.execute("create table foo (id int, name varchar)")
+ await db.execute(
+ "insert into foo values (?, ?), (?, ?)", (1, "Sally", 2, "Janet")
+ )
+ await db.commit()
+
+ threads = [Thread(target=runner, args=(k, db)) for k in range(4)]
+ for thread in threads:
+ thread.start()
+ for thread in threads:
+ thread.join()
+
+ self.assertEqual(len(results), 4)
+ for rows in results.values():
+ self.assertEqual(len(rows), 2)
+
+ async def test_context_cursor(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ async with db.cursor() as cursor:
+ await cursor.execute(
+ "create table context_cursor "
+ "(i integer primary key asc, k integer)"
+ )
+ await cursor.executemany(
+ "insert into context_cursor (k) values (?)",
+ [[i] for i in range(10)],
+ )
+ await db.commit()
+
+ async with aiosqlite.connect(TEST_DB) as db:
+ async with db.execute("select * from context_cursor") as cursor:
+ rows = []
+ async for row in cursor:
+ rows.append(row)
+
+ assert len(rows) == 10
+
+ async def test_cursor_return_self(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ cursor = await db.cursor()
+
+ result = await cursor.execute(
+ "create table test_cursor_return_self (i integer, k integer)"
+ )
+ self.assertEqual(result, cursor, "cursor execute returns itself")
+
+ result = await cursor.executemany(
+ "insert into test_cursor_return_self values (?, ?)", [(1, 1), (2, 2)]
+ )
+ self.assertEqual(result, cursor)
+
+ result = await cursor.executescript(
+ "insert into test_cursor_return_self values (3, 3);"
+ "insert into test_cursor_return_self values (4, 4);"
+ "insert into test_cursor_return_self values (5, 5);"
+ )
+ self.assertEqual(result, cursor)
+
+ async def test_connection_properties(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ self.assertEqual(db.total_changes, 0)
+
+ async with db.cursor() as cursor:
+ self.assertFalse(db.in_transaction)
+ await cursor.execute(
+ "create table test_properties "
+ "(i integer primary key asc, k integer, d text)"
+ )
+ await cursor.execute(
+ "insert into test_properties (k, d) values (1, 'hi')"
+ )
+ self.assertTrue(db.in_transaction)
+ await db.commit()
+ self.assertFalse(db.in_transaction)
+
+ self.assertEqual(db.total_changes, 1)
+
+ self.assertIsNone(db.row_factory)
+ self.assertEqual(db.text_factory, default_text_factory)
+
+ async with db.cursor() as cursor:
+ await cursor.execute("select * from test_properties")
+ row = await cursor.fetchone()
+ self.assertIsInstance(row, tuple)
+ self.assertEqual(row, (1, 1, "hi"))
+ with self.assertRaises(TypeError):
+ _ = row["k"]
+
+ async with db.cursor() as cursor:
+ cursor.row_factory = aiosqlite.Row
+ self.assertEqual(cursor.row_factory, aiosqlite.Row)
+ await cursor.execute("select * from test_properties")
+ row = await cursor.fetchone()
+ self.assertIsInstance(row, aiosqlite.Row)
+ self.assertEqual(row[1], 1)
+ self.assertEqual(row[2], "hi")
+ self.assertEqual(row["k"], 1)
+ self.assertEqual(row["d"], "hi")
+
+ db.row_factory = aiosqlite.Row
+ db.text_factory = bytes
+ self.assertEqual(db.row_factory, aiosqlite.Row)
+ self.assertEqual(db.text_factory, bytes)
+
+ async with db.cursor() as cursor:
+ await cursor.execute("select * from test_properties")
+ row = await cursor.fetchone()
+ self.assertIsInstance(row, aiosqlite.Row)
+ self.assertEqual(row[1], 1)
+ self.assertEqual(row[2], b"hi")
+ self.assertEqual(row["k"], 1)
+ self.assertEqual(row["d"], b"hi")
+
+ async def test_fetch_all(self):
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.execute(
+ "create table test_fetch_all (i integer primary key asc, k integer)"
+ )
+ await db.execute(
+ "insert into test_fetch_all (k) values (10), (24), (16), (32)"
+ )
+ await db.commit()
+
+ async with aiosqlite.connect(TEST_DB) as db:
+ cursor = await db.execute("select k from test_fetch_all where k < 30")
+ rows = await cursor.fetchall()
+ self.assertEqual(rows, [(10,), (24,), (16,)])
+
+ async def test_enable_load_extension(self):
+ """Assert that after enabling extension loading, they can be loaded"""
+ async with aiosqlite.connect(TEST_DB) as db:
+ try:
+ await db.enable_load_extension(True)
+ await db.load_extension("test")
+ except OperationalError as e:
+ assert "not authorized" not in e.args
+ except AttributeError as e:
+ raise SkipTest(
+ "python was not compiled with sqlite3 "
+ "extension support, so we can't test it"
+ ) from e
+
+ async def test_set_progress_handler(self):
+ """
+ Assert that after setting a progress handler returning 1, DB operations are aborted
+ """
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.set_progress_handler(lambda: 1, 1)
+ with self.assertRaises(OperationalError):
+ await db.execute(
+ "create table test_progress_handler (i integer primary key asc, k integer)"
+ )
+
+ async def test_create_function(self):
+ """Assert that after creating a custom function, it can be used"""
+
+ def no_arg():
+ return "no arg"
+
+ def one_arg(num):
+ return num * 2
+
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.create_function("no_arg", 0, no_arg)
+ await db.create_function("one_arg", 1, one_arg)
+
+ async with db.execute("SELECT no_arg();") as res:
+ row = await res.fetchone()
+ self.assertEqual(row[0], "no arg")
+
+ async with db.execute("SELECT one_arg(10);") as res:
+ row = await res.fetchone()
+ self.assertEqual(row[0], 20)
+
+ async def test_create_function_deterministic(self):
+ """Assert that after creating a deterministic custom function, it can be used.
+
+ https://sqlite.org/deterministic.html
+ """
+
+ def one_arg(num):
+ return num * 2
+
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.create_function("one_arg", 1, one_arg, deterministic=True)
+ await db.execute("create table foo (id int, bar int)")
+
+ # Non-deterministic functions cannot be used in indexes
+ await db.execute("create index t on foo(one_arg(bar))")
+
+ async def test_set_trace_callback(self):
+ statements = []
+
+ def callback(statement: str):
+ statements.append(statement)
+
+ async with aiosqlite.connect(TEST_DB) as db:
+ await db.set_trace_callback(callback)
+
+ await db.execute("select 10")
+ self.assertIn("select 10", statements)
+
+ async def test_connect_error(self):
+ bad_db = Path("/something/that/shouldnt/exist.db")
+ with self.assertRaisesRegex(OperationalError, "unable to open database"):
+ async with aiosqlite.connect(bad_db) as db:
+ self.assertIsNone(db) # should never be reached
+
+ with self.assertRaisesRegex(OperationalError, "unable to open database"):
+ await aiosqlite.connect(bad_db)
+
+ async def test_iterdump(self):
+ async with aiosqlite.connect(":memory:") as db:
+ await db.execute("create table foo (i integer, k charvar(250))")
+ await db.executemany(
+ "insert into foo values (?, ?)", [(1, "hello"), (2, "world")]
+ )
+
+ lines = [line async for line in db.iterdump()]
+ self.assertEqual(
+ lines,
+ [
+ "BEGIN TRANSACTION;",
+ "CREATE TABLE foo (i integer, k charvar(250));",
+ "INSERT INTO \"foo\" VALUES(1,'hello');",
+ "INSERT INTO \"foo\" VALUES(2,'world');",
+ "COMMIT;",
+ ],
+ )
+
+ async def test_cursor_on_closed_connection(self):
+ db = await aiosqlite.connect(TEST_DB)
+
+ cursor = await db.execute("select 1, 2")
+ await db.close()
+ with self.assertRaisesRegex(ValueError, "Connection closed"):
+ await cursor.fetchall()
+ with self.assertRaisesRegex(ValueError, "Connection closed"):
+ await cursor.fetchall()
+
+ async def test_cursor_on_closed_connection_loop(self):
+ db = await aiosqlite.connect(TEST_DB)
+
+ cursor = await db.execute("select 1, 2")
+ tasks = []
+ for i in range(100):
+ if i == 50:
+ tasks.append(asyncio.ensure_future(db.close()))
+ tasks.append(asyncio.ensure_future(cursor.fetchall()))
+ for task in tasks:
+ try:
+ await task
+ except sqlite3.ProgrammingError:
+ pass
+
+ async def test_close_twice(self):
+ db = await aiosqlite.connect(TEST_DB)
+
+ await db.close()
+
+ # no error
+ await db.close()
+
+ async def test_backup_aiosqlite(self):
+ def progress(a, b, c):
+ print(a, b, c)
+
+ async with aiosqlite.connect(":memory:") as db1, aiosqlite.connect(
+ ":memory:"
+ ) as db2:
+ await db1.execute("create table foo (i integer, k charvar(250))")
+ await db1.executemany(
+ "insert into foo values (?, ?)", [(1, "hello"), (2, "world")]
+ )
+ await db1.commit()
+
+ with self.assertRaisesRegex(OperationalError, "no such table: foo"):
+ await db2.execute("select * from foo")
+
+ await db1.backup(db2, progress=progress)
+
+ async with db2.execute("select * from foo") as cursor:
+ rows = await cursor.fetchall()
+ self.assertEqual(rows, [(1, "hello"), (2, "world")])
+
+ async def test_backup_sqlite(self):
+ async with aiosqlite.connect(":memory:") as db1:
+ with sqlite3.connect(":memory:") as db2:
+ await db1.execute("create table foo (i integer, k charvar(250))")
+ await db1.executemany(
+ "insert into foo values (?, ?)", [(1, "hello"), (2, "world")]
+ )
+ await db1.commit()
+
+ with self.assertRaisesRegex(OperationalError, "no such table: foo"):
+ db2.execute("select * from foo")
+
+ await db1.backup(db2)
+
+ cursor = db2.execute("select * from foo")
+ rows = cursor.fetchall()
+ self.assertEqual(rows, [(1, "hello"), (2, "world")])