1+ from __future__ import annotations
2+
3+ import logging
14import os
25import sqlite3
36import threading
4- import logging
5- from _error import Timeout
6- from filelock ._api import AcquireReturnProxy , BaseFileLock
7- from typing import Literal , Any
87from contextlib import contextmanager
8+ from typing import Any , Literal
99from weakref import WeakValueDictionary
1010
11+ from _error import Timeout
12+
13+ from filelock ._api import AcquireReturnProxy
14+
1115_LOGGER = logging .getLogger ("filelock" )
1216
1317# PRAGMA busy_timeout=N delegates to https://www.sqlite.org/c3ref/busy_timeout.html,
1418# which accepts an int argument, which has the maximum value of 2_147_483_647 on 32-bit
1519# systems. Use even a lower value to be safe. This 2 bln milliseconds is about 23 days.
1620_MAX_SQLITE_TIMEOUT_MS = 2_000_000_000 - 1
1721
22+
1823def timeout_for_sqlite (timeout : float = - 1 , blocking : bool = True ) -> int :
1924 if blocking is False :
2025 return 0
2126 if timeout == - 1 :
2227 return _MAX_SQLITE_TIMEOUT_MS
2328 if timeout < 0 :
24- raise ValueError ("timeout must be a non-negative number or -1" )
25-
29+ msg = "timeout must be a non-negative number or -1"
30+ raise ValueError (msg )
31+
2632 assert timeout >= 0
2733 timeout_ms = int (timeout * 1000 )
2834 if timeout_ms > _MAX_SQLITE_TIMEOUT_MS or timeout_ms < 0 :
@@ -33,9 +39,16 @@ def timeout_for_sqlite(timeout: float = -1, blocking: bool = True) -> int:
3339
3440class _ReadWriteLockMeta (type ):
3541 """Metaclass that redirects instance creation to get_lock() when is_singleton=True."""
36- def __call__ (cls , lock_file : str | os .PathLike [str ],
37- timeout : float = - 1 , blocking : bool = True ,
38- is_singleton : bool = True , * args : Any , ** kwargs : Any ) -> "ReadWriteLock" :
42+
43+ def __call__ (
44+ cls ,
45+ lock_file : str | os .PathLike [str ],
46+ timeout : float = - 1 ,
47+ blocking : bool = True ,
48+ is_singleton : bool = True ,
49+ * args : Any ,
50+ ** kwargs : Any ,
51+ ) -> ReadWriteLock :
3952 if is_singleton :
4053 return cls .get_lock (lock_file , timeout , blocking )
4154 return super ().__call__ (lock_file , timeout , blocking , is_singleton , * args , ** kwargs )
@@ -47,16 +60,22 @@ class ReadWriteLock(metaclass=_ReadWriteLockMeta):
4760 _instances_lock = threading .Lock ()
4861
4962 @classmethod
50- def get_lock (cls , lock_file : str | os .PathLike [str ],
51- timeout : float = - 1 , blocking : bool = True ) -> "ReadWriteLock" :
63+ def get_lock (cls , lock_file : str | os .PathLike [str ], timeout : float = - 1 , blocking : bool = True ) -> ReadWriteLock :
5264 """Return the one-and-only ReadWriteLock for a given file."""
5365 normalized = os .path .abspath (lock_file )
5466 with cls ._instances_lock :
5567 if normalized not in cls ._instances :
5668 cls ._instances [normalized ] = cls (lock_file , timeout , blocking )
5769 instance = cls ._instances [normalized ]
5870 if instance .timeout != timeout or instance .blocking != blocking :
59- raise ValueError ("Singleton lock created with timeout=%s, blocking=%s, cannot be changed to timeout=%s, blocking=%s" , instance .timeout , instance .blocking , timeout , blocking )
71+ msg = "Singleton lock created with timeout=%s, blocking=%s, cannot be changed to timeout=%s, blocking=%s"
72+ raise ValueError (
73+ msg ,
74+ instance .timeout ,
75+ instance .blocking ,
76+ timeout ,
77+ blocking ,
78+ )
6079 return instance
6180
6281 def __init__ (
@@ -76,7 +95,7 @@ def __init__(
7695 self ._internal_lock = threading .Lock ()
7796 self ._lock_level = 0 # Reentrance counter.
7897 # _current_mode holds the active lock mode ("read" or "write") or None if no lock is held.
79- self ._current_mode : Literal ["read" , "write" , None ] = None
98+ self ._current_mode : Literal ["read" , "write" ] | None = None
8099 # _lock_level is the reentrance counter.
81100 self ._lock_level = 0
82101 self .con = sqlite3 .connect (self .lock_file , check_same_thread = False )
@@ -92,16 +111,19 @@ def __init__(
92111 # acquire, so crashes cannot adversely affect the DB. Even journal_mode=OFF would probably
93112 # be fine, too, but the SQLite documentation says that ROLLBACK becomes *undefined behaviour*
94113 # with journal_mode=OFF which sounds scarier.
95- self .con .execute (' PRAGMA journal_mode=MEMORY;' )
114+ self .con .execute (" PRAGMA journal_mode=MEMORY;" )
96115
97116 def acquire_read (self , timeout : float = - 1 , blocking : bool = True ) -> AcquireReturnProxy :
98- """Acquire a read lock. If a lock is already held, it must be a read lock.
99- Upgrading from read to write is prohibited."""
117+ """
118+ Acquire a read lock. If a lock is already held, it must be a read lock.
119+ Upgrading from read to write is prohibited.
120+ """
100121 with self ._internal_lock :
101122 if self ._lock_level > 0 :
102123 # Must already be in read mode.
103124 if self ._current_mode != "read" :
104- raise RuntimeError ("Cannot acquire read lock when a write lock is held (no upgrade allowed)" )
125+ msg = "Cannot acquire read lock when a write lock is held (no upgrade allowed)"
126+ raise RuntimeError (msg )
105127 self ._lock_level += 1
106128 return AcquireReturnProxy (lock = self )
107129
@@ -117,36 +139,40 @@ def acquire_read(self, timeout: float = -1, blocking: bool = True) -> AcquireRet
117139 if self ._lock_level > 0 :
118140 # Must already be in read mode.
119141 if self ._current_mode != "read" :
120- raise RuntimeError ("Cannot acquire read lock when a write lock is held (no upgrade allowed)" )
142+ msg = "Cannot acquire read lock when a write lock is held (no upgrade allowed)"
143+ raise RuntimeError (msg )
121144 self ._lock_level += 1
122145 return AcquireReturnProxy (lock = self )
123-
124- self .con .execute (' PRAGMA busy_timeout=?;' , (timeout_ms ,))
125- self .con .execute (' BEGIN TRANSACTION;' )
146+
147+ self .con .execute (" PRAGMA busy_timeout=?;" , (timeout_ms ,))
148+ self .con .execute (" BEGIN TRANSACTION;" )
126149 # Need to make SELECT to compel SQLite to actually acquire a SHARED db lock.
127150 # See https://www.sqlite.org/lockingv3.html#transaction_control
128- self .con .execute (' SELECT name from sqlite_schema LIMIT 1;' )
151+ self .con .execute (" SELECT name from sqlite_schema LIMIT 1;" )
129152
130153 with self ._internal_lock :
131154 self ._current_mode = "read"
132155 self ._lock_level = 1
133-
156+
134157 return AcquireReturnProxy (lock = self )
135158
136159 except sqlite3 .OperationalError as e :
137- if ' database is locked' not in str (e ):
160+ if " database is locked" not in str (e ):
138161 raise # Re-raise unexpected errors.
139162 raise Timeout (self .lock_file )
140163 finally :
141164 self ._transaction_lock .release ()
142165
143166 def acquire_write (self , timeout : float = - 1 , blocking : bool = True ) -> AcquireReturnProxy :
144- """Acquire a write lock. If a lock is already held, it must be a write lock.
145- Upgrading from read to write is prohibited."""
167+ """
168+ Acquire a write lock. If a lock is already held, it must be a write lock.
169+ Upgrading from read to write is prohibited.
170+ """
146171 with self ._internal_lock :
147172 if self ._lock_level > 0 :
148173 if self ._current_mode != "write" :
149- raise RuntimeError ("Cannot acquire write lock: already holding a read lock (no upgrade allowed)" )
174+ msg = "Cannot acquire write lock: already holding a read lock (no upgrade allowed)"
175+ raise RuntimeError (msg )
150176 self ._lock_level += 1
151177 return AcquireReturnProxy (lock = self )
152178
@@ -158,21 +184,24 @@ def acquire_write(self, timeout: float = -1, blocking: bool = True) -> AcquireRe
158184 with self ._internal_lock :
159185 if self ._lock_level > 0 :
160186 if self ._current_mode != "write" :
161- raise RuntimeError ("Cannot acquire write lock: already holding a read lock (no upgrade allowed)" )
187+ msg = "Cannot acquire write lock: already holding a read lock (no upgrade allowed)"
188+ raise RuntimeError (
189+ msg
190+ )
162191 self ._lock_level += 1
163192 return AcquireReturnProxy (lock = self )
164-
165- self .con .execute (' PRAGMA busy_timeout=?;' , (timeout_ms ,))
166- self .con .execute (' BEGIN EXCLUSIVE TRANSACTION;' )
193+
194+ self .con .execute (" PRAGMA busy_timeout=?;" , (timeout_ms ,))
195+ self .con .execute (" BEGIN EXCLUSIVE TRANSACTION;" )
167196
168197 with self ._internal_lock :
169198 self ._current_mode = "write"
170199 self ._lock_level = 1
171-
200+
172201 return AcquireReturnProxy (lock = self )
173202
174203 except sqlite3 .OperationalError as e :
175- if ' database is locked' not in str (e ):
204+ if " database is locked" not in str (e ):
176205 raise # Re-raise if it is an unexpected error.
177206 raise Timeout (self .lock_file )
178207 finally :
@@ -183,7 +212,8 @@ def release(self, force: bool = False) -> None:
183212 if self ._lock_level == 0 :
184213 if force :
185214 return
186- raise RuntimeError ("Cannot release a lock that is not held" )
215+ msg = "Cannot release a lock that is not held"
216+ raise RuntimeError (msg )
187217 if force :
188218 self ._lock_level = 0
189219 else :
@@ -200,10 +230,11 @@ def release(self, force: bool = False) -> None:
200230 # (We provide two context managers as helpers.)
201231
202232 @contextmanager
203- def read_lock (self , timeout : float | None = None ,
204- blocking : bool | None = None ):
205- """Context manager for acquiring a read lock.
206- Attempts to upgrade to write lock are disallowed."""
233+ def read_lock (self , timeout : float | None = None , blocking : bool | None = None ):
234+ """
235+ Context manager for acquiring a read lock.
236+ Attempts to upgrade to write lock are disallowed.
237+ """
207238 if timeout is None :
208239 timeout = self .timeout
209240 if blocking is None :
@@ -215,10 +246,11 @@ def read_lock(self, timeout: float | None = None,
215246 self .release ()
216247
217248 @contextmanager
218- def write_lock (self , timeout : float | None = None ,
219- blocking : bool | None = None ):
220- """Context manager for acquiring a write lock.
221- Acquiring read locks on the same file while helding a write lock is prohibited."""
249+ def write_lock (self , timeout : float | None = None , blocking : bool | None = None ):
250+ """
251+ Context manager for acquiring a write lock.
252+ Acquiring read locks on the same file while helding a write lock is prohibited.
253+ """
222254 if timeout is None :
223255 timeout = self .timeout
224256 if blocking is None :
@@ -228,9 +260,7 @@ def write_lock(self, timeout: float | None = None,
228260 yield
229261 finally :
230262 self .release ()
231-
263+
232264 def __del__ (self ) -> None :
233265 """Called when the lock object is deleted."""
234266 self .release (force = True )
235-
236-
0 commit comments