66import pathlib
77import re
88
9+ from python_toolbox import cute_iter_tools
910from python_toolbox import context_management
1011
1112
1516 r'''(?P<raw_name>.*) \((?P<number>[0-9]+)\)'''
1617)
1718
18- def _get_next_name (path ):
19+ def _get_next_path (path ):
20+ '''
21+ Get the name that `path` should be renamed to if taken.
22+
23+ For example, "c:\example.ogg" would become "c:\example (1).ogg", while
24+ "c:\example (1).ogg" would become "c:\example (2).ogg".
25+
26+ (Uses `Path` objects rather than strings.)
27+ '''
1928 assert isinstance (path , pathlib .Path )
2029 suffix = path .suffix
2130 suffixless_name = path .name [:- len (suffix )]
@@ -33,28 +42,35 @@ def _get_next_name(path):
3342 return pathlib .Path (
3443 '{}{}{}' .format (parent_with_separator , fixed_suffixless_name , suffix )
3544 )
45+
46+
47+ def iterate_file_paths (path ):
48+ '''
49+ Iterate over file paths, hoping to find one that's available.
50+
51+ For example, when given "c:\example.ogg", would first yield
52+ "c:\example.ogg", then "c:\example (1).ogg", then "c:\example (2).ogg", and
53+ so on.
54+
55+ (Uses `Path` objects rather than strings.)
56+ '''
57+ while True :
58+ yield path
59+ path = _get_next_path (path )
3660
3761
38- class FileContainer (context_management .ContextManager ):
39- def __init__ (self , file ):
40- self .file = file
41-
42- def __exit__ (self , exc_type , exc_value , exc_traceback ):
43- return self .file .__exit__ (exc_type , exc_value , exc_traceback )
44-
45-
4662def create_file_renaming_if_taken (path , mode = 'x' ,
4763 buffering = - 1 , encoding = None ,
4864 errors = None , newline = None ):
4965 assert 'x' in mode
50- current_path = pathlib .Path (path )
51- for i in range ( N_MAX_ATTEMPTS ):
66+ for path in cute_iter_tools . shorten ( iterate_file_paths ( pathlib .Path (path )),
67+ N_MAX_ATTEMPTS ):
5268 try :
53- return current_path .open (mode , buffering = buffering ,
69+ return path .open (mode , buffering = buffering ,
5470 encoding = encoding , errors = errors ,
5571 newline = newline )
5672 except FileExistsError :
57- current_path = _get_next_name ( current_path )
73+ pass
5874 else :
5975 raise Exception ("Exceeded {} tries, can't create file {}" .format (
6076 N_MAX_ATTEMPTS ,
0 commit comments