Here is an answer that does not contain a race condition from checking if the file exists in advance. It caps out at 999 attempts so the program won't hang forever if the file fails to create, the same as a shell usually would.
import os
def create_file_unique(path, read=False, binary=False, min_=1, max_=1000):
mode = ''.join(('x', '+' if read else '', 'b' if binary else ''))
def increment_file_name():
root, ext = os.path.splitext(path)
return (path if num == min_ else '%s (%d)%s' % (root, num, ext
) for num in range(min_, max_))
for i in increment_file_name():
try:
return open(i, mode)
except FileExistsError:
pass
raise IOError('The file %r could not be created uniquely.' % path)
if __name__ == '__main__':
with create_file_unique('unique.txt') as f:
print('Hello World', file=f)
By default, it will open the file with write-only permissions in text mode. Specify read as True for read and write permissions, and binary as True for binary mode. (Note that having read-only permissions would be useless - after all, you just created the file blank!)
Be aware that for the sake of simplicity, this answer (as well as the other answers here) assumes the filename passed in is "bare." That is to say, it will not handle the scenario where the filename passed in already contains a number in parenthesis, like create_file_unique('unique (2).txt'). In that case, this function will add another number on the end, resulting in unique (2) (2).txt. This differs from the typical behaviour of OS functions intended to accomplish this task, like PathYetAnotherMakeUniqueName from the Windows shell, which will check if the filename passed in already contains a number in parenthesis and will update that number in place if it finds one.
If you're willing to forego sequential filenames, you could use NamedTemporaryFile with a prefix, suffix, and deletion turned off, in order to accomplish something similar.
import os
from tempfile import NamedTemporaryFile
def create_file_unique(path, mode='w+b'):
root, ext = os.path.splitext(path)
return NamedTemporaryFile(
delete=False, mode=mode,
prefix=root, suffix=ext, dir=''
)
filename_fix_existing(filename)