[Python-ideas] Implementation of shutil.move
Calvin Spealman
ironfroggy at gmail.com
Fri Aug 12 15:45:41 CEST 2011
On Fri, Aug 12, 2011 at 9:20 AM, Mike Graham <mikegraham at gmail.com> wrote:
> On Fri, Aug 12, 2011 at 7:59 AM, David Townshend <aquavitae69 at gmail.com> wrote:
>> The shutil.move function uses os.rename to move files on the same file
>> system. On unix, this function will overwrite an existing destination, so
>> the obvious approach is
>> if not os.path.exists(dst):
>> shutil.move(src, dst)
>> But this could result in race conditions if dst is created after
>> os.path.exists and before shutil.move. From my research, it seems that this
>> is a limitation in the unix c library, but it should be possible to avoid it
>> through a workaround (pieced together
>> from http://bytes.com/topic/python/answers/555794-safely-renaming-file-without-overwriting).
>> This involves some fairly low-level work, so I propose adding a new move2
>> function to shutil, which raises an error if dst exists and locking it if it
>> doesn't:
>> def move2(src, dst):
>> try:
>> fd = os.open(dst, os.O_EXCL | os.O_CREAT)
>> except OSError:
>> raise Error('Destination exists')
>> try:
>> move(src, dst)
>> finally:
>> os.close(fd)
>> This could be optimised by using shutil.move code rather than just calling
>> it, but the idea is that an attempt is made to create dst with exclusive
>> access. If this fails, then it means that the file exists, but if it passes,
>> then dst is locked so no other process can create it.
>
> This type of problem comes up regularly and a lot of user code is
> riddled with this kind of race conditions. Many (most?) are avoidable
> now by writing code to using EAFP rather than LBYL, but many are not.
> I wonder if this broader problem could be addressed by a context
> manager.
>
> Something to the general effect of the usage
>
> try:
> with lockfile(dst):
> move(src, dst)
> except OSError as e:
> if e != errno.EEXIST:
> raise
> raise AppSpecificError("File already exists.") # or whatever
>
> and a definition like
>
> @contextlib.contextmanager
> def lockfile(path):
> fd = os.open(path, os.O_EXCL | os.O_CREAT)
> yield
> os.close(fd)
>
>
> The usage is still sort of ugly, but I'm not sure I can think of a
> general way that isn't.
lockfile is not a good name for this, but the manager itself is very
simple and useful. +1 from me for getting this somewhere into stdlib.
> Mike
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> http://mail.python.org/mailman/listinfo/python-ideas
>
--
Read my blog! I depend on your acceptance of my opinion! I am interesting!
http://techblog.ironfroggy.com/
Follow me if you're into that sort of thing: http://www.twitter.com/ironfroggy
More information about the Python-ideas
mailing list