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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sqlite-anyio
3
- Version: 0.2.9
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.14,<5.0
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"
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.14,<5.0",
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, TypeVar
11
+ from typing import Any
13
12
 
14
- import anyio
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, TypeVarTuple, Unpack
19
- else: # pragma: nocover
20
- from exceptiongroup import BaseExceptionGroup
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 = anyio.CapacityLimiter(1)
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 _interruptible_dispatch(self, self._real_connection.execute, sql, parameters)
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
- with anyio.CancelScope(shield=True):
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 _interruptible_dispatch(self, self._real_connection.commit)
63
+ await to_thread.run_sync(self._real_connection.commit, limiter=self._limiter)
133
64
 
134
65
  async def rollback(self) -> None:
135
- with anyio.CancelScope(shield=True):
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: anyio.CapacityLimiter,
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
- with anyio.CancelScope(shield=True):
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 _interruptible_dispatch(self, self._real_cursor.execute, sql, parameters)
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 _interruptible_dispatch(self, self._real_cursor.executemany, sql, parameters)
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 _interruptible_dispatch(self, self._real_cursor.executescript, sql_script)
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 _interruptible_dispatch(self, self._real_cursor.fetchone)
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 _interruptible_dispatch(self, self._real_cursor.fetchmany, size)
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 _interruptible_dispatch(self, self._real_cursor.fetchall)
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