26
26
# Build OS-portable and safer paths
27
27
28
28
29
- def safe_path (path , posix = False , preserve_spaces = False ):
29
+ def safe_path (path , posix = False , preserve_spaces = False , posix_only = False ):
30
30
"""
31
31
Convert `path` to a safe and portable POSIX path usable on multiple OSes.
32
32
The returned path is an ASCII-only byte string, resolved for relative
@@ -52,7 +52,13 @@ def safe_path(path, posix=False, preserve_spaces=False):
52
52
_pathmod , path_sep = path_handlers (path , posix )
53
53
54
54
segments = [s .strip () for s in path .split (path_sep ) if s .strip ()]
55
- segments = [portable_filename (s , preserve_spaces = preserve_spaces ) for s in segments ]
55
+ segments = [
56
+ portable_filename (
57
+ s ,
58
+ preserve_spaces = preserve_spaces ,
59
+ posix_only = posix_only
60
+ ) for s in segments
61
+ ]
56
62
57
63
if not segments :
58
64
return '_'
@@ -134,17 +140,34 @@ def resolve(path, posix=True):
134
140
return path
135
141
136
142
137
- legal_punctuation = r"!\#$%&\(\)\+,\-\.;\=@\[\]_\{\}\~"
138
- legal_spaces = r" "
139
- legal_chars = r'A-Za-z0-9' + legal_punctuation
143
+ legal_punctuation = r'!\#$%&\(\)\+,\-\.;\=@\[\]_\{\}\~'
144
+ legal_spaces = r' '
145
+ legal_alphanumeric = r'A-Za-z0-9'
146
+ legal_chars = legal_alphanumeric + legal_punctuation
140
147
legal_chars_inc_spaces = legal_chars + legal_spaces
141
148
illegal_chars_re = r'[^' + legal_chars + r']'
142
149
illegal_chars_exc_spaces_re = r'[^' + legal_chars_inc_spaces + r']'
143
150
replace_illegal_chars = re .compile (illegal_chars_re ).sub
144
151
replace_illegal_chars_exc_spaces = re .compile (illegal_chars_exc_spaces_re ).sub
145
152
146
153
147
- def portable_filename (filename , preserve_spaces = False ):
154
+ posix_legal_punctuation = r'<:"/>\|\*\^\\\'`\?' + legal_punctuation
155
+ posix_legal_chars = legal_alphanumeric + posix_legal_punctuation
156
+ posix_legal_chars_inc_spaces = posix_legal_chars + legal_spaces
157
+ posix_illegal_chars_re = r'[^' + posix_legal_chars + r']'
158
+ posix_illegal_chars_exc_spaces_re = r'[^' + posix_legal_chars_inc_spaces + r']'
159
+ replace_illegal_posix_chars = re .compile (posix_illegal_chars_re ).sub
160
+ replace_illegal_posix_chars_exc_spaces = re .compile (posix_illegal_chars_exc_spaces_re ).sub
161
+
162
+
163
+ ILLEGAL_WINDOWS_NAMES = set ([
164
+ 'com1' , 'com2' , 'com3' , 'com4' , 'com5' , 'com6' , 'com7' , 'com8' , 'com9' ,
165
+ 'lpt1' , 'lpt2' , 'lpt3' , 'lpt4' , 'lpt5' , 'lpt6' , 'lpt7' , 'lpt8' , 'lpt9' ,
166
+ 'aux' , 'con' , 'nul' , 'prn'
167
+ ])
168
+
169
+
170
+ def portable_filename (filename , preserve_spaces = False , posix_only = False ):
148
171
"""
149
172
Return a new name for `filename` that is portable across operating systems.
150
173
@@ -170,22 +193,21 @@ def portable_filename(filename, preserve_spaces=False):
170
193
if not filename :
171
194
return '_'
172
195
173
- if preserve_spaces :
174
- filename = replace_illegal_chars_exc_spaces ('_' , filename )
196
+ if posix_only :
197
+ if preserve_spaces :
198
+ filename = replace_illegal_posix_chars_exc_spaces ('_' , filename )
199
+ else :
200
+ filename = replace_illegal_posix_chars ('_' , filename )
175
201
else :
176
- filename = replace_illegal_chars ('_' , filename )
177
-
178
- # these are illegal both upper and lowercase and with or without an extension
179
- # we insert an underscore after the base name.
180
- windows_illegal_names = set ([
181
- 'com1' , 'com2' , 'com3' , 'com4' , 'com5' , 'com6' , 'com7' , 'com8' , 'com9' ,
182
- 'lpt1' , 'lpt2' , 'lpt3' , 'lpt4' , 'lpt5' , 'lpt6' , 'lpt7' , 'lpt8' , 'lpt9' ,
183
- 'aux' , 'con' , 'nul' , 'prn'
184
- ])
202
+ if preserve_spaces :
203
+ filename = replace_illegal_chars_exc_spaces ('_' , filename )
204
+ else :
205
+ filename = replace_illegal_chars ('_' , filename )
185
206
186
- basename , dot , extension = filename .partition ('.' )
187
- if basename .lower () in windows_illegal_names :
188
- filename = '' .join ([basename , '_' , dot , extension ])
207
+ if not posix_only :
208
+ basename , dot , extension = filename .partition ('.' )
209
+ if basename .lower () in ILLEGAL_WINDOWS_NAMES :
210
+ filename = '' .join ([basename , '_' , dot , extension ])
189
211
190
212
# no name made only of dots.
191
213
if set (filename ) == set (['.' ]):
@@ -198,6 +220,7 @@ def portable_filename(filename, preserve_spaces=False):
198
220
199
221
return filename
200
222
223
+
201
224
#
202
225
# paths comparisons, common prefix and suffix extraction
203
226
#
0 commit comments