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

  • Committer: Richard Wilbur
  • Date: 2016-02-04 19:07:28 UTC
  • mto: This revision was merged to the branch mainline in revision 6618.
  • Revision ID: richard.wilbur@gmail.com-20160204190728-p0zvfii6zase0fw7
Update COPYING.txt from the original http://www.gnu.org/licenses/gpl-2.0.txt  (Only differences were in whitespace.)  Thanks to Petr Stodulka for pointing out the discrepancy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
import re
23
23
import sys
24
24
 
25
 
try:
26
 
    import urlparse
27
 
except ImportError:
28
 
    from urllib import parse as urlparse
 
25
from bzrlib.lazy_import import lazy_import
 
26
lazy_import(globals(), """
 
27
from posixpath import split as _posix_split
 
28
import urlparse
29
29
 
30
 
from . import (
 
30
from bzrlib import (
31
31
    errors,
32
32
    osutils,
33
33
    )
34
 
 
35
 
from .lazy_import import lazy_import
36
 
lazy_import(globals(), """
37
 
from posixpath import split as _posix_split
38
34
""")
39
35
 
40
 
from .sixish import (
41
 
    PY3,
42
 
    text_type,
43
 
    )
44
 
 
45
 
 
46
 
class InvalidURL(errors.PathError):
47
 
 
48
 
    _fmt = 'Invalid url supplied to transport: "%(path)s"%(extra)s'
49
 
 
50
 
 
51
 
class InvalidURLJoin(errors.PathError):
52
 
 
53
 
    _fmt = "Invalid URL join request: %(reason)s: %(base)r + %(join_args)r"
54
 
 
55
 
    def __init__(self, reason, base, join_args):
56
 
        self.reason = reason
57
 
        self.base = base
58
 
        self.join_args = join_args
59
 
        errors.PathError.__init__(self, base, reason)
60
 
 
61
 
 
62
 
class InvalidRebaseURLs(errors.PathError):
63
 
 
64
 
    _fmt = "URLs differ by more than path: %(from_)r and %(to)r"
65
 
 
66
 
    def __init__(self, from_, to):
67
 
        self.from_ = from_
68
 
        self.to = to
69
 
        errors.PathError.__init__(self, from_, 'URLs differ by more than path.')
70
 
 
71
36
 
72
37
def basename(url, exclude_trailing_slash=True):
73
38
    """Return the last component of a URL.
104
69
               'abcdefghijklmnopqrstuvwxyz'
105
70
               '0123456789' '_.-')
106
71
_safe_map = {}
107
 
for i, c in zip(range(256), ''.join(map(chr, range(256)))):
 
72
for i, c in zip(xrange(256), str(bytearray(xrange(256)))):
108
73
    _safe_map[c] = c if (i < 128 and c in always_safe) else '%{0:02X}'.format(i)
109
74
_safe_quoters = {}
110
75
 
149
114
    return ''.join(map(quoter, s))
150
115
 
151
116
 
152
 
unquote = urlparse.unquote
 
117
_hexdig = '0123456789ABCDEFabcdef'
 
118
_hextochr = dict((a + b, chr(int(a + b, 16)))
 
119
                 for a in _hexdig for b in _hexdig)
 
120
 
 
121
def unquote(s):
 
122
    """unquote('abc%20def') -> 'abc def'."""
 
123
    res = s.split('%')
 
124
    # fastpath
 
125
    if len(res) == 1:
 
126
        return s
 
127
    s = res[0]
 
128
    for item in res[1:]:
 
129
        try:
 
130
            s += _hextochr[item[:2]] + item[2:]
 
131
        except KeyError:
 
132
            s += '%' + item
 
133
        except UnicodeDecodeError:
 
134
            s += unichr(int(item[:2], 16)) + item[2:]
 
135
    return s
153
136
 
154
137
 
155
138
def escape(relpath):
156
139
    """Escape relpath to be a valid url."""
157
 
    if not isinstance(relpath, str):
 
140
    if isinstance(relpath, unicode):
158
141
        relpath = relpath.encode('utf-8')
159
 
    return quote(relpath, safe='/~')
 
142
    # After quoting and encoding, the path should be perfectly
 
143
    # safe as a plain ASCII string, str() just enforces this
 
144
    return str(quote(relpath, safe='/~'))
160
145
 
161
146
 
162
147
def file_relpath(base, path):
257
242
                continue
258
243
            elif chunk == '..':
259
244
                if path == ['']:
260
 
                    raise InvalidURLJoin('Cannot go above root',
 
245
                    raise errors.InvalidURLJoin('Cannot go above root',
261
246
                            base, args)
262
247
                path.pop()
263
248
            else:
276
261
    if url.startswith(file_localhost_prefix):
277
262
        path = url[len(file_localhost_prefix) - 1:]
278
263
    elif not url.startswith('file:///'):
279
 
        raise InvalidURL(
 
264
        raise errors.InvalidURL(
280
265
            url, 'local urls must start with file:/// or file://localhost/')
281
266
    else:
282
267
        path = url[len('file://'):]
297
282
def _win32_local_path_from_url(url):
298
283
    """Convert a url like file:///C:/path/to/foo into C:/path/to/foo"""
299
284
    if not url.startswith('file://'):
300
 
        raise InvalidURL(url, 'local urls must start with file:///, '
 
285
        raise errors.InvalidURL(url, 'local urls must start with file:///, '
301
286
                                     'UNC path urls must start with file://')
302
287
    url = split_segment_parameters_raw(url)[0]
303
288
    # We strip off all 3 slashes
306
291
    if not win32_url.startswith('///'):
307
292
        if (win32_url[2] == '/'
308
293
            or win32_url[3] in '|:'):
309
 
            raise InvalidURL(url, 'Win32 UNC path urls'
 
294
            raise errors.InvalidURL(url, 'Win32 UNC path urls'
310
295
                ' have form file://HOST/path')
311
296
        return unescape(win32_url)
312
297
 
320
305
                                'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
321
306
        or win32_url[4] not in  '|:'
322
307
        or win32_url[5] != '/'):
323
 
        raise InvalidURL(url, 'Win32 file urls start with'
 
308
        raise errors.InvalidURL(url, 'Win32 file urls start with'
324
309
                ' file:///x:/, where x is a valid drive letter')
325
310
    return win32_url[3].upper() + u':' + unescape(win32_url[5:])
326
311
 
359
344
    MIN_ABS_FILEURL_LENGTH = WIN32_MIN_ABS_FILEURL_LENGTH
360
345
 
361
346
 
362
 
_url_scheme_re = re.compile('^(?P<scheme>[^:/]{2,}):(//)?(?P<path>.*)$')
363
 
_url_hex_escapes_re = re.compile('(%[0-9a-fA-F]{2})')
 
347
_url_scheme_re = re.compile(r'^(?P<scheme>[^:/]{2,}):(//)?(?P<path>.*)$')
 
348
_url_hex_escapes_re = re.compile(r'(%[0-9a-fA-F]{2})')
364
349
 
365
350
 
366
351
def _unescape_safe_chars(matchobj):
400
385
        return local_path_to_url(url)
401
386
    prefix = url[:path_start]
402
387
    path = url[path_start:]
403
 
    if not isinstance(url, text_type):
 
388
    if not isinstance(url, unicode):
404
389
        for c in url:
405
390
            if c not in _url_safe_characters:
406
 
                raise InvalidURL(url, 'URLs can only contain specific'
 
391
                raise errors.InvalidURL(url, 'URLs can only contain specific'
407
392
                                            ' safe characters (not %r)' % c)
408
393
        path = _url_hex_escapes_re.sub(_unescape_safe_chars, path)
409
394
        return str(prefix + ''.join(path))
411
396
    # We have a unicode (hybrid) url
412
397
    path_chars = list(path)
413
398
 
414
 
    for i in range(len(path_chars)):
 
399
    for i in xrange(len(path_chars)):
415
400
        if path_chars[i] not in _url_safe_characters:
416
401
            chars = path_chars[i].encode('utf-8')
417
402
            path_chars[i] = ''.join(
478
463
    # Strip off the drive letter
479
464
    # path is currently /C:/foo
480
465
    if len(path) < 4 or path[2] not in ':|' or path[3] != '/':
481
 
        raise InvalidURL(url_base + path,
 
466
        raise errors.InvalidURL(url_base + path,
482
467
            'win32 file:/// paths need a drive letter')
483
468
    url_base += path[0:3] # file:// + /C:
484
469
    path = path[3:] # /foo
567
552
    if not subsegments:
568
553
        return base
569
554
    for subsegment in subsegments:
570
 
        if not isinstance(subsegment, str):
 
555
        if type(subsegment) is not str:
571
556
            raise TypeError("Subsegment %r is not a bytestring" % subsegment)
572
557
        if "," in subsegment:
573
 
            raise InvalidURLJoin(", exists in subsegments",
 
558
            raise errors.InvalidURLJoin(", exists in subsegments",
574
559
                                        base, subsegments)
575
560
    return ",".join((base,) + subsegments)
576
561
 
587
572
    (base, existing_parameters) = split_segment_parameters(url)
588
573
    new_parameters = {}
589
574
    new_parameters.update(existing_parameters)
590
 
    for key, value in parameters.items():
591
 
        if not isinstance(key, str):
 
575
    for key, value in parameters.iteritems():
 
576
        if type(key) is not str:
592
577
            raise TypeError("parameter key %r is not a bytestring" % key)
593
 
        if not isinstance(value, str):
 
578
        if type(value) is not str:
594
579
            raise TypeError("parameter value %r for %s is not a bytestring" %
595
580
                (key, value))
596
581
        if "=" in key:
597
 
            raise InvalidURLJoin("= exists in parameter key", url,
 
582
            raise errors.InvalidURLJoin("= exists in parameter key", url,
598
583
                parameters)
599
584
        new_parameters[key] = value
600
585
    return join_segment_parameters_raw(base, 
662
647
    #       plain ASCII strings, or the final .decode will
663
648
    #       try to encode the UNICODE => ASCII, and then decode
664
649
    #       it into utf-8.
665
 
    if isinstance(url, text_type):
666
 
        try:
667
 
            url = url.encode("ascii")
668
 
        except UnicodeError as e:
669
 
            raise InvalidURL(url, 'URL was not a plain ASCII url: %s' % (e,))
670
 
    if PY3:
671
 
        unquoted = urlparse.unquote_to_bytes(url)
672
 
    else:
673
 
        unquoted = unquote(url)
 
650
    try:
 
651
        url = str(url)
 
652
    except UnicodeError, e:
 
653
        raise errors.InvalidURL(url, 'URL was not a plain ASCII url: %s' % (e,))
 
654
 
 
655
    unquoted = unquote(url)
674
656
    try:
675
657
        unicode_path = unquoted.decode('utf-8')
676
 
    except UnicodeError as e:
677
 
        raise InvalidURL(url, 'Unable to encode the URL as utf-8: %s' % (e,))
 
658
    except UnicodeError, e:
 
659
        raise errors.InvalidURL(url, 'Unable to encode the URL as utf-8: %s' % (e,))
678
660
    return unicode_path
679
661
 
680
662
 
686
668
_hex_display_map = dict(([('%02x' % o, chr(o)) for o in range(256)]
687
669
                    + [('%02X' % o, chr(o)) for o in range(256)]))
688
670
#These entries get mapped to themselves
689
 
_hex_display_map.update((hex, '%'+hex) for hex in _no_decode_hex)
 
671
_hex_display_map.update((hex,'%'+hex) for hex in _no_decode_hex)
690
672
 
691
673
# These characters shouldn't be percent-encoded, and it's always safe to
692
674
# unencode them if they are.
734
716
 
735
717
    # Split into sections to try to decode utf-8
736
718
    res = url.split('/')
737
 
    for i in range(1, len(res)):
 
719
    for i in xrange(1, len(res)):
738
720
        escaped_chunks = res[i].split('%')
739
 
        for j in range(1, len(escaped_chunks)):
 
721
        for j in xrange(1, len(escaped_chunks)):
740
722
            item = escaped_chunks[j]
741
723
            try:
742
724
                escaped_chunks[j] = _hex_display_map[item[:2]] + item[2:]
804
786
    old_parsed = urlparse.urlparse(old_base)
805
787
    new_parsed = urlparse.urlparse(new_base)
806
788
    if (old_parsed[:2]) != (new_parsed[:2]):
807
 
        raise InvalidRebaseURLs(old_base, new_base)
 
789
        raise errors.InvalidRebaseURLs(old_base, new_base)
808
790
    return determine_relative_path(new_parsed[2],
809
791
                                   join(old_parsed[2], url))
810
792
 
870
852
 
871
853
        :param url: URL as bytestring
872
854
        """
873
 
        # GZ 2017-06-09: Actually validate ascii-ness
874
 
        if not isinstance(url, str):
875
 
            raise InvalidURL('should be ascii:\n%r' % url)
 
855
        if isinstance(url, unicode):
 
856
            raise errors.InvalidURL('should be ascii:\n%r' % url)
 
857
        url = url.encode('utf-8')
876
858
        (scheme, netloc, path, params,
877
859
         query, fragment) = urlparse.urlparse(url, allow_fragments=False)
878
860
        user = password = host = port = None
885
867
 
886
868
        if ':' in host and not (host[0] == '[' and host[-1] == ']'):
887
869
            # there *is* port
888
 
            host, port = host.rsplit(':', 1)
 
870
            host, port = host.rsplit(':',1)
889
871
            try:
890
872
                port = int(port)
891
873
            except ValueError:
892
 
                raise InvalidURL('invalid port number %s in url:\n%s' %
893
 
                                 (port, url))
 
874
                raise errors.InvalidURL('invalid port number %s in url:\n%s' %
 
875
                                        (port, url))
894
876
        if host != "" and host[0] == '[' and host[-1] == ']': #IPv6
895
877
            host = host[1:-1]
896
878
 
931
913
        :return: urlencoded string for final path.
932
914
        """
933
915
        if not isinstance(relpath, str):
934
 
            raise InvalidURL(relpath)
 
916
            raise errors.InvalidURL(relpath)
935
917
        relpath = _url_hex_escapes_re.sub(_unescape_safe_chars, relpath)
936
918
        if relpath.startswith('/'):
937
919
            base_parts = []