/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: John Arbash Meinel
  • Date: 2006-05-02 20:46:11 UTC
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: john@arbash-meinel.com-20060502204611-02caa5c20fb84ef8
Moved url functions into bzrlib.urlutils

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
import types
31
31
import tempfile
32
32
import unicodedata
33
 
import urllib
34
33
from ntpath import (abspath as _nt_abspath,
35
34
                    join as _nt_join,
36
35
                    normpath as _nt_normpath,
43
42
                           NoSuchFile,
44
43
                           PathNotChild,
45
44
                           IllegalPath,
46
 
                           InvalidURL,
47
45
                           )
48
46
from bzrlib.trace import mutter
49
47
 
180
178
                rename_func(tmp_name, new)
181
179
 
182
180
 
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))
190
 
 
191
 
 
192
 
def urlunescape(url):
193
 
    """Unescape relpath from url format.
194
 
 
195
 
    This returns a Unicode path from a URL
196
 
    """
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
203
 
    #       it into utf-8.
204
 
    try:
205
 
        url = str(url)
206
 
    except UnicodeError, e:
207
 
        raise InvalidURL(url, 'URL was not a plain ASCII url: %s' % (e,))
208
 
    unquoted = urllib.unquote(url)
209
 
    try:
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,))
213
 
    return unicode_path
214
 
 
215
 
 
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
224
 
 
225
 
 
226
 
def urlfordisplay(url):
227
 
    """Decode what you can for a URL, so that we get a nice looking path.
228
 
 
229
 
    This will turn file:// urls into local paths, and try to decode
230
 
    any portions of a http:// style url that it can.
231
 
    """
232
 
    if url.startswith('file://'):
233
 
        return local_path_from_url(url)
234
 
 
235
 
    # Split into sections to try to decode utf-8
236
 
    res = url.split('/')
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]
241
 
            try:
242
 
                escaped_chunks[j] = _hex_display_map[item[:2]] + item[2:]
243
 
            except KeyError:
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)
249
 
        try:
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
254
 
            pass
255
 
    return '/'.join(res)
256
 
 
257
 
def _posix_local_path_to_url(path):
258
 
    """Convert a local path like ./foo into a URL like file:///path/to/foo
259
 
 
260
 
    This also handles transforming escaping unicode characters, etc.
261
 
    """
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)))
266
 
 
267
 
 
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://'):])
274
 
 
275
 
 
276
 
def _win32_local_path_to_url(path):
277
 
    """Convert a local path like ./foo into a URL like file:///C|/path/to/foo
278
 
 
279
 
    This also handles transforming escaping unicode characters, etc.
280
 
    """
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:])
286
 
 
287
 
 
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:])
301
 
 
302
 
 
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
356
234
rename = os.rename
357
235
dirname = os.path.dirname
358
236
basename = os.path.basename
359
 
local_path_to_url = _posix_local_path_to_url
360
 
local_path_from_url = _posix_local_path_from_url
361
237
 
362
238
MIN_ABS_PATHLENGTH = 1
363
 
MIN_ABS_URLPATHLENGTH = len('file:///')
364
239
 
365
240
 
366
241
if sys.platform == 'win32':
372
247
    mkdtemp = _win32_mkdtemp
373
248
    rename = _win32_rename
374
249
 
375
 
    local_path_to_url = _win32_local_path_to_url
376
 
    local_path_from_url = _win32_local_path_from_url
377
 
 
378
250
    MIN_ABS_PATHLENGTH = 3
379
 
    MIN_ABS_URLPATHLENGTH = len('file:///C|/')
380
251
 
381
252
 
382
253
def normalizepath(f):
784
655
        return ''
785
656
 
786
657
 
787
 
def urlrelpath(base, path):
788
 
    """Compute just the relative sub-portion of a url
789
 
    
790
 
    This assumes that both paths are already fully specified file:// URLs.
791
 
    """
792
 
    assert len(base) >= MIN_ABS_URLPATHLENGTH, ('Length of base must be equal or'
793
 
        ' exceed the platform minimum url length (which is %d)' % 
794
 
        MIN_ABS_URLPATHLENGTH)
795
 
 
796
 
    base = local_path_from_url(base)
797
 
    path = local_path_from_url(path)
798
 
    return urlescape(relpath(base, path))
799
 
 
800
 
 
801
658
def safe_unicode(unicode_or_utf8_string):
802
659
    """Coerce unicode_or_utf8_string into unicode.
803
660
 
879
736
    return sys.platform != "win32"
880
737
 
881
738
 
882
 
def strip_url_trailing_slash(path):
883
 
    """Strip trailing slash, except for root paths.
884
 
    The definition of 'root path' is platform-dependent.
885
 
    """
886
 
    assert path.startswith('file:///'), \
887
 
        'strip_url_trailing_slash expects file:// urls (%s)' % path
888
 
    if len(path) != MIN_ABS_URLPATHLENGTH and path[-1] == '/':
889
 
        return path[:-1]
890
 
    else:
891
 
        return path
892
 
 
893
 
 
894
739
_validWin32PathRE = re.compile(r'^([A-Za-z]:[/\\])?[^:<>*"?\|]*$')
895
740
 
896
741