~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/urlutils.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-08-09 15:19:06 UTC
  • mfrom: (2681.1.7 send-bundle)
  • Revision ID: pqm@pqm.ubuntu.com-20070809151906-hdn9oyslf2qib2op
Allow omitting -o for bundle, add --format

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
#
15
15
# You should have received a copy of the GNU General Public License
16
16
# along with this program; if not, write to the Free Software
17
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
18
 
19
19
"""A collection of function for handling URL operations."""
20
20
 
26
26
lazy_import(globals(), """
27
27
from posixpath import split as _posix_split, normpath as _posix_normpath
28
28
import urllib
29
 
import urlparse
30
29
 
31
30
from bzrlib import (
32
31
    errors,
68
67
        relpath = relpath.encode('utf-8')
69
68
    # After quoting and encoding, the path should be perfectly
70
69
    # safe as a plain ASCII string, str() just enforces this
71
 
    return str(urllib.quote(relpath, safe='/~'))
 
70
    return str(urllib.quote(relpath))
72
71
 
73
72
 
74
73
def file_relpath(base, path):
75
74
    """Compute just the relative sub-portion of a url
76
 
 
 
75
    
77
76
    This assumes that both paths are already fully specified file:// URLs.
78
77
    """
79
 
    if len(base) < MIN_ABS_FILEURL_LENGTH:
80
 
        raise ValueError('Length of base must be equal or'
81
 
            ' exceed the platform minimum url length (which is %d)' %
82
 
            MIN_ABS_FILEURL_LENGTH)
 
78
    assert len(base) >= MIN_ABS_FILEURL_LENGTH, ('Length of base must be equal or'
 
79
        ' exceed the platform minimum url length (which is %d)' % 
 
80
        MIN_ABS_FILEURL_LENGTH)
 
81
 
83
82
    base = local_path_from_url(base)
84
83
    path = local_path_from_url(path)
85
84
    return escape(osutils.relpath(base, path))
185
184
 
186
185
def joinpath(base, *args):
187
186
    """Join URL path segments to a URL path segment.
188
 
 
 
187
    
189
188
    This is somewhat like osutils.joinpath, but intended for URLs.
190
189
 
191
190
    XXX: this duplicates some normalisation logic, and also duplicates a lot of
230
229
 
231
230
    This also handles transforming escaping unicode characters, etc.
232
231
    """
233
 
    # importing directly from posixpath allows us to test this
 
232
    # importing directly from posixpath allows us to test this 
234
233
    # on non-posix platforms
235
234
    return 'file://' + escape(_posix_normpath(
236
235
        osutils._posix_abspath(path)))
250
249
            raise errors.InvalidURL(url, 'Win32 UNC path urls'
251
250
                ' have form file://HOST/path')
252
251
        return unescape(win32_url)
253
 
 
254
 
    # allow empty paths so we can serve all roots
255
 
    if win32_url == '///':
256
 
        return '/'
257
 
 
258
252
    # usual local path with drive letter
259
253
    if (win32_url[3] not in ('abcdefghijklmnopqrstuvwxyz'
260
254
                             'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
270
264
 
271
265
    This also handles transforming escaping unicode characters, etc.
272
266
    """
273
 
    # importing directly from ntpath allows us to test this
 
267
    # importing directly from ntpath allows us to test this 
274
268
    # on non-win32 platform
275
269
    # FIXME: It turns out that on nt, ntpath.abspath uses nt._getfullpathname
276
270
    #       which actually strips trailing space characters.
277
271
    #       The worst part is that under linux ntpath.abspath has different
278
272
    #       semantics, since 'nt' is not an available module.
279
 
    if path == '/':
280
 
        return 'file:///'
281
 
 
282
273
    win32_path = osutils._win32_abspath(path)
283
274
    # check for UNC path \\HOST\path
284
275
    if win32_path.startswith('//'):
285
276
        return 'file:' + escape(win32_path)
286
 
    return ('file:///' + str(win32_path[0].upper()) + ':' +
287
 
        escape(win32_path[2:]))
 
277
    return 'file:///' + win32_path[0].upper() + ':' + escape(win32_path[2:])
288
278
 
289
279
 
290
280
local_path_to_url = _posix_local_path_to_url
305
295
 
306
296
def _unescape_safe_chars(matchobj):
307
297
    """re.sub callback to convert hex-escapes to plain characters (if safe).
308
 
 
 
298
    
309
299
    e.g. '%7E' will be converted to '~'.
310
300
    """
311
301
    hex_digits = matchobj.group(0)[1:]
318
308
 
319
309
def normalize_url(url):
320
310
    """Make sure that a path string is in fully normalized URL form.
321
 
 
 
311
    
322
312
    This handles URLs which have unicode characters, spaces,
323
313
    special characters, etc.
324
314
 
370
360
    dummy, base_first_slash = _find_scheme_and_separator(base)
371
361
    if base_first_slash is None:
372
362
        return other
373
 
 
 
363
    
374
364
    dummy, other_first_slash = _find_scheme_and_separator(other)
375
365
    if other_first_slash is None:
376
366
        return other
380
370
    other_scheme = other[:other_first_slash]
381
371
    if base_scheme != other_scheme:
382
372
        return other
383
 
    elif sys.platform == 'win32' and base_scheme == 'file://':
384
 
        base_drive = base[base_first_slash+1:base_first_slash+3]
385
 
        other_drive = other[other_first_slash+1:other_first_slash+3]
386
 
        if base_drive != other_drive:
387
 
            return other
388
373
 
389
374
    base_path = base[base_first_slash+1:]
390
375
    other_path = other[other_first_slash+1:]
418
403
    # Strip off the drive letter
419
404
    # path is currently /C:/foo
420
405
    if len(path) < 3 or path[2] not in ':|' or path[3] != '/':
421
 
        raise errors.InvalidURL(url_base + path,
 
406
        raise errors.InvalidURL(url_base + path, 
422
407
            'win32 file:/// paths need a drive letter')
423
408
    url_base += path[0:3] # file:// + /C:
424
409
    path = path[3:] # /foo
432
417
    :param exclude_trailing_slash: Strip off a final '/' if it is part
433
418
        of the path (but not if it is part of the protocol specification)
434
419
 
435
 
    :return: (parent_url, child_dir).  child_dir may be the empty string if we're at
 
420
    :return: (parent_url, child_dir).  child_dir may be the empty string if we're at 
436
421
        the root.
437
422
    """
438
423
    scheme_loc, first_path_slash = _find_scheme_and_separator(url)
542
527
# These are characters that if escaped, should stay that way
543
528
_no_decode_chars = ';/?:@&=+$,#'
544
529
_no_decode_ords = [ord(c) for c in _no_decode_chars]
545
 
_no_decode_hex = (['%02x' % o for o in _no_decode_ords]
 
530
_no_decode_hex = (['%02x' % o for o in _no_decode_ords] 
546
531
                + ['%02X' % o for o in _no_decode_ords])
547
532
_hex_display_map = dict(([('%02x' % o, chr(o)) for o in range(256)]
548
533
                    + [('%02X' % o, chr(o)) for o in range(256)]))
574
559
    This will turn file:// urls into local paths, and try to decode
575
560
    any portions of a http:// style url that it can.
576
561
 
577
 
    Any sections of the URL which can't be represented in the encoding or
 
562
    Any sections of the URL which can't be represented in the encoding or 
578
563
    need to stay as escapes are left alone.
579
564
 
580
565
    :param url: A 7-bit ASCII URL
581
566
    :param encoding: The final output encoding
582
567
 
583
 
    :return: A unicode string which can be safely encoded into the
 
568
    :return: A unicode string which can be safely encoded into the 
584
569
         specified encoding.
585
570
    """
586
 
    if encoding is None:
587
 
        raise ValueError('you cannot specify None for the display encoding')
 
571
    assert encoding is not None, 'you cannot specify None for the display encoding.'
588
572
    if url.startswith('file://'):
589
573
        try:
590
574
            path = local_path_from_url(url)
645
629
            return from_location[sep+1:]
646
630
        else:
647
631
            return from_location
648
 
 
649
 
 
650
 
def _is_absolute(url):
651
 
    return (osutils.pathjoin('/foo', url) == url)
652
 
 
653
 
 
654
 
def rebase_url(url, old_base, new_base):
655
 
    """Convert a relative path from an old base URL to a new base URL.
656
 
 
657
 
    The result will be a relative path.
658
 
    Absolute paths and full URLs are returned unaltered.
659
 
    """
660
 
    scheme, separator = _find_scheme_and_separator(url)
661
 
    if scheme is not None:
662
 
        return url
663
 
    if _is_absolute(url):
664
 
        return url
665
 
    old_parsed = urlparse.urlparse(old_base)
666
 
    new_parsed = urlparse.urlparse(new_base)
667
 
    if (old_parsed[:2]) != (new_parsed[:2]):
668
 
        raise errors.InvalidRebaseURLs(old_base, new_base)
669
 
    return determine_relative_path(new_parsed[2],
670
 
                                   join(old_parsed[2], url))
671
 
 
672
 
 
673
 
def determine_relative_path(from_path, to_path):
674
 
    """Determine a relative path from from_path to to_path."""
675
 
    from_segments = osutils.splitpath(from_path)
676
 
    to_segments = osutils.splitpath(to_path)
677
 
    count = -1
678
 
    for count, (from_element, to_element) in enumerate(zip(from_segments,
679
 
                                                       to_segments)):
680
 
        if from_element != to_element:
681
 
            break
682
 
    else:
683
 
        count += 1
684
 
    unique_from = from_segments[count:]
685
 
    unique_to = to_segments[count:]
686
 
    segments = (['..'] * len(unique_from) + unique_to)
687
 
    if len(segments) == 0:
688
 
        return '.'
689
 
    return osutils.pathjoin(*segments)