sqlite-anyio 0.2.9__tar.gz → 0.2.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sqlite_anyio-0.2.9 → sqlite_anyio-0.2.10}/PKG-INFO +2 -2
- {sqlite_anyio-0.2.9 → sqlite_anyio-0.2.10}/pyproject.toml +2 -2
- {sqlite_anyio-0.2.9 → sqlite_anyio-0.2.10}/src/sqlite_anyio/sqlite.py +18 -89
- {sqlite_anyio-0.2.9 → sqlite_anyio-0.2.10}/LICENSE +0 -0
- {sqlite_anyio-0.2.9 → sqlite_anyio-0.2.10}/README.md +0 -0
- {sqlite_anyio-0.2.9 → sqlite_anyio-0.2.10}/src/sqlite_anyio/__init__.py +0 -0
- {sqlite_anyio-0.2.9 → sqlite_anyio-0.2.10}/src/sqlite_anyio/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: sqlite-anyio
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.10
|
|
4
4
|
Summary: Asynchronous client for SQLite using AnyIO
|
|
5
5
|
Author: Alex Grönholm, David Brochart
|
|
6
6
|
Author-email: Alex Grönholm <alex.gronholm@nextday.fi>, David Brochart <david.brochart@gmail.com>
|
|
@@ -35,7 +35,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
35
35
|
Classifier: Programming Language :: Python :: 3.12
|
|
36
36
|
Classifier: Programming Language :: Python :: 3.13
|
|
37
37
|
Classifier: Programming Language :: Python :: 3.14
|
|
38
|
-
Requires-Dist: anyio>=4.
|
|
38
|
+
Requires-Dist: anyio>=4.0,<5.0
|
|
39
39
|
Requires-Dist: typing-extensions>=4.15.0
|
|
40
40
|
Requires-Python: >=3.10
|
|
41
41
|
Project-URL: Source, https://github.com/davidbrochart/sqlite-anyio
|
|
@@ -6,7 +6,7 @@ build-backend = "uv_build"
|
|
|
6
6
|
name = "sqlite-anyio"
|
|
7
7
|
description = "Asynchronous client for SQLite using AnyIO"
|
|
8
8
|
readme = "README.md"
|
|
9
|
-
version = "0.2.
|
|
9
|
+
version = "0.2.10"
|
|
10
10
|
authors = [
|
|
11
11
|
{name = "Alex Grönholm", email = "alex.gronholm@nextday.fi"},
|
|
12
12
|
{name = "David Brochart", email = "david.brochart@gmail.com"},
|
|
@@ -28,7 +28,7 @@ classifiers = [
|
|
|
28
28
|
]
|
|
29
29
|
requires-python = ">= 3.10"
|
|
30
30
|
dependencies = [
|
|
31
|
-
"anyio >=4.
|
|
31
|
+
"anyio >=4.0,<5.0",
|
|
32
32
|
"typing-extensions >=4.15.0",
|
|
33
33
|
]
|
|
34
34
|
|
|
@@ -4,86 +4,18 @@ __all__ = ["connect", "Connection", "Cursor"]
|
|
|
4
4
|
|
|
5
5
|
import sqlite3
|
|
6
6
|
import sys
|
|
7
|
-
import threading
|
|
8
7
|
from collections.abc import Callable, Sequence
|
|
9
8
|
from functools import partial
|
|
10
9
|
from logging import Logger, getLogger
|
|
11
10
|
from types import TracebackType
|
|
12
|
-
from typing import Any
|
|
11
|
+
from typing import Any
|
|
13
12
|
|
|
14
|
-
import
|
|
15
|
-
from anyio import to_thread, from_thread
|
|
13
|
+
from anyio import CapacityLimiter, to_thread
|
|
16
14
|
|
|
17
15
|
if sys.version_info >= (3, 11):
|
|
18
|
-
from typing import Self
|
|
19
|
-
else:
|
|
20
|
-
from
|
|
21
|
-
from typing_extensions import Self, TypeVarTuple, Unpack
|
|
22
|
-
|
|
23
|
-
T_Retval = TypeVar("T_Retval")
|
|
24
|
-
PosArgsT = TypeVarTuple("PosArgsT")
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
async def _interruptible_dispatch(
|
|
28
|
-
self: Connection | Cursor,
|
|
29
|
-
func: Callable[[Unpack[PosArgsT]], T_Retval],
|
|
30
|
-
*args: Unpack[PosArgsT]
|
|
31
|
-
) -> T_Retval:
|
|
32
|
-
if isinstance(self, Connection):
|
|
33
|
-
real_connection = self._real_connection
|
|
34
|
-
elif isinstance(self, Cursor):
|
|
35
|
-
real_connection = self._real_cursor.connection
|
|
36
|
-
else: # pragma: nocover
|
|
37
|
-
raise AssertionError("Unknown type:", self)
|
|
38
|
-
|
|
39
|
-
ev = anyio.Event()
|
|
40
|
-
lock = threading.Lock()
|
|
41
|
-
need_interrupt = False
|
|
42
|
-
|
|
43
|
-
async def cancel_detector() -> None:
|
|
44
|
-
try:
|
|
45
|
-
await ev.wait()
|
|
46
|
-
except anyio.get_cancelled_exc_class():
|
|
47
|
-
# Block progress in the thread while checking this flag.
|
|
48
|
-
# Our guard_interrupt thread only ever holds the lock briefly,
|
|
49
|
-
# so there's no risk of blocking the event loop.
|
|
50
|
-
with lock:
|
|
51
|
-
# Due to race conditions, the first calls to interrupt may be
|
|
52
|
-
# ignored. This race is quick so this loop should not cycle much.
|
|
53
|
-
while need_interrupt:
|
|
54
|
-
real_connection.interrupt()
|
|
55
|
-
await anyio.lowlevel.cancel_shielded_checkpoint()
|
|
56
|
-
# we do NOT re-raise the cancellation so that the task group
|
|
57
|
-
# does not swallow our retval. If a Cancelled is to propagate,
|
|
58
|
-
# it should come out of to_thread.run_sync
|
|
59
|
-
|
|
60
|
-
def guard_interrupt() -> T_Retval:
|
|
61
|
-
nonlocal need_interrupt
|
|
62
|
-
|
|
63
|
-
with lock:
|
|
64
|
-
from_thread.check_cancelled()
|
|
65
|
-
need_interrupt = True
|
|
66
|
-
try:
|
|
67
|
-
return func(*args)
|
|
68
|
-
except sqlite3.OperationalError as e:
|
|
69
|
-
if str(e) == "interrupted":
|
|
70
|
-
from_thread.check_cancelled()
|
|
71
|
-
raise
|
|
72
|
-
finally:
|
|
73
|
-
need_interrupt = False
|
|
74
|
-
|
|
75
|
-
try:
|
|
76
|
-
async with anyio.create_task_group() as g:
|
|
77
|
-
g.start_soon(cancel_detector)
|
|
78
|
-
retval = await to_thread.run_sync(guard_interrupt, limiter=self._limiter)
|
|
79
|
-
ev.set()
|
|
80
|
-
except BaseExceptionGroup as eg:
|
|
81
|
-
if len(eg.exceptions) == 1:
|
|
82
|
-
if isinstance(eg.exceptions[0], Exception):
|
|
83
|
-
raise eg.exceptions[0]
|
|
84
|
-
raise # pragma: nocover (would be an internal error that should fail other tests)
|
|
85
|
-
|
|
86
|
-
return retval
|
|
16
|
+
from typing import Self
|
|
17
|
+
else:
|
|
18
|
+
from typing_extensions import Self # pragma: nocover
|
|
87
19
|
|
|
88
20
|
|
|
89
21
|
class Connection:
|
|
@@ -96,7 +28,7 @@ class Connection:
|
|
|
96
28
|
self._real_connection = _real_connection
|
|
97
29
|
self._exception_handler = _exception_handler
|
|
98
30
|
self._log = _log or getLogger(__name__)
|
|
99
|
-
self._limiter =
|
|
31
|
+
self._limiter = CapacityLimiter(1)
|
|
100
32
|
|
|
101
33
|
async def __aenter__(self) -> Self:
|
|
102
34
|
return self
|
|
@@ -121,19 +53,17 @@ class Connection:
|
|
|
121
53
|
return exception_handled
|
|
122
54
|
|
|
123
55
|
async def execute(self, sql: str, parameters: Sequence[Any] = (), /) -> Cursor:
|
|
124
|
-
real_cursor = await
|
|
56
|
+
real_cursor = await to_thread.run_sync(self._real_connection.execute, sql, parameters, limiter=self._limiter)
|
|
125
57
|
return Cursor(real_cursor, self._limiter, self._exception_handler, self._log)
|
|
126
58
|
|
|
127
59
|
async def close(self) -> None:
|
|
128
|
-
|
|
129
|
-
await to_thread.run_sync(self._real_connection.close, limiter=self._limiter)
|
|
60
|
+
await to_thread.run_sync(self._real_connection.close, limiter=self._limiter)
|
|
130
61
|
|
|
131
62
|
async def commit(self) -> None:
|
|
132
|
-
await
|
|
63
|
+
await to_thread.run_sync(self._real_connection.commit, limiter=self._limiter)
|
|
133
64
|
|
|
134
65
|
async def rollback(self) -> None:
|
|
135
|
-
|
|
136
|
-
await to_thread.run_sync(self._real_connection.rollback, limiter=self._limiter)
|
|
66
|
+
await to_thread.run_sync(self._real_connection.rollback, limiter=self._limiter)
|
|
137
67
|
|
|
138
68
|
async def cursor(self, factory: Callable[[sqlite3.Connection], sqlite3.Cursor] = sqlite3.Cursor) -> Cursor:
|
|
139
69
|
real_cursor = await to_thread.run_sync(self._real_connection.cursor, factory, limiter=self._limiter)
|
|
@@ -144,7 +74,7 @@ class Cursor:
|
|
|
144
74
|
def __init__(
|
|
145
75
|
self,
|
|
146
76
|
real_cursor: sqlite3.Cursor,
|
|
147
|
-
limiter:
|
|
77
|
+
limiter: CapacityLimiter,
|
|
148
78
|
_exception_handler: Callable[[type[BaseException], BaseException, TracebackType, Logger], bool] | None,
|
|
149
79
|
_log: Logger,
|
|
150
80
|
) -> None:
|
|
@@ -187,29 +117,28 @@ class Cursor:
|
|
|
187
117
|
return exception_handled
|
|
188
118
|
|
|
189
119
|
async def close(self) -> None:
|
|
190
|
-
|
|
191
|
-
await to_thread.run_sync(self._real_cursor.close, limiter=self._limiter)
|
|
120
|
+
await to_thread.run_sync(self._real_cursor.close, limiter=self._limiter)
|
|
192
121
|
|
|
193
122
|
async def execute(self, sql: str, parameters: Sequence[Any] = (), /) -> Cursor:
|
|
194
|
-
await
|
|
123
|
+
await to_thread.run_sync(self._real_cursor.execute, sql, parameters, limiter=self._limiter)
|
|
195
124
|
return self
|
|
196
125
|
|
|
197
126
|
async def executemany(self, sql: str, parameters: Sequence[Any], /) -> Cursor:
|
|
198
|
-
await
|
|
127
|
+
await to_thread.run_sync(self._real_cursor.executemany, sql, parameters, limiter=self._limiter)
|
|
199
128
|
return self
|
|
200
129
|
|
|
201
130
|
async def executescript(self, sql_script: str, /) -> Cursor:
|
|
202
|
-
await
|
|
131
|
+
await to_thread.run_sync(self._real_cursor.executescript, sql_script, limiter=self._limiter)
|
|
203
132
|
return self
|
|
204
133
|
|
|
205
134
|
async def fetchone(self) -> tuple[Any, ...] | None:
|
|
206
|
-
return await
|
|
135
|
+
return await to_thread.run_sync(self._real_cursor.fetchone, limiter=self._limiter)
|
|
207
136
|
|
|
208
137
|
async def fetchmany(self, size: int) -> list[tuple[Any, ...]]:
|
|
209
|
-
return await
|
|
138
|
+
return await to_thread.run_sync(self._real_cursor.fetchmany, size, limiter=self._limiter)
|
|
210
139
|
|
|
211
140
|
async def fetchall(self) -> list[tuple[Any, ...]]:
|
|
212
|
-
return await
|
|
141
|
+
return await to_thread.run_sync(self._real_cursor.fetchall, limiter=self._limiter)
|
|
213
142
|
|
|
214
143
|
|
|
215
144
|
async def connect(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|