From 6d7ba58f880be618ade07f8ea080fe8c4bf8a896 Mon Sep 17 00:00:00 2001 From: cyfraeviolae Date: Wed, 3 Apr 2024 03:10:44 -0400 Subject: venv --- .../python3.11/site-packages/aiosqlite/__init__.py | 44 ++ .../aiosqlite/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 1054 bytes .../__pycache__/__version__.cpython-311.pyc | Bin 0 -> 370 bytes .../aiosqlite/__pycache__/context.cpython-311.pyc | Bin 0 -> 3486 bytes .../aiosqlite/__pycache__/core.cpython-311.pyc | Bin 0 -> 21080 bytes .../aiosqlite/__pycache__/cursor.cpython-311.pyc | Bin 0 -> 7080 bytes .../site-packages/aiosqlite/__version__.py | 7 + .../python3.11/site-packages/aiosqlite/context.py | 54 +++ .../lib/python3.11/site-packages/aiosqlite/core.py | 394 ++++++++++++++++++ .../python3.11/site-packages/aiosqlite/cursor.py | 118 ++++++ .../python3.11/site-packages/aiosqlite/py.typed | 0 .../site-packages/aiosqlite/tests/__init__.py | 4 + .../site-packages/aiosqlite/tests/__main__.py | 7 + .../tests/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 254 bytes .../tests/__pycache__/__main__.cpython-311.pyc | Bin 0 -> 390 bytes .../tests/__pycache__/helpers.cpython-311.pyc | Bin 0 -> 1482 bytes .../tests/__pycache__/perf.cpython-311.pyc | Bin 0 -> 14885 bytes .../tests/__pycache__/smoke.cpython-311.pyc | Bin 0 -> 44531 bytes .../site-packages/aiosqlite/tests/helpers.py | 29 ++ .../site-packages/aiosqlite/tests/perf.py | 203 +++++++++ .../site-packages/aiosqlite/tests/smoke.py | 452 +++++++++++++++++++++ 21 files changed, 1312 insertions(+) create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/__init__.py create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/__pycache__/__init__.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/__pycache__/__version__.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/__pycache__/context.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/__pycache__/core.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/__pycache__/cursor.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/__version__.py create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/context.py create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/core.py create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/cursor.py create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/py.typed create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/tests/__init__.py create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/tests/__main__.py create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/__init__.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/__main__.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/helpers.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/perf.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/smoke.cpython-311.pyc create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/tests/helpers.py create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/tests/perf.py create mode 100644 venv/lib/python3.11/site-packages/aiosqlite/tests/smoke.py (limited to 'venv/lib/python3.11/site-packages/aiosqlite') 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 Binary files /dev/null and b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/__init__.cpython-311.pyc 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 Binary files /dev/null and b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/__version__.cpython-311.pyc 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 Binary files /dev/null and b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/context.cpython-311.pyc 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 Binary files /dev/null and b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/core.cpython-311.pyc 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 Binary files /dev/null and b/venv/lib/python3.11/site-packages/aiosqlite/__pycache__/cursor.cpython-311.pyc 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 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 Binary files /dev/null and b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/__init__.cpython-311.pyc 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 Binary files /dev/null and b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/__main__.cpython-311.pyc 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 Binary files /dev/null and b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/helpers.cpython-311.pyc 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 Binary files /dev/null and b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/perf.cpython-311.pyc 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 Binary files /dev/null and b/venv/lib/python3.11/site-packages/aiosqlite/tests/__pycache__/smoke.cpython-311.pyc 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")]) -- cgit v1.2.3