Skip to content

Commit 91013a2

Browse files
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
1 parent 8938c15 commit 91013a2

File tree

1 file changed

+91
-58
lines changed

1 file changed

+91
-58
lines changed

src/filelock/_read_write.py

Lines changed: 91 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,41 @@
1+
from __future__ import annotations
2+
3+
import logging
14
import os
25
import sqlite3
36
import threading
4-
import logging
57
import time
6-
from _error import Timeout
7-
from filelock._api import AcquireReturnProxy, BaseFileLock
8-
from typing import Literal, Any
98
from contextlib import contextmanager
9+
from typing import Any, Literal
1010
from weakref import WeakValueDictionary
1111

12+
from _error import Timeout
13+
14+
from filelock._api import AcquireReturnProxy
15+
1216
_LOGGER = logging.getLogger("filelock")
1317

1418
# PRAGMA busy_timeout=N delegates to https://www.sqlite.org/c3ref/busy_timeout.html,
1519
# which accepts an int argument, which has the maximum value of 2_147_483_647 on 32-bit
1620
# systems. Use even a lower value to be safe. This 2 bln milliseconds is about 23 days.
1721
_MAX_SQLITE_TIMEOUT_MS = 2_000_000_000 - 1
1822

23+
1924
def timeout_for_sqlite(timeout: float, blocking: bool, already_waited: float) -> int:
2025
if blocking is False:
2126
return 0
22-
27+
2328
if timeout == -1:
2429
return _MAX_SQLITE_TIMEOUT_MS
25-
30+
2631
if timeout < 0:
27-
raise ValueError("timeout must be a non-negative number or -1")
28-
32+
msg = "timeout must be a non-negative number or -1"
33+
raise ValueError(msg)
34+
2935
if timeout > 0:
30-
timeout = timeout - already_waited
31-
if timeout < 0:
32-
timeout = 0
33-
36+
timeout -= already_waited
37+
timeout = max(timeout, 0)
38+
3439
assert timeout >= 0
3540

3641
timeout_ms = int(timeout * 1000)
@@ -42,9 +47,16 @@ def timeout_for_sqlite(timeout: float, blocking: bool, already_waited: float) ->
4247

4348
class _ReadWriteLockMeta(type):
4449
"""Metaclass that redirects instance creation to get_lock() when is_singleton=True."""
45-
def __call__(cls, lock_file: str | os.PathLike[str],
46-
timeout: float = -1, blocking: bool = True,
47-
is_singleton: bool = True, *args: Any, **kwargs: Any) -> "ReadWriteLock":
50+
51+
def __call__(
52+
cls,
53+
lock_file: str | os.PathLike[str],
54+
timeout: float = -1,
55+
blocking: bool = True,
56+
is_singleton: bool = True,
57+
*args: Any,
58+
**kwargs: Any,
59+
) -> ReadWriteLock:
4860
if is_singleton:
4961
return cls.get_lock(lock_file, timeout, blocking)
5062
return super().__call__(lock_file, timeout, blocking, is_singleton, *args, **kwargs)
@@ -56,16 +68,22 @@ class ReadWriteLock(metaclass=_ReadWriteLockMeta):
5668
_instances_lock = threading.Lock()
5769

5870
@classmethod
59-
def get_lock(cls, lock_file: str | os.PathLike[str],
60-
timeout: float = -1, blocking: bool = True) -> "ReadWriteLock":
71+
def get_lock(cls, lock_file: str | os.PathLike[str], timeout: float = -1, blocking: bool = True) -> ReadWriteLock:
6172
"""Return the one-and-only ReadWriteLock for a given file."""
6273
normalized = os.path.abspath(lock_file)
6374
with cls._instances_lock:
6475
if normalized not in cls._instances:
6576
cls._instances[normalized] = cls(lock_file, timeout, blocking)
6677
instance = cls._instances[normalized]
6778
if instance.timeout != timeout or instance.blocking != blocking:
68-
raise ValueError("Singleton lock created with timeout=%s, blocking=%s, cannot be changed to timeout=%s, blocking=%s", instance.timeout, instance.blocking, timeout, blocking)
79+
msg = "Singleton lock created with timeout=%s, blocking=%s, cannot be changed to timeout=%s, blocking=%s"
80+
raise ValueError(
81+
msg,
82+
instance.timeout,
83+
instance.blocking,
84+
timeout,
85+
blocking,
86+
)
6987
return instance
7088

7189
def __init__(
@@ -85,7 +103,7 @@ def __init__(
85103
self._internal_lock = threading.Lock()
86104
self._lock_level = 0 # Reentrance counter.
87105
# _current_mode holds the active lock mode ("read" or "write") or None if no lock is held.
88-
self._current_mode: Literal["read", "write", None] = None
106+
self._current_mode: Literal["read", "write"] | None = None
89107
# _lock_level is the reentrance counter.
90108
self._lock_level = 0
91109
self.con = sqlite3.connect(self.lock_file, check_same_thread=False)
@@ -101,21 +119,25 @@ def __init__(
101119
# acquire, so crashes cannot adversely affect the DB. Even journal_mode=OFF would probably
102120
# be fine, too, but the SQLite documentation says that ROLLBACK becomes *undefined behaviour*
103121
# with journal_mode=OFF which sounds scarier.
104-
self.con.execute('PRAGMA journal_mode=MEMORY;')
122+
self.con.execute("PRAGMA journal_mode=MEMORY;")
105123

106124
def acquire_read(self, timeout: float = -1, blocking: bool = True) -> AcquireReturnProxy:
107-
"""Acquire a read lock. If a lock is already held, it must be a read lock.
108-
Upgrading from read to write is prohibited."""
109-
125+
"""
126+
Acquire a read lock. If a lock is already held, it must be a read lock.
127+
Upgrading from read to write is prohibited.
128+
"""
110129
# Attempt to re-enter already held lock.
111130
with self._internal_lock:
112131
if self._lock_level > 0:
113132
# Must already be in read mode.
114133
if self._current_mode != "read":
115-
raise RuntimeError(
116-
f"Cannot acquire read lock on {self.lock_file} (lock id: {id(self)}): "
117-
"already holding a write lock (downgrade not allowed)"
118-
)
134+
msg = (
135+
f"Cannot acquire read lock on {self.lock_file} (lock id: {id(self)}): "
136+
"already holding a write lock (downgrade not allowed)"
137+
)
138+
raise RuntimeError(
139+
msg
140+
)
119141
self._lock_level += 1
120142
return AcquireReturnProxy(lock=self)
121143

@@ -131,47 +153,54 @@ def acquire_read(self, timeout: float = -1, blocking: bool = True) -> AcquireRet
131153
with self._internal_lock:
132154
if self._lock_level > 0:
133155
if self._current_mode != "read":
134-
raise RuntimeError(
156+
msg = (
135157
f"Cannot acquire read lock on {self.lock_file} (lock id: {id(self)}): "
136158
"already holding a write lock (downgrade not allowed)"
137159
)
160+
raise RuntimeError(
161+
msg
162+
)
138163
self._lock_level += 1
139164
return AcquireReturnProxy(lock=self)
140-
165+
141166
waited = time.perf_counter() - start_time
142167
timeout_ms = timeout_for_sqlite(timeout, blocking, waited)
143-
144-
self.con.execute('PRAGMA busy_timeout=?;', (timeout_ms,))
145-
self.con.execute('BEGIN TRANSACTION;')
168+
169+
self.con.execute("PRAGMA busy_timeout=?;", (timeout_ms,))
170+
self.con.execute("BEGIN TRANSACTION;")
146171
# Need to make SELECT to compel SQLite to actually acquire a SHARED db lock.
147172
# See https://www.sqlite.org/lockingv3.html#transaction_control
148-
self.con.execute('SELECT name from sqlite_schema LIMIT 1;')
173+
self.con.execute("SELECT name from sqlite_schema LIMIT 1;")
149174

150175
with self._internal_lock:
151176
self._current_mode = "read"
152177
self._lock_level = 1
153-
178+
154179
return AcquireReturnProxy(lock=self)
155180

156181
except sqlite3.OperationalError as e:
157-
if 'database is locked' not in str(e):
182+
if "database is locked" not in str(e):
158183
raise # Re-raise unexpected errors.
159184
raise Timeout(self.lock_file)
160185
finally:
161186
self._transaction_lock.release()
162187

163188
def acquire_write(self, timeout: float = -1, blocking: bool = True) -> AcquireReturnProxy:
164-
"""Acquire a write lock. If a lock is already held, it must be a write lock.
165-
Upgrading from read to write is prohibited."""
166-
189+
"""
190+
Acquire a write lock. If a lock is already held, it must be a write lock.
191+
Upgrading from read to write is prohibited.
192+
"""
167193
# Attempt to re-enter already held lock.
168194
with self._internal_lock:
169195
if self._lock_level > 0:
170196
if self._current_mode != "write":
171-
raise RuntimeError(
197+
msg = (
172198
f"Cannot acquire write lock on {self.lock_file} (lock id: {id(self)}): "
173199
"already holding a read lock (upgrade not allowed)"
174200
)
201+
raise RuntimeError(
202+
msg
203+
)
175204
self._lock_level += 1
176205
return AcquireReturnProxy(lock=self)
177206

@@ -185,27 +214,30 @@ def acquire_write(self, timeout: float = -1, blocking: bool = True) -> AcquireRe
185214
with self._internal_lock:
186215
if self._lock_level > 0:
187216
if self._current_mode != "write":
188-
raise RuntimeError(
217+
msg = (
189218
f"Cannot acquire write lock on {self.lock_file} (lock id: {id(self)}): "
190219
"already holding a read lock (upgrade not allowed)"
191220
)
221+
raise RuntimeError(
222+
msg
223+
)
192224
self._lock_level += 1
193225
return AcquireReturnProxy(lock=self)
194-
226+
195227
waited = time.perf_counter() - start_time
196228
timeout_ms = timeout_for_sqlite(timeout, blocking, waited)
197-
198-
self.con.execute('PRAGMA busy_timeout=?;', (timeout_ms,))
199-
self.con.execute('BEGIN EXCLUSIVE TRANSACTION;')
229+
230+
self.con.execute("PRAGMA busy_timeout=?;", (timeout_ms,))
231+
self.con.execute("BEGIN EXCLUSIVE TRANSACTION;")
200232

201233
with self._internal_lock:
202234
self._current_mode = "write"
203235
self._lock_level = 1
204-
236+
205237
return AcquireReturnProxy(lock=self)
206238

207239
except sqlite3.OperationalError as e:
208-
if 'database is locked' not in str(e):
240+
if "database is locked" not in str(e):
209241
raise # Re-raise if it is an unexpected error.
210242
raise Timeout(self.lock_file)
211243
finally:
@@ -216,7 +248,8 @@ def release(self, force: bool = False) -> None:
216248
if self._lock_level == 0:
217249
if force:
218250
return
219-
raise RuntimeError(f"Cannot release a lock on {self.lock_file} (lock id: {id(self)}) that is not held")
251+
msg = f"Cannot release a lock on {self.lock_file} (lock id: {id(self)}) that is not held"
252+
raise RuntimeError(msg)
220253
if force:
221254
self._lock_level = 0
222255
else:
@@ -233,10 +266,11 @@ def release(self, force: bool = False) -> None:
233266
# (We provide two context managers as helpers.)
234267

235268
@contextmanager
236-
def read_lock(self, timeout: float | None = None,
237-
blocking: bool | None = None):
238-
"""Context manager for acquiring a read lock.
239-
Attempts to upgrade to write lock are disallowed."""
269+
def read_lock(self, timeout: float | None = None, blocking: bool | None = None):
270+
"""
271+
Context manager for acquiring a read lock.
272+
Attempts to upgrade to write lock are disallowed.
273+
"""
240274
if timeout is None:
241275
timeout = self.timeout
242276
if blocking is None:
@@ -248,10 +282,11 @@ def read_lock(self, timeout: float | None = None,
248282
self.release()
249283

250284
@contextmanager
251-
def write_lock(self, timeout: float | None = None,
252-
blocking: bool | None = None):
253-
"""Context manager for acquiring a write lock.
254-
Acquiring read locks on the same file while helding a write lock is prohibited."""
285+
def write_lock(self, timeout: float | None = None, blocking: bool | None = None):
286+
"""
287+
Context manager for acquiring a write lock.
288+
Acquiring read locks on the same file while helding a write lock is prohibited.
289+
"""
255290
if timeout is None:
256291
timeout = self.timeout
257292
if blocking is None:
@@ -261,9 +296,7 @@ def write_lock(self, timeout: float | None = None,
261296
yield
262297
finally:
263298
self.release()
264-
299+
265300
def __del__(self) -> None:
266301
"""Called when the lock object is deleted."""
267302
self.release(force=True)
268-
269-

0 commit comments

Comments
 (0)