180
178
rename_func(tmp_name, new)
183
def urlescape(relpath):
184
"""Escape relpath to be a valid url."""
185
if isinstance(relpath, unicode):
186
relpath = relpath.encode('utf-8')
187
# After quoting and encoding, the path should be perfectly
188
# safe as a plain ASCII string, str() just enforces this
189
return str(urllib.quote(relpath))
192
def urlunescape(url):
193
"""Unescape relpath from url format.
195
This returns a Unicode path from a URL
197
# jam 20060427 URLs are supposed to be ASCII only strings
198
# If they are passed in as unicode, urllib.unquote
199
# will return a UNICODE string, which actually contains
200
# utf-8 bytes. So we have to ensure that they are
201
# plain ASCII strings, or the final .decode will
202
# try to encode the UNICODE => ASCII, and then decode
206
except UnicodeError, e:
207
raise InvalidURL(url, 'URL was not a plain ASCII url: %s' % (e,))
208
unquoted = urllib.unquote(url)
210
unicode_path = unquoted.decode('utf-8')
211
except UnicodeError, e:
212
raise InvalidURL(url, 'Unable to encode the URL as utf-8: %s' % (e,))
216
# These are characters that if escaped, should stay that way
217
_no_decode_chars = ';/?:@&=+$,#'
218
_no_decode_ords = [ord(c) for c in _no_decode_chars]
219
_no_decode_hex = (['%02x' % o for o in _no_decode_ords]
220
+ ['%02X' % o for o in _no_decode_ords])
221
_hex_display_map = urllib._hextochr.copy()
222
_hex_display_map.update((hex,'%'+hex) for hex in _no_decode_hex)
223
#These entries get mapped to themselves
226
def urlfordisplay(url):
227
"""Decode what you can for a URL, so that we get a nice looking path.
229
This will turn file:// urls into local paths, and try to decode
230
any portions of a http:// style url that it can.
232
if url.startswith('file://'):
233
return local_path_from_url(url)
235
# Split into sections to try to decode utf-8
237
for i in xrange(1, len(res)):
238
escaped_chunks = res[i].split('%')
239
for j in xrange(1, len(escaped_chunks)):
240
item = escaped_chunks[j]
242
escaped_chunks[j] = _hex_display_map[item[:2]] + item[2:]
244
# Put back the percent symbol
245
escaped_chunks[j] = '%' + item
246
except UnicodeDecodeError:
247
escaped_chunks[j] = unichr(int(item[:2], 16)) + item[2:]
248
unescaped = ''.join(escaped_chunks)
250
res[i] = unescaped.decode('utf-8')
251
except UnicodeDecodeError:
252
# If this path segment cannot be properly utf-8 decoded
253
# after doing unescaping we will just leave it alone
257
def _posix_local_path_to_url(path):
258
"""Convert a local path like ./foo into a URL like file:///path/to/foo
260
This also handles transforming escaping unicode characters, etc.
262
# importing directly from posixpath allows us to test this
263
# on non-posix platforms
264
from posixpath import normpath
265
return 'file://' + urlescape(normpath(_posix_abspath(path)))
268
def _posix_local_path_from_url(url):
269
"""Convert a url like file:///path/to/foo into /path/to/foo"""
270
if not url.startswith('file:///'):
271
raise InvalidURL(url, 'local urls must start with file:///')
272
# We only strip off 2 slashes
273
return urlunescape(url[len('file://'):])
276
def _win32_local_path_to_url(path):
277
"""Convert a local path like ./foo into a URL like file:///C|/path/to/foo
279
This also handles transforming escaping unicode characters, etc.
281
# importing directly from ntpath allows us to test this
282
# on non-win32 platforms
283
# TODO: jam 20060426 consider moving this import outside of the function
284
win32_path = _nt_normpath(_win32_abspath(path)).replace('\\', '/')
285
return 'file:///' + win32_path[0] + '|' + urlescape(win32_path[2:])
288
def _win32_local_path_from_url(url):
289
"""Convert a url like file:///C|/path/to/foo into C:/path/to/foo"""
290
if not url.startswith('file:///'):
291
raise InvalidURL(url, 'local urls must start with file:///')
292
# We strip off all 3 slashes
293
win32_url = url[len('file:///'):]
294
if (win32_url[0] not in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
295
or win32_url[1] not in '|:'
296
or win32_url[2] != '/'):
297
raise InvalidURL(url, 'Win32 file urls start with file:///X|/, where X is a valid drive letter')
298
# TODO: jam 20060426, we could .upper() or .lower() the drive letter
299
# for better consistency.
300
return win32_url[0] + u':' + urlunescape(win32_url[2:])
303
181
# In Python 2.4.2 and older, os.path.abspath and os.path.realpath
304
182
# choke on a Unicode string containing a relative path if
305
183
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded