S
Steven D'Aprano
I'm trying to safely rename a file without over-writing any existing
files, and I've run into a problem with file locks. Here's a naive way of
renaming without over-writing:
import os
oldname = "ham.txt"
newname = "spam.txt"
if not os.path.exists(newname):
os.rename(oldname, newname)
It's naive because there's a race-condition: if file newname is created
by another process after the call to exists(), but before the call to
rename(), then it will be over-written.
Here's my current solution, based on advice given by people on this
thread:
http://mail.python.org/pipermail/python-list/2006-October/411432.html
and this recipe:
http://code.activestate.com/recipes/65203/
but it isn't working for me.
import os, fcntl
oldname = "ham.txt"
newname = "spam.txt"
def lock_destination(name):
fileno = os.open(name, os.O_CREAT | os.O_EXCL)
fcntl.flock(fileno, fcntl.LOCK_EX) # POSIX systems only
return fileno
# Create a test file to be renamed.
f = open(oldname, 'w')
f.write('this is my file\n')
f.close()
fileno = lock_destination(newname)
# At this point, I can see "ham.txt" plus an empty file
# "spam.txt" in my file browser
os.rename(oldname, newname)
The rename works, but here is my problem: after getting what I thought
was an exclusive lock on the new file, but before calling os.rename(), I
can still over-write it from another process:
$ echo "this comes from another process" > spam.txt
$ cat spam.txt
this comes from another process
This is despite running lock_destination("spam.txt"). What am I doing
wrong?
Before anyone asks, yes I have checked that the Python process and the
shell process are in the same working directory, and therefore are
writing to the same file:
'this comes from another process\n'
I'm using Linux (Fedora).
files, and I've run into a problem with file locks. Here's a naive way of
renaming without over-writing:
import os
oldname = "ham.txt"
newname = "spam.txt"
if not os.path.exists(newname):
os.rename(oldname, newname)
It's naive because there's a race-condition: if file newname is created
by another process after the call to exists(), but before the call to
rename(), then it will be over-written.
Here's my current solution, based on advice given by people on this
thread:
http://mail.python.org/pipermail/python-list/2006-October/411432.html
and this recipe:
http://code.activestate.com/recipes/65203/
but it isn't working for me.
import os, fcntl
oldname = "ham.txt"
newname = "spam.txt"
def lock_destination(name):
fileno = os.open(name, os.O_CREAT | os.O_EXCL)
fcntl.flock(fileno, fcntl.LOCK_EX) # POSIX systems only
return fileno
# Create a test file to be renamed.
f = open(oldname, 'w')
f.write('this is my file\n')
f.close()
fileno = lock_destination(newname)
# At this point, I can see "ham.txt" plus an empty file
# "spam.txt" in my file browser
os.rename(oldname, newname)
The rename works, but here is my problem: after getting what I thought
was an exclusive lock on the new file, but before calling os.rename(), I
can still over-write it from another process:
$ echo "this comes from another process" > spam.txt
$ cat spam.txt
this comes from another process
This is despite running lock_destination("spam.txt"). What am I doing
wrong?
Before anyone asks, yes I have checked that the Python process and the
shell process are in the same working directory, and therefore are
writing to the same file:
'this comes from another process\n'
I'm using Linux (Fedora).