0
# Filelock
1
2
A platform-independent file lock that supports the with-statement for coordinating access to shared resources across processes. Filelock provides synchronous and asynchronous file locking with platform-specific optimizations for Unix (fcntl), Windows (msvcrt), and a fallback soft lock implementation.
3
4
## Package Information
5
6
- **Package Name**: filelock
7
- **Language**: Python
8
- **Installation**: `pip install filelock`
9
10
## Core Imports
11
12
```python
13
from filelock import FileLock
14
```
15
16
For async usage:
17
18
```python
19
from filelock import AsyncFileLock
20
```
21
22
Import specific lock types:
23
24
```python
25
from filelock import SoftFileLock, UnixFileLock, WindowsFileLock
26
from filelock import AsyncSoftFileLock, AsyncUnixFileLock, AsyncWindowsFileLock
27
```
28
29
Import exceptions:
30
31
```python
32
from filelock import Timeout
33
```
34
35
Import version:
36
37
```python
38
from filelock import __version__
39
```
40
41
Import utility functions:
42
43
```python
44
from filelock._util import raise_on_not_writable_file, ensure_directory_exists
45
from filelock._unix import has_fcntl
46
```
47
48
## Basic Usage
49
50
```python
51
from filelock import FileLock
52
import time
53
54
# Create a file lock
55
lock = FileLock("my_file.lock")
56
57
# Use with context manager (recommended)
58
with lock:
59
# Critical section - only one process can execute this at a time
60
print("Lock acquired, doing work...")
61
time.sleep(2)
62
print("Work completed")
63
# Lock is automatically released
64
65
# Async usage
66
import asyncio
67
from filelock import AsyncFileLock
68
69
async def async_example():
70
lock = AsyncFileLock("async_file.lock")
71
72
async with lock:
73
print("Async lock acquired")
74
await asyncio.sleep(2)
75
print("Async work completed")
76
77
# Manual acquire/release (not recommended)
78
lock = FileLock("manual.lock")
79
try:
80
lock.acquire(timeout=10)
81
print("Lock acquired manually")
82
finally:
83
lock.release()
84
```
85
86
## Architecture
87
88
Filelock uses a hierarchical class design with platform-specific implementations:
89
90
- **BaseFileLock**: Abstract base class defining the common interface
91
- **Platform Implementations**: UnixFileLock (fcntl), WindowsFileLock (msvcrt), SoftFileLock (fallback)
92
- **FileLock Alias**: Automatically selects the best implementation for the current platform
93
- **Async Support**: BaseAsyncFileLock and async variants of all lock types
94
- **Context Management**: All locks support both synchronous `with` and asynchronous `async with` statements
95
96
The library automatically chooses the most appropriate locking mechanism based on the platform and available system features.
97
98
## Capabilities
99
100
### Platform-Specific File Locks
101
102
Cross-platform file locking with automatic platform detection. FileLock automatically selects the best available implementation for the current system.
103
104
```python { .api }
105
class FileLock(BaseFileLock):
106
"""Platform-specific file lock (alias for UnixFileLock/WindowsFileLock/SoftFileLock)."""
107
108
def __init__(
109
self,
110
lock_file: str | os.PathLike[str],
111
timeout: float = -1,
112
mode: int = 0o644,
113
thread_local: bool = True,
114
*,
115
blocking: bool = True,
116
is_singleton: bool = False,
117
) -> None:
118
"""
119
Create a new lock object.
120
121
Args:
122
lock_file: Path to the lock file
123
timeout: Default timeout in seconds (-1 for no timeout)
124
mode: File permissions for the lock file
125
thread_local: Whether context should be thread local
126
blocking: Whether the lock should be blocking
127
is_singleton: If True, only one instance per lock file path
128
"""
129
130
class AsyncFileLock(BaseAsyncFileLock):
131
"""Platform-specific async file lock."""
132
133
def __init__(
134
self,
135
lock_file: str | os.PathLike[str],
136
timeout: float = -1,
137
mode: int = 0o644,
138
thread_local: bool = False,
139
*,
140
blocking: bool = True,
141
is_singleton: bool = False,
142
loop: asyncio.AbstractEventLoop | None = None,
143
run_in_executor: bool = True,
144
executor: concurrent.futures.Executor | None = None,
145
) -> None:
146
"""
147
Create a new async lock object.
148
149
Args:
150
lock_file: Path to the lock file
151
timeout: Default timeout in seconds (-1 for no timeout)
152
mode: File permissions for the lock file
153
thread_local: Whether context should be thread local
154
blocking: Whether the lock should be blocking
155
is_singleton: If True, only one instance per lock file path
156
loop: Event loop to use
157
run_in_executor: Whether to run in executor
158
executor: Executor to use for blocking operations
159
"""
160
```
161
162
### Unix File Locks (fcntl-based)
163
164
Hard file locking using fcntl.flock for Unix-like systems (Linux, macOS, BSD).
165
166
```python { .api }
167
class UnixFileLock(BaseFileLock):
168
"""Uses fcntl.flock to hard lock the file on Unix systems."""
169
170
class AsyncUnixFileLock(UnixFileLock, BaseAsyncFileLock):
171
"""Async version of UnixFileLock."""
172
```
173
174
### Platform Availability Constants
175
176
Constants indicating platform-specific feature availability.
177
178
```python { .api }
179
has_fcntl: bool
180
# Boolean constant indicating if fcntl is available on the current system
181
# True on Unix-like systems with fcntl support, False on Windows or systems without fcntl
182
```
183
184
### Windows File Locks (msvcrt-based)
185
186
Hard file locking using msvcrt.locking for Windows systems.
187
188
```python { .api }
189
class WindowsFileLock(BaseFileLock):
190
"""Uses msvcrt.locking to hard lock the file on Windows systems."""
191
192
class AsyncWindowsFileLock(WindowsFileLock, BaseAsyncFileLock):
193
"""Async version of WindowsFileLock."""
194
```
195
196
### Soft File Locks (cross-platform fallback)
197
198
File existence-based locking that works on all platforms but provides softer guarantees.
199
200
```python { .api }
201
class SoftFileLock(BaseFileLock):
202
"""Simply watches the existence of the lock file."""
203
204
class AsyncSoftFileLock(SoftFileLock, BaseAsyncFileLock):
205
"""Async version of SoftFileLock."""
206
```
207
208
### Lock Acquisition and Release
209
210
Core methods for acquiring and releasing file locks with timeout support.
211
212
```python { .api }
213
class BaseFileLock:
214
def acquire(
215
self,
216
timeout: float | None = None,
217
poll_interval: float = 0.05,
218
*,
219
poll_intervall: float | None = None,
220
blocking: bool | None = None,
221
) -> AcquireReturnProxy:
222
"""
223
Try to acquire the file lock.
224
225
Args:
226
timeout: Maximum wait time in seconds (None uses default)
227
poll_interval: Interval between acquisition attempts
228
poll_intervall: Deprecated, use poll_interval instead
229
blocking: Whether to block until acquired
230
231
Returns:
232
Context manager proxy for the lock
233
234
Raises:
235
Timeout: If lock cannot be acquired within timeout
236
"""
237
238
def release(self, force: bool = False) -> None:
239
"""
240
Release the file lock.
241
242
Args:
243
force: If True, ignore lock counter and force release
244
"""
245
246
class BaseAsyncFileLock:
247
@property
248
def run_in_executor(self) -> bool:
249
"""Whether operations run in an executor."""
250
251
@property
252
def executor(self) -> concurrent.futures.Executor | None:
253
"""The executor used for blocking operations."""
254
255
@executor.setter
256
def executor(self, value: concurrent.futures.Executor | None) -> None:
257
"""Set the executor for blocking operations."""
258
259
@property
260
def loop(self) -> asyncio.AbstractEventLoop | None:
261
"""The event loop associated with this lock."""
262
263
async def acquire(
264
self,
265
timeout: float | None = None,
266
poll_interval: float = 0.05,
267
*,
268
blocking: bool | None = None,
269
) -> AsyncAcquireReturnProxy:
270
"""
271
Async version of acquire.
272
273
Args:
274
timeout: Maximum wait time in seconds (None uses default)
275
poll_interval: Interval between acquisition attempts
276
blocking: Whether to block until acquired
277
278
Returns:
279
Async context manager proxy for the lock
280
281
Raises:
282
Timeout: If lock cannot be acquired within timeout
283
"""
284
285
async def release(self, force: bool = False) -> None:
286
"""
287
Async version of release.
288
289
Args:
290
force: If True, ignore lock counter and force release
291
"""
292
```
293
294
### Lock State and Properties
295
296
Properties and methods for inspecting lock state and configuration.
297
298
```python { .api }
299
class BaseFileLock:
300
@property
301
def is_locked(self) -> bool:
302
"""Whether the lock is currently held."""
303
304
@property
305
def lock_counter(self) -> int:
306
"""Number of times lock has been acquired (for reentrant locking)."""
307
308
@property
309
def lock_file(self) -> str:
310
"""Path to the lock file."""
311
312
@property
313
def timeout(self) -> float:
314
"""Default timeout value in seconds."""
315
316
@timeout.setter
317
def timeout(self, value: float | str) -> None:
318
"""Set the default timeout value."""
319
320
@property
321
def blocking(self) -> bool:
322
"""Whether locking is blocking by default."""
323
324
@blocking.setter
325
def blocking(self, value: bool) -> None:
326
"""Set the default blocking behavior."""
327
328
@property
329
def mode(self) -> int:
330
"""File permissions for the lock file."""
331
332
@property
333
def is_singleton(self) -> bool:
334
"""Whether this lock uses singleton pattern."""
335
336
def is_thread_local(self) -> bool:
337
"""Whether this lock uses thread-local context."""
338
```
339
340
### Context Manager Support
341
342
Built-in support for context managers enabling safe automatic lock release.
343
344
```python { .api }
345
class BaseFileLock:
346
def __enter__(self) -> BaseFileLock:
347
"""Enter the context manager (acquire lock)."""
348
349
def __exit__(
350
self,
351
exc_type: type[BaseException] | None,
352
exc_value: BaseException | None,
353
traceback: types.TracebackType | None,
354
) -> None:
355
"""Exit the context manager (release lock)."""
356
357
class BaseAsyncFileLock:
358
async def __aenter__(self) -> BaseAsyncFileLock:
359
"""Enter the async context manager (acquire lock)."""
360
361
async def __aexit__(
362
self,
363
exc_type: type[BaseException] | None,
364
exc_value: BaseException | None,
365
traceback: types.TracebackType | None,
366
) -> None:
367
"""Exit the async context manager (release lock)."""
368
369
def __enter__(self) -> NoReturn:
370
"""Raises NotImplementedError - use async with instead."""
371
```
372
373
### Context Manager Proxies
374
375
Helper classes that provide context manager functionality while preventing double-acquisition.
376
377
```python { .api }
378
class AcquireReturnProxy:
379
"""Context manager returned by acquire() for safe lock handling."""
380
381
def __init__(self, lock: BaseFileLock) -> None: ...
382
383
def __enter__(self) -> BaseFileLock: ...
384
385
def __exit__(
386
self,
387
exc_type: type[BaseException] | None,
388
exc_value: BaseException | None,
389
traceback: types.TracebackType | None,
390
) -> None: ...
391
392
class AsyncAcquireReturnProxy:
393
"""Async context manager returned by async acquire()."""
394
395
def __init__(self, lock: BaseAsyncFileLock) -> None: ...
396
397
async def __aenter__(self) -> BaseAsyncFileLock: ...
398
399
async def __aexit__(
400
self,
401
exc_type: type[BaseException] | None,
402
exc_value: BaseException | None,
403
traceback: types.TracebackType | None,
404
) -> None: ...
405
```
406
407
### Exception Handling
408
409
Exception class for lock acquisition timeouts and error handling.
410
411
```python { .api }
412
class Timeout(TimeoutError):
413
"""Raised when the lock could not be acquired within the timeout period."""
414
415
def __init__(self, lock_file: str) -> None:
416
"""
417
Create a timeout exception.
418
419
Args:
420
lock_file: Path to the lock file that timed out
421
"""
422
423
def __str__(self) -> str:
424
"""Return string representation of the exception."""
425
426
def __repr__(self) -> str:
427
"""Return detailed string representation of the exception."""
428
429
def __reduce__(self) -> str | tuple[Any, ...]:
430
"""Support for pickling the exception."""
431
432
@property
433
def lock_file(self) -> str:
434
"""Path of the file lock that timed out."""
435
436
# Other exceptions that may be raised:
437
# ValueError: When invalid parameters are passed to singleton locks
438
# NotImplementedError: When platform-specific features are unavailable
439
# PermissionError: When lock file cannot be written (via utility functions)
440
# IsADirectoryError: When lock path points to a directory (via utility functions)
441
```
442
443
### Utility Functions
444
445
Internal utility functions that are part of the public API for file and directory handling.
446
447
```python { .api }
448
def raise_on_not_writable_file(filename: str) -> None:
449
"""
450
Raise an exception if attempting to open the file for writing would fail.
451
452
This is done so files that will never be writable can be separated from files
453
that are writable but currently locked.
454
455
Args:
456
filename: Path to the file to check
457
458
Raises:
459
PermissionError: If file exists but is not writable
460
IsADirectoryError: If path points to a directory (Unix/macOS)
461
PermissionError: If path points to a directory (Windows)
462
"""
463
464
def ensure_directory_exists(filename: str | os.PathLike[str]) -> None:
465
"""
466
Ensure the directory containing the file exists (create it if necessary).
467
468
Args:
469
filename: Path to the file whose parent directory should exist
470
"""
471
```
472
473
### Version Information
474
475
Package version information.
476
477
```python { .api }
478
__version__: str
479
# Version of the filelock package as a string
480
```
481
482
## Types
483
484
```python { .api }
485
import os
486
import asyncio
487
import concurrent.futures
488
from types import TracebackType
489
from typing import Union, Optional, Any, NoReturn
490
491
# Type aliases used in the API
492
PathLike = Union[str, os.PathLike[str]]
493
OptionalFloat = Optional[float]
494
OptionalBool = Optional[bool]
495
OptionalLoop = Optional[asyncio.AbstractEventLoop]
496
OptionalExecutor = Optional[concurrent.futures.Executor]
497
OptionalExceptionType = Optional[type[BaseException]]
498
OptionalException = Optional[BaseException]
499
OptionalTraceback = Optional[TracebackType]
500
```
501
502
## Usage Examples
503
504
### Timeout Handling
505
506
```python
507
from filelock import FileLock, Timeout
508
509
lock = FileLock("resource.lock", timeout=5)
510
511
try:
512
with lock:
513
print("Got the lock!")
514
time.sleep(10) # Simulate work
515
except Timeout:
516
print("Could not acquire lock within 5 seconds")
517
```
518
519
### Non-blocking Lock Attempts
520
521
```python
522
from filelock import FileLock
523
524
lock = FileLock("resource.lock", blocking=False)
525
526
try:
527
with lock.acquire():
528
print("Lock acquired immediately")
529
except Timeout:
530
print("Lock is currently held by another process")
531
```
532
533
### Singleton Locks
534
535
```python
536
from filelock import FileLock
537
538
# All instances with same path share the same lock object
539
lock1 = FileLock("shared.lock", is_singleton=True)
540
lock2 = FileLock("shared.lock", is_singleton=True)
541
542
assert lock1 is lock2 # Same object
543
```
544
545
### Async Lock Usage
546
547
```python
548
import asyncio
549
from filelock import AsyncFileLock
550
551
async def worker(worker_id: int):
552
lock = AsyncFileLock("shared_resource.lock")
553
554
async with lock:
555
print(f"Worker {worker_id} acquired lock")
556
await asyncio.sleep(1)
557
print(f"Worker {worker_id} releasing lock")
558
559
async def main():
560
# Run multiple workers concurrently
561
await asyncio.gather(
562
worker(1),
563
worker(2),
564
worker(3)
565
)
566
567
asyncio.run(main())
568
```