How Python Handles File Locking
Learn the practical details of file locking in Python across Linux, macOS, and Windows — from Unix-only fcntl to cross-platform portalocker — and avoid common production pitfalls.
Ever tried to open a file in Python only to find another process had it locked, leaving your script hanging? You're not alone. File locking is one of those topics that seems simple until you actually need it — and then it gets messy fast.
In some systems, file locking works across processes. In others, it doesn't. Some OSes enforce mandatory locks; others only offer advisory locks. Python sits between all these worlds, trying to give you something that works without making you cry.
Let me show you what actually works.
The Cross-Platform Trap
Here's the thing most tutorials won't tell you: Python has no built-in, cross-platform file locking module. The os module gives you os.lockf() and os.flock(), but those are Unix-only. On Windows, you need entirely different mechanisms.
So if you're writing code for PythonSkillset.com's deployment system, and it needs to run on both Linux servers and Windows workstations, you've got work to do.
What Actually Works on Linux and macOS
The most practical approach for Unix systems is using the fcntl module, which gives you cooperative (advisory) locking. Here's the pattern:
import fcntl
import time
def acquire_lock(file_obj, exclusive=True):
"""Attempt to lock file; returns True if successful."""
try:
if exclusive:
fcntl.flock(file_obj, fcntl.LOCK_EX | fcntl.LOCK_NB)
else:
fcntl.flock(file_obj, fcntl.LOCK_SH | fcntl.LOCK_NB)
return True
except BlockingIOError:
return False
with open('config.json', 'r+') as f:
if acquire_lock(f):
# Safe to modify
data = f.read()
f.seek(0)
f.write(updated_data)
f.truncate()
else:
print("File is locked by another process")
The LOCK_NB flag makes it non-blocking. Without it, your script will wait forever, which might be what you want — or a recipe for deadlocked servers.
Windows Files: A Different Animal
Windows uses mandatory locking by default. That means if one process has a file open with exclusive access, other processes simply can't read or write it at all. This sounds strict, and it is.
For Windows, you typically use the msvcrt module:
import msvcrt
def lock_windows(file_obj, exclusive=True):
try:
msvcrt.locking(file_obj.fileno(),
msvcrt.LK_NBLCK if exclusive else msvcrt.LK_NBRLCK,
1)
return True
except:
return False
Notice I used msvcrt.LK_NBLCK — that's the non-blocking exclusive lock. The plain LK_LOCK will block until available.
The Portable One: portalocker
If you've spent any time on PythonSkillset.com's forums, you've probably seen portalocker mentioned. It's the closest thing to a "just works" solution across platforms.
import portalocker
with open('data.json', 'r+') as f:
portalocker.lock(f, portalocker.LOCK_EX)
# Do your thing
portalocker.unlock(f)
It wraps the platform-specific calls so you don't have to. For most real-world projects, I'd start here unless you have specific performance needs.
When Locks Go Wrong
Here's a mistake I've seen even experienced developers make: forgetting that locks are only advisory on Linux. A process can write to a file without holding a lock. The lock only works if every process that accesses the file tries to acquire the lock first.
Another ugly bug: file locks are often released when a process dies — but not always. On some NFS filesystems, locks persist. Your monitoring scripts suddenly can't access a file that's been "locked" by a process that crashed two days ago.
The Context Manager Solution
For cleaner code, wrap your locking in a context manager:
from contextlib import contextmanager
import fcntl
@contextmanager
def file_lock(path, mode='r+'):
with open(path, mode) as f:
try:
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
yield f
except BlockingIOError:
raise RuntimeError(f"Cannot lock {path}")
finally:
fcntl.flock(f, fcntl.LOCK_UN)
# Usage
with file_lock('queue.db') as f:
# Only one process at a time
This ensures locks are always released, even if exceptions happen.
Final Thoughts
File locking in Python works — but you need to know your operating system first. Start with portalocker if you need cross-platform support. Use fcntl directly for fine-grained control on Unix. And test, test, test, because locks that work in development might fail in production when multiple processes hammer the same file.
At PythonSkillset, we've seen this exact issue bring down automated deployment pipelines. A lock that didn't release, a process that wouldn't die, a file that stayed inaccessible for hours. Understanding how your OS handles file locking — and how Python translates that — will save you a very bad day.
Comments
Questions, corrections, and tips stay visible for everyone reading this page.
Join the discussion
No comments yet
Be the first to leave a note — it helps the next reader.