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

1#!/usr/bin/env python 

2""" 

3general purpose file utilities 

4""" 

5from pathlib import Path 

6from random import Random 

7 

8alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789' 

9 

10rng = Random() 

11 

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) 

22 

23 

24def increment_filename(inpfile, ndigits=3, delim='.'): 

25 """ 

26 increment a data filename, returning a new (non-existing) filename 

27 

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. 

31 

32 the numerical part of the file name will contain at least three digits. 

33 

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' 

42 

43 >>> increment_filename('x.dat') 

44 'x_001.dat' 

45 

46 >>> increment_filename('C:/program files/oo/data/x.002') 

47 'C:/program files/ifeffit/data/x.003' 

48 

49 >>> increment_filename('a_001.dat') 

50 'a_002.dat' 

51 

52 >>> increment_filename('a.001.dat') 

53 'a.002.dat' 

54 

55 >>> increment_filename('a_6.dat') 

56 'a_007.dat' 

57 

58 >>> increment_filename('a_001.002') 

59 'a_001.003' 

60 

61 >>> increment_filename("path/a.003") 

62 'path/a.004' 

63""" 

64 

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' 

70 

71 if ext.startswith('.'): 

72 ext = ext[1:] 

73 ndigits = max(3, ndigits) 

74 

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) 

99 

100 # increment once 

101 base, ext = _incr(base, ext) 

102 fout = Path(dirname,f"{base}{delim}{ext}") 

103 

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() 

110 

111def new_filename(fname=None, ndigits=3): 

112 """ generate a new file name, either based on 

113 filename or generating a random one 

114 

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}" 

121 

122 if Path(fname).exists(): 

123 fname = increment_filename(fname, ndigits=ndigits) 

124 return fname 

125 

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 

129 

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}" 

136 

137 dirname = dirname.replace('.', '_') 

138 if Path(dirname).exists(): 

139 dirname = increment_filename(dirname, ndigits=ndigits, delim='_') 

140 return dirname 

141 

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')