Coverage for larch/io/fileutils.py: 12%
72 statements
« prev ^ index » next coverage.py v7.6.0, created at 2024-10-16 21:04 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2024-10-16 21:04 +0000
1#!/usr/bin/env python
2"""
3general purpose file utilities
4"""
5from pathlib import Path
6from random import Random
8alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789'
10rng = Random()
12def random_string(n, rng_seed=None):
13 """ random_string(n)
14 generates a random string of length n, that will match:
15 [a-z][a-z0-9](n-1)
16 """
17 if rng_seed is not None:
18 rng.seed(rng_seed)
19 s = [nng.choice(alphanum[:26])]
20 s.extend([rng.choice(alphanum) for i in range(n-2)])
21 return ''.join(s)
24def increment_filename(inpfile, ndigits=3, delim='.'):
25 """
26 increment a data filename, returning a new (non-existing) filename
28 first see if a number is before '.'. if so, increment it.
29 second look for number in the prefix. if so, increment it.
30 lastly, insert a '_001' before the '.', preserving suffix.
32 the numerical part of the file name will contain at least three digits.
34 >>> increment_filename('a.002')
35 'a.003'
36 >>> increment_filename('a.999')
37 'a.1000'
38 >>> increment_filename('b_017.xrf')
39 'b_018.xrf'
40 >>> increment_filename('x_10300243.dat')
41 'x_10300244.dat'
43 >>> increment_filename('x.dat')
44 'x_001.dat'
46 >>> increment_filename('C:/program files/oo/data/x.002')
47 'C:/program files/ifeffit/data/x.003'
49 >>> increment_filename('a_001.dat')
50 'a_002.dat'
52 >>> increment_filename('a.001.dat')
53 'a.002.dat'
55 >>> increment_filename('a_6.dat')
56 'a_007.dat'
58 >>> increment_filename('a_001.002')
59 'a_001.003'
61 >>> increment_filename("path/a.003")
62 'path/a.004'
63"""
65 pinp = Path(inpfile)
66 dirname, filename = pinp.parent.as_posix(), pinp.name
67 base, ext = pinp.stem, pinp.suffix
68 if ext == '':
69 ext = '.000'
71 if ext.startswith('.'):
72 ext = ext[1:]
73 ndigits = max(3, ndigits)
75 def _incr(base, ext):
76 if ext.isdigit():
77 ext = f"{(int(ext)+1):0{ndigits}d}"
78 else:
79 found = False
80 if '_' in base:
81 parts = base.split('_')
82 for iw, word in enumerate(parts[::-1]):
83 if word.isdigit():
84 parts[len(parts)-iw-1] = f"{(int(word)+1):0{ndigits}d}"
85 found = True
86 break
87 base = '_'.join(parts)
88 if not found and '.' in base:
89 parts = base.split('.')
90 for iw, word in enumerate(parts[::-1]):
91 if word.isdigit():
92 parts[len(parts)-iw-1] = f"{(int(word)+1):0{ndigits}d}"
93 found = True
94 break
95 base = '.'.join(parts)
96 if not found:
97 base = f"{base}_001"
98 return (base, ext)
100 # increment once
101 base, ext = _incr(base, ext)
102 fout = Path(dirname,f"{base}{delim}{ext}")
104 # then gaurantee that file does not exist,
105 # continuing to increment if necessary
106 while fout.exists():
107 base, ext = _incr(base, ext)
108 fout = Path(dirname,f"{base}{delim}{ext}")
109 return fout.as_posix()
111def new_filename(fname=None, ndigits=3):
112 """ generate a new file name, either based on
113 filename or generating a random one
115 >>> new_filename(fname='x.001')
116 'x.002'
117 # if 'x.001' exists
118 """
119 if fname is None:
120 fname = f"{random_string(6)}.{1:0{ndigits}d}"
122 if Path(fname).exists():
123 fname = increment_filename(fname, ndigits=ndigits)
124 return fname
126def new_dirname(dirname=None, ndigits=3):
127 """ generate a new subdirectory name (no '.' in name), either
128 based on dirname or generating a random one
130 >>> new_dirname('x.001')
131 'x_002'
132 # if 'x_001' exists
133 """
134 if dirname is None:
135 dirname = f"{random_string(6)}_{1:0{ndigits}d}"
137 dirname = dirname.replace('.', '_')
138 if Path(dirname).exists():
139 dirname = increment_filename(dirname, ndigits=ndigits, delim='_')
140 return dirname
142def test_incrementfilename():
143 tests = (('a.002', 'a.003'),
144 ('a.999', 'a.1000'),
145 ('b_017.xrf', 'b_018.xrf'),
146 ('x_10300243.dat', 'x_10300244.dat'),
147 ('x.dat' , 'x_001.dat'),
148 ('C:/program files/data/x.002',
149 'C:/program files/data/x.003'),
150 ('a_001.dat', 'a_002.dat'),
151 ('a_6.dat', 'a_007.dat'),
152 ('a_001.002', 'a_001.003'),
153 ('path/a.003', 'path/a.004'))
154 npass = nfail = 0
155 for inp,out in tests:
156 tval = increment_filename(inp)
157 if tval != out:
158 print(f"Error converting {inp}")
159 print(f"Got '{tval}' expected '{out}'")
160 nfail = nfail + 1
161 else:
162 npass = npass + 1
163 print(f'Passed {npass} of {npass+nfail} tests')