Rename file without overwriting existing files

eryk sun eryksun at gmail.com
Sun Feb 12 22:31:10 EST 2017


On Sun, Feb 12, 2017 at 4:09 AM, Steve D'Aprano
<steve+python at pearwood.info> wrote:
> On Fri, 10 Feb 2017 12:07 am, eryk sun wrote:
>
>> On Thu, Feb 9, 2017 at 11:46 AM, Steve D'Aprano
>> <steve+python at pearwood.info> wrote:
>>>
>>> So to summarise, os.rename(source, destination):
>>>
>>> - is atomic on POSIX systems, if source and destination are both on the
>>>   same file system;
>>> - may not be atomic on Windows?
>>> - may over-write an existing destination on POSIX systems, but not on
>>>   Windows;
>>> - and it doesn't work across file systems.
>>
>> On Windows in 2.7 and prior to 3.3, os.rename will silently copy and
>> delete when the destination isn't on the same volume.
>
> Will the copy overwrite an existing file?

2.7/3.2 calls MoveFile. This is effectively the same as (but not
necessarily implemented by) MoveFileEx with the flag
MOVEFILE_COPY_ALLOWED. It will not replace an existing file whether or
not the target is on the same volume. For a cross-volume move, it's
effectively a CopyFile followed by DeleteFile. If deleting the source
file fails, it tries to reset the file attributes and retries the
delete.

> On Windows, how would you implement a file rename (potentially across file
> system boundaries) which will not overwrite existing files? Just by calling
> os.rename()?

I'd prefer to always call shutil.move on Windows. In 2.7 the os.rename
call in shutil.move will copy and delete for a cross-volume move of a
file, but fail for a directory. In the latter case shutil.move falls
back on shutil.copytree and shutil.rmtree. The OS won't allow
replacing an existing file with a directory, so that's not a problem.

In 3.3+ os.rename doesn't move files across volumes on any platform --
I think. In this case the default copy2 function used by shutil.move
is a problem for everyone. Ideally we want "x" mode instead of "w"
mode for creating the destination file. It raises a FileExistsError.
3.5+ allows using a custom copy function to implement this.



More information about the Python-list mailing list