2626# Build OS-portable and safer paths
2727
2828
29- def safe_path (path , posix = False ):
29+ def safe_path (path , posix = False , preserve_spaces = False ):
3030 """
3131 Convert `path` to a safe and portable POSIX path usable on multiple OSes.
3232 The returned path is an ASCII-only byte string, resolved for relative
3333 segments and itself relative.
3434
3535 The `path` is treated as a POSIX path if `posix` is True or as a Windows
3636 path with blackslash separators otherwise.
37+
38+ If `preserve_spaces` is True, then the spaces in `path` will not be replaced.
3739 """
3840 # if the path is UTF, try to use unicode instead
3941 if not isinstance (path , str ):
@@ -50,7 +52,7 @@ def safe_path(path, posix=False):
5052 _pathmod , path_sep = path_handlers (path , posix )
5153
5254 segments = [s .strip () for s in path .split (path_sep ) if s .strip ()]
53- segments = [portable_filename (s ) for s in segments ]
55+ segments = [portable_filename (s , preserve_spaces = preserve_spaces ) for s in segments ]
5456
5557 if not segments :
5658 return '_'
@@ -133,12 +135,16 @@ def resolve(path, posix=True):
133135
134136
135137legal_punctuation = r"!\#$%&\(\)\+,\-\.;\=@\[\]_\{\}\~"
138+ legal_spaces = r" "
136139legal_chars = r'A-Za-z0-9' + legal_punctuation
140+ legal_chars_inc_spaces = legal_chars + legal_spaces
137141illegal_chars_re = r'[^' + legal_chars + r']'
142+ illegal_chars_exc_spaces_re = r'[^' + legal_chars_inc_spaces + r']'
138143replace_illegal_chars = re .compile (illegal_chars_re ).sub
144+ replace_illegal_chars_exc_spaces = re .compile (illegal_chars_exc_spaces_re ).sub
139145
140146
141- def portable_filename (filename ):
147+ def portable_filename (filename , preserve_spaces = False ):
142148 """
143149 Return a new name for `filename` that is portable across operating systems.
144150
@@ -156,13 +162,18 @@ def portable_filename(filename):
156162
157163 Also inspired by Werkzeug:
158164 https://raw.githubusercontent.com/pallets/werkzeug/8c2d63ce247ba1345e1b9332a68ceff93b2c07ab/werkzeug/utils.py
165+
166+ If `preserve_spaces` is True, then spaces in `filename` will not be replaced.
159167 """
160168 filename = toascii (filename , translit = True )
161169
162170 if not filename :
163171 return '_'
164172
165- filename = replace_illegal_chars ('_' , filename )
173+ if preserve_spaces :
174+ filename = replace_illegal_chars_exc_spaces ('_' , filename )
175+ else :
176+ filename = replace_illegal_chars ('_' , filename )
166177
167178 # these are illegal both upper and lowercase and with or without an extension
168179 # we insert an underscore after the base name.
0 commit comments