/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 breezy/urlutils.py

  • Committer: Jelmer Vernooij
  • Date: 2018-11-11 04:08:32 UTC
  • mto: (7143.16.20 even-more-cleanups)
  • mto: This revision was merged to the branch mainline in revision 7175.
  • Revision ID: jelmer@jelmer.uk-20181111040832-nsljjynzzwmznf3h
Run autopep8.

Show diffs side-by-side

added added

removed removed

Lines of Context:
67
67
    def __init__(self, from_, to):
68
68
        self.from_ = from_
69
69
        self.to = to
70
 
        errors.PathError.__init__(self, from_, 'URLs differ by more than path.')
 
70
        errors.PathError.__init__(
 
71
            self, from_, 'URLs differ by more than path.')
71
72
 
72
73
 
73
74
def basename(url, exclude_trailing_slash=True):
111
112
                   '0123456789' '_.-')
112
113
    _safe_map = {}
113
114
    for i, c in zip(range(256), ''.join(map(chr, range(256)))):
114
 
        _safe_map[c] = c if (i < 128 and c in always_safe) else '%{0:02X}'.format(i)
 
115
        _safe_map[c] = c if (
 
116
            i < 128 and c in always_safe) else '%{0:02X}'.format(i)
115
117
    _safe_quoters = {}
116
118
 
117
119
    def quote_from_bytes(s, safe='/'):
174
176
    """
175
177
    if len(base) < MIN_ABS_FILEURL_LENGTH:
176
178
        raise ValueError('Length of base (%r) must equal or'
177
 
            ' exceed the platform minimum url length (which is %d)' %
178
 
            (base, MIN_ABS_FILEURL_LENGTH))
 
179
                         ' exceed the platform minimum url length (which is %d)' %
 
180
                         (base, MIN_ABS_FILEURL_LENGTH))
179
181
    base = osutils.normpath(local_path_from_url(base))
180
182
    path = osutils.normpath(local_path_from_url(path))
181
183
    return escape(osutils.relpath(base, path))
199
201
    first_path_slash = path.find('/')
200
202
    if first_path_slash == -1:
201
203
        return len(scheme), None
202
 
    return len(scheme), first_path_slash+m.start('path')
 
204
    return len(scheme), first_path_slash + m.start('path')
203
205
 
204
206
 
205
207
def is_url(url):
255
257
    """
256
258
    path = base.split('/')
257
259
    if len(path) > 1 and path[-1] == '':
258
 
        #If the path ends in a trailing /, remove it.
 
260
        # If the path ends in a trailing /, remove it.
259
261
        path.pop()
260
262
    for arg in args:
261
263
        if arg.startswith('/'):
266
268
            elif chunk == '..':
267
269
                if path == ['']:
268
270
                    raise InvalidURLJoin('Cannot go above root',
269
 
                            base, args)
 
271
                                         base, args)
270
272
                path.pop()
271
273
            else:
272
274
                path.append(chunk)
306
308
    """Convert a url like file:///C:/path/to/foo into C:/path/to/foo"""
307
309
    if not url.startswith('file://'):
308
310
        raise InvalidURL(url, 'local urls must start with file:///, '
309
 
                                     'UNC path urls must start with file://')
 
311
                         'UNC path urls must start with file://')
310
312
    url = split_segment_parameters_raw(url)[0]
311
313
    # We strip off all 3 slashes
312
314
    win32_url = url[len('file:'):]
313
315
    # check for UNC path: //HOST/path
314
316
    if not win32_url.startswith('///'):
315
317
        if (win32_url[2] == '/'
316
 
            or win32_url[3] in '|:'):
 
318
                or win32_url[3] in '|:'):
317
319
            raise InvalidURL(url, 'Win32 UNC path urls'
318
 
                ' have form file://HOST/path')
 
320
                             ' have form file://HOST/path')
319
321
        return unescape(win32_url)
320
322
 
321
323
    # allow empty paths so we can serve all roots
325
327
    # usual local path with drive letter
326
328
    if (len(win32_url) < 6
327
329
        or win32_url[3] not in ('abcdefghijklmnopqrstuvwxyz'
328
 
                                'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
329
 
        or win32_url[4] not in  '|:'
330
 
        or win32_url[5] != '/'):
 
330
                             'ABCDEFGHIJKLMNOPQRSTUVWXYZ') or
 
331
        win32_url[4] not in '|:'
 
332
            or win32_url[5] != '/'):
331
333
        raise InvalidURL(url, 'Win32 file urls start with'
332
 
                ' file:///x:/, where x is a valid drive letter')
 
334
                         ' file:///x:/, where x is a valid drive letter')
333
335
    return win32_url[3].upper() + u':' + unescape(win32_url[5:])
334
336
 
335
337
 
352
354
    if win32_path.startswith('//'):
353
355
        return 'file:' + escape(win32_path)
354
356
    return ('file:///' + str(win32_path[0].upper()) + ':' +
355
 
        escape(win32_path[2:]))
 
357
            escape(win32_path[2:]))
356
358
 
357
359
 
358
360
local_path_to_url = _posix_local_path_to_url
412
414
        for c in url:
413
415
            if c not in _url_safe_characters:
414
416
                raise InvalidURL(url, 'URLs can only contain specific'
415
 
                                            ' safe characters (not %r)' % c)
 
417
                                 ' safe characters (not %r)' % c)
416
418
        path = _url_hex_escapes_re.sub(_unescape_safe_chars, path)
417
419
        return str(prefix + ''.join(path))
418
420
 
449
451
    if base_scheme != other_scheme:
450
452
        return other
451
453
    elif sys.platform == 'win32' and base_scheme == 'file://':
452
 
        base_drive = base[base_first_slash+1:base_first_slash+3]
453
 
        other_drive = other[other_first_slash+1:other_first_slash+3]
 
454
        base_drive = base[base_first_slash + 1:base_first_slash + 3]
 
455
        other_drive = other[other_first_slash + 1:other_first_slash + 3]
454
456
        if base_drive != other_drive:
455
457
            return other
456
458
 
457
 
    base_path = base[base_first_slash+1:]
458
 
    other_path = other[other_first_slash+1:]
 
459
    base_path = base[base_first_slash + 1:]
 
460
    other_path = other[other_first_slash + 1:]
459
461
 
460
462
    if base_path.endswith('/'):
461
463
        base_path = base_path[:-1]
487
489
    # path is currently /C:/foo
488
490
    if len(path) < 4 or path[2] not in ':|' or path[3] != '/':
489
491
        raise InvalidURL(url_base + path,
490
 
            'win32 file:/// paths need a drive letter')
491
 
    url_base += path[0:3] # file:// + /C:
492
 
    path = path[3:] # /foo
 
492
                         'win32 file:/// paths need a drive letter')
 
493
    url_base += path[0:3]  # file:// + /C:
 
494
    path = path[3:]  # /foo
493
495
    return url_base, path
494
496
 
495
497
 
517
519
            return url, ''
518
520
 
519
521
    # We have a fully defined path
520
 
    url_base = url[:first_path_slash] # http://host, file://
521
 
    path = url[first_path_slash:] # /file/foo
 
522
    url_base = url[:first_path_slash]  # http://host, file://
 
523
    path = url[first_path_slash:]  # /file/foo
522
524
 
523
525
    if sys.platform == 'win32' and url.startswith('file:///'):
524
526
        # Strip off the drive letter
541
543
    """
542
544
    # GZ 2011-11-18: Dodgy removing the terminal slash like this, function
543
545
    #                operates on urls not url+segments, and Transport classes
544
 
    #                should not be blindly adding slashes in the first place. 
 
546
    #                should not be blindly adding slashes in the first place.
545
547
    lurl = strip_trailing_slash(url)
546
548
    # Segments begin at first comma after last forward slash, if one exists
547
 
    segment_start = lurl.find(",", lurl.rfind("/")+1)
 
549
    segment_start = lurl.find(",", lurl.rfind("/") + 1)
548
550
    if segment_start == -1:
549
551
        return (url, [])
550
 
    return (lurl[:segment_start], [str(s) for s in lurl[segment_start+1:].split(",")])
 
552
    return (lurl[:segment_start], [str(s) for s in lurl[segment_start + 1:].split(",")])
551
553
 
552
554
 
553
555
def split_segment_parameters(url):
583
585
            raise TypeError("Subsegment %r is not a bytestring" % subsegment)
584
586
        if "," in subsegment:
585
587
            raise InvalidURLJoin(", exists in subsegments",
586
 
                                        base, subsegments)
 
588
                                 base, subsegments)
587
589
    return ",".join((base,) + subsegments)
588
590
 
589
591
 
604
606
            raise TypeError("parameter key %r is not a str" % key)
605
607
        if not isinstance(value, str):
606
608
            raise TypeError("parameter value %r for %r is not a str" %
607
 
                (value, key))
 
609
                            (value, key))
608
610
        if "=" in key:
609
611
            raise InvalidURLJoin("= exists in parameter key", url,
610
 
                parameters)
 
612
                                 parameters)
611
613
        new_parameters[key] = value
612
614
    return join_segment_parameters_raw(base,
613
 
        *["%s=%s" % item for item in sorted(new_parameters.items())])
 
615
                                       *["%s=%s" % item for item in sorted(new_parameters.items())])
614
616
 
615
617
 
616
618
def _win32_strip_local_trailing_slash(url):
654
656
        # so just chop off the last character
655
657
        return url[:-1]
656
658
 
657
 
    if first_path_slash is None or first_path_slash == len(url)-1:
 
659
    if first_path_slash is None or first_path_slash == len(url) - 1:
658
660
        # Don't chop off anything if the only slash is the path
659
661
        # separating slash
660
662
        return url
680
682
            try:
681
683
                url.encode("ascii")
682
684
            except UnicodeError as e:
683
 
                raise InvalidURL(url, 'URL was not a plain ASCII url: %s' % (e,))
 
685
                raise InvalidURL(
 
686
                    url, 'URL was not a plain ASCII url: %s' % (e,))
684
687
        return urlparse.unquote(url)
685
688
    else:
686
689
        if isinstance(url, text_type):
687
690
            try:
688
691
                url = url.encode("ascii")
689
692
            except UnicodeError as e:
690
 
                raise InvalidURL(url, 'URL was not a plain ASCII url: %s' % (e,))
 
693
                raise InvalidURL(
 
694
                    url, 'URL was not a plain ASCII url: %s' % (e,))
691
695
        unquoted = unquote(url)
692
696
        try:
693
697
            unicode_path = unquoted.decode('utf-8')
694
698
        except UnicodeError as e:
695
 
            raise InvalidURL(url, 'Unable to encode the URL as utf-8: %s' % (e,))
 
699
            raise InvalidURL(
 
700
                url, 'Unable to encode the URL as utf-8: %s' % (e,))
696
701
        return unicode_path
697
702
 
698
703
 
700
705
_no_decode_chars = ';/?:@&=+$,#'
701
706
_no_decode_ords = [ord(c) for c in _no_decode_chars]
702
707
_no_decode_hex = (['%02x' % o for o in _no_decode_ords]
703
 
                + ['%02X' % o for o in _no_decode_ords])
 
708
                  + ['%02X' % o for o in _no_decode_ords])
704
709
_hex_display_map = dict(([('%02x' % o, int2byte(o)) for o in range(256)]
705
 
                    + [('%02X' % o, int2byte(o)) for o in range(256)]))
706
 
#These entries get mapped to themselves
707
 
_hex_display_map.update((hex, b'%'+hex.encode('ascii')) for hex in _no_decode_hex)
 
710
                         + [('%02X' % o, int2byte(o)) for o in range(256)]))
 
711
# These entries get mapped to themselves
 
712
_hex_display_map.update((hex, b'%' + hex.encode('ascii'))
 
713
                        for hex in _no_decode_hex)
708
714
 
709
715
# These characters shouldn't be percent-encoded, and it's always safe to
710
716
# unencode them if they are.
711
717
_url_dont_escape_characters = set(
712
 
   "abcdefghijklmnopqrstuvwxyz" # Lowercase alpha
713
 
   "ABCDEFGHIJKLMNOPQRSTUVWXYZ" # Uppercase alpha
714
 
   "0123456789" # Numbers
715
 
   "-._~"  # Unreserved characters
 
718
    "abcdefghijklmnopqrstuvwxyz"  # Lowercase alpha
 
719
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"  # Uppercase alpha
 
720
    "0123456789"  # Numbers
 
721
    "-._~"  # Unreserved characters
716
722
)
717
723
 
718
724
# These characters should not be escaped
719
725
_url_safe_characters = set(
720
 
   "abcdefghijklmnopqrstuvwxyz" # Lowercase alpha
721
 
   "ABCDEFGHIJKLMNOPQRSTUVWXYZ" # Uppercase alpha
722
 
   "0123456789" # Numbers
723
 
   "_.-!~*'()"  # Unreserved characters
724
 
   "/;?:@&=+$," # Reserved characters
725
 
   "%#"         # Extra reserved characters
 
726
    "abcdefghijklmnopqrstuvwxyz"  # Lowercase alpha
 
727
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"  # Uppercase alpha
 
728
    "0123456789"  # Numbers
 
729
    "_.-!~*'()"  # Unreserved characters
 
730
    "/;?:@&=+$,"  # Reserved characters
 
731
    "%#"         # Extra reserved characters
726
732
)
727
733
 
728
734
 
745
751
            escaped_chunks[j] = _hex_display_map[item[:2]]
746
752
        except KeyError:
747
753
            # Put back the percent symbol
748
 
            escaped_chunks[j] = b'%' + (item[:2].encode('utf-8') if PY3 else item[:2])
 
754
            escaped_chunks[j] = b'%' + \
 
755
                (item[:2].encode('utf-8') if PY3 else item[:2])
749
756
        except UnicodeDecodeError:
750
757
            escaped_chunks[j] = unichr(int(item[:2], 16)).encode('utf-8')
751
 
        escaped_chunks[j] +=  (item[2:].encode('utf-8') if PY3 else item[2:])
 
758
        escaped_chunks[j] += (item[2:].encode('utf-8') if PY3 else item[2:])
752
759
    unescaped = b''.join(escaped_chunks)
753
760
    try:
754
761
        decoded = unescaped.decode('utf-8')
817
824
    else:
818
825
        sep = from_location.find(":")
819
826
        if sep > 0:
820
 
            return from_location[sep+1:]
 
827
            return from_location[sep + 1:]
821
828
        else:
822
829
            return from_location
823
830
 
851
858
    to_segments = osutils.splitpath(to_path)
852
859
    count = -1
853
860
    for count, (from_element, to_element) in enumerate(zip(from_segments,
854
 
                                                       to_segments)):
 
861
                                                           to_segments)):
855
862
        if from_element != to_element:
856
863
            break
857
864
    else:
868
875
    """Parsed URL."""
869
876
 
870
877
    def __init__(self, scheme, quoted_user, quoted_password, quoted_host,
871
 
            port, quoted_path):
 
878
                 port, quoted_path):
872
879
        self.scheme = scheme
873
880
        self.quoted_host = quoted_host
874
881
        self.host = unquote(self.quoted_host)
883
890
        else:
884
891
            self.password = None
885
892
        self.port = port
886
 
        self.quoted_path = _url_hex_escapes_re.sub(_unescape_safe_chars, quoted_path)
 
893
        self.quoted_path = _url_hex_escapes_re.sub(
 
894
            _unescape_safe_chars, quoted_path)
887
895
        self.path = unquote(self.quoted_path)
888
896
 
889
897
    def __eq__(self, other):
938
946
                                     (port, url))
939
947
            else:
940
948
                port = None
941
 
        if host != "" and host[0] == '[' and host[-1] == ']': #IPv6
 
949
        if host != "" and host[0] == '[' and host[-1] == ']':  # IPv6
942
950
            host = host[1:-1]
943
951
 
944
952
        return cls(scheme, user, password, host, port, path)
1002
1010
                    continue
1003
1011
                base_parts.pop()
1004
1012
            elif p == '.':
1005
 
                continue # No-op
 
1013
                continue  # No-op
1006
1014
            elif p != '':
1007
1015
                base_parts.append(p)
1008
1016
        path = '/'.join(base_parts)
1025
1033
        else:
1026
1034
            path = self.quoted_path
1027
1035
        return self.__class__(self.scheme, self.quoted_user,
1028
 
                self.quoted_password, self.quoted_host, self.port,
1029
 
                path)
 
1036
                              self.quoted_password, self.quoted_host, self.port,
 
1037
                              path)
1030
1038
 
1031
1039
 
1032
1040
def parse_url(url):
1041
1049
    """
1042
1050
    parsed_url = URL.from_string(url)
1043
1051
    return (parsed_url.scheme, parsed_url.user, parsed_url.password,
1044
 
        parsed_url.host, parsed_url.port, parsed_url.path)
 
1052
            parsed_url.host, parsed_url.port, parsed_url.path)