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

  • Committer: Jelmer Vernooij
  • Date: 2018-05-06 11:48:54 UTC
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@jelmer.uk-20180506114854-h4qd9ojaqy8wxjsd
Move .mailmap to root.

Show diffs side-by-side

added added

removed removed

Lines of Context:
70
70
    )
71
71
 
72
72
 
 
73
# Cross platform wall-clock time functionality with decent resolution.
 
74
# On Linux ``time.clock`` returns only CPU time. On Windows, ``time.time()``
 
75
# only has a resolution of ~15ms. Note that ``time.clock()`` is not
 
76
# synchronized with ``time.time()``, this is only meant to be used to find
 
77
# delta times by subtracting from another call to this function.
 
78
timer_func = time.time
 
79
if sys.platform == 'win32':
 
80
    timer_func = time.clock
 
81
 
73
82
# On win32, O_BINARY is used to indicate the file should
74
83
# be opened in binary mode, rather than text mode.
75
84
# On other platforms, O_BINARY doesn't exist, because
98
107
        return [a.decode(user_encoding) for a in sys.argv[1:]]
99
108
    except UnicodeDecodeError:
100
109
        raise errors.BzrError(gettext("Parameter {0!r} encoding is unsupported by {1} "
101
 
                                      "application locale.").format(a, user_encoding))
 
110
            "application locale.").format(a, user_encoding))
102
111
 
103
112
 
104
113
def make_readonly(filename):
146
155
        return set(paths)
147
156
 
148
157
    def sort_key(path):
149
 
        if isinstance(path, bytes):
150
 
            return path.split(b'/')
151
 
        else:
152
 
            return path.split('/')
 
158
        return path.split('/')
153
159
    sorted_paths = sorted(list(paths), key=sort_key)
154
160
 
155
161
    search_paths = [sorted_paths[0]]
182
188
 
183
189
_directory_kind = 'directory'
184
190
 
185
 
 
186
191
def get_umask():
187
192
    """Return the current umask"""
188
193
    # Assume that people aren't messing with the umask while running
219
224
            return True
220
225
        except OSError as e:
221
226
            if e.errno == errno.ENOENT:
222
 
                return False
 
227
                return False;
223
228
            else:
224
 
                raise errors.BzrError(
225
 
                    gettext("lstat/stat of ({0!r}): {1!r}").format(f, e))
 
229
                raise errors.BzrError(gettext("lstat/stat of ({0!r}): {1!r}").format(f, e))
226
230
 
227
231
 
228
232
def fancy_rename(old, new, rename_func, unlink_func):
252
256
    file_existed = False
253
257
    try:
254
258
        rename_func(new, tmp_name)
255
 
    except (errors.NoSuchFile,):
 
259
    except (errors.NoSuchFile,) as e:
256
260
        pass
257
261
    except IOError as e:
258
262
        # RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
262
266
            raise
263
267
    except Exception as e:
264
268
        if (getattr(e, 'errno', None) is None
265
 
                or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
 
269
            or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
266
270
            raise
267
271
    else:
268
272
        file_existed = True
278
282
        # case-insensitive filesystem), so we may have accidentally renamed
279
283
        # source by when we tried to rename target
280
284
        if (file_existed and e.errno in (None, errno.ENOENT)
281
 
                and old.lower() == new.lower()):
 
285
            and old.lower() == new.lower()):
282
286
            # source and target are the same file on a case-insensitive
283
287
            # filesystem, so we don't generate an exception
284
288
            pass
319
323
    # as a special case here by simply removing the first slash, as we consider
320
324
    # that breaking POSIX compatibility for this obscure feature is acceptable.
321
325
    # This is not a paranoid precaution, as we notably get paths like this when
322
 
    # the repo is hosted at the root of the filesystem, i.e. in "/".
 
326
    # the repo is hosted at the root of the filesystem, i.e. in "/".    
323
327
    if path.startswith('//'):
324
328
        path = path[1:]
325
329
    return path
362
366
        return name.decode(user_encoding)
363
367
    except UnicodeDecodeError:
364
368
        raise errors.BzrError("Encoding of username %r is unsupported by %s "
365
 
                              "application locale." % (name, user_encoding))
 
369
            "application locale." % (name, user_encoding))
366
370
 
367
371
 
368
372
def _win32_fixdrive(path):
380
384
 
381
385
def _win32_abspath(path):
382
386
    # Real ntpath.abspath doesn't have a problem with a unicode cwd
383
 
    return _win32_fixdrive(ntpath.abspath(path).replace('\\', '/'))
 
387
    return _win32_fixdrive(ntpath.abspath(unicode(path)).replace('\\', '/'))
384
388
 
385
389
 
386
390
def _win32_realpath(path):
387
391
    # Real ntpath.realpath doesn't have a problem with a unicode cwd
388
 
    return _win32_fixdrive(ntpath.realpath(path).replace('\\', '/'))
 
392
    return _win32_fixdrive(ntpath.realpath(unicode(path)).replace('\\', '/'))
389
393
 
390
394
 
391
395
def _win32_pathjoin(*args):
393
397
 
394
398
 
395
399
def _win32_normpath(path):
396
 
    return _win32_fixdrive(ntpath.normpath(path).replace('\\', '/'))
 
400
    return _win32_fixdrive(ntpath.normpath(unicode(path)).replace('\\', '/'))
397
401
 
398
402
 
399
403
def _win32_getcwd():
438
442
            rename_func(old, new)
439
443
        except OSError as e:
440
444
            detailed_error = OSError(e.errno, e.strerror +
441
 
                                     " [occurred when renaming '%s' to '%s']" %
442
 
                                     (old, new))
 
445
                                " [occurred when renaming '%s' to '%s']" %
 
446
                                (old, new))
443
447
            detailed_error.old_filename = old
444
448
            detailed_error.new_filename = new
445
449
            raise detailed_error
476
480
lstat = os.lstat
477
481
fstat = os.fstat
478
482
 
479
 
 
480
483
def wrap_stat(st):
481
484
    return st
482
485
 
509
512
        """
510
513
        exception = excinfo[1]
511
514
        if function in (os.remove, os.rmdir) \
512
 
                and isinstance(exception, OSError) \
513
 
                and exception.errno == errno.EACCES:
 
515
            and isinstance(exception, OSError) \
 
516
            and exception.errno == errno.EACCES:
514
517
            make_writable(path)
515
518
            function(path)
516
519
        else:
520
523
        """Replacer for shutil.rmtree: could remove readonly dirs/files"""
521
524
        return shutil.rmtree(path, ignore_errors, onerror)
522
525
 
523
 
    get_unicode_argv = getattr(win32utils, 'get_unicode_argv', get_unicode_argv)
 
526
    f = win32utils.get_unicode_argv     # special function or None
 
527
    if f is not None:
 
528
        get_unicode_argv = f
524
529
    path_from_environ = win32utils.get_environ_unicode
525
530
    _get_home_dir = win32utils.get_home_location
526
531
    getuser_unicode = win32utils.get_user_name
552
557
            output_encoding = get_user_encoding()
553
558
            if trace:
554
559
                mutter('encoding stdout as osutils.get_user_encoding() %r',
555
 
                       output_encoding)
 
560
                   output_encoding)
556
561
        else:
557
562
            output_encoding = input_encoding
558
563
            if trace:
559
564
                mutter('encoding stdout as sys.stdin encoding %r',
560
 
                       output_encoding)
 
565
                    output_encoding)
561
566
    else:
562
567
        if trace:
563
568
            mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
566
571
        output_encoding = get_user_encoding()
567
572
        if trace:
568
573
            mutter('cp0 is invalid encoding.'
569
 
                   ' encoding stdout as osutils.get_user_encoding() %r',
570
 
                   output_encoding)
 
574
               ' encoding stdout as osutils.get_user_encoding() %r',
 
575
               output_encoding)
571
576
    # check encoding
572
577
    try:
573
578
        codecs.lookup(output_encoding)
576
581
                         ' unknown terminal encoding %s.\n'
577
582
                         '  Using encoding %s instead.\n'
578
583
                         % (output_encoding, get_user_encoding())
579
 
                         )
 
584
                        )
580
585
        output_encoding = get_user_encoding()
581
586
 
582
587
    return output_encoding
609
614
    except OSError:
610
615
        return False
611
616
 
612
 
 
613
617
def islink(f):
614
618
    """True if f is a symlink."""
615
619
    try:
617
621
    except OSError:
618
622
        return False
619
623
 
620
 
 
621
624
def is_inside(dir, fname):
622
625
    """True if fname is inside dir.
623
626
 
633
636
    if dir == fname:
634
637
        return True
635
638
 
636
 
    if dir in ('', b''):
 
639
    if dir == '':
637
640
        return True
638
641
 
639
 
    if isinstance(dir, bytes):
640
 
        if not dir.endswith(b'/'):
641
 
            dir += b'/'
642
 
    else:
643
 
        if not dir.endswith('/'):
644
 
            dir += '/'
 
642
    if dir[-1] != '/':
 
643
        dir += '/'
645
644
 
646
645
    return fname.startswith(dir)
647
646
 
720
719
    # writes fail on some platforms (e.g. Windows with SMB  mounted
721
720
    # drives).
722
721
    if not segment_size:
723
 
        segment_size = 5242880  # 5MB
 
722
        segment_size = 5242880 # 5MB
724
723
    offsets = range(0, len(bytes), segment_size)
725
724
    view = memoryview(bytes)
726
725
    write = file_handle.write
727
726
    for offset in offsets:
728
 
        write(view[offset:offset + segment_size])
 
727
        write(view[offset:offset+segment_size])
729
728
 
730
729
 
731
730
def file_iterator(input_file, readsize=32768):
752
751
    The file cursor should be already at the start.
753
752
    """
754
753
    s = sha()
755
 
    BUFSIZE = 128 << 10
 
754
    BUFSIZE = 128<<10
756
755
    while True:
757
756
        b = f.read(BUFSIZE)
758
757
        if not b:
769
768
    """
770
769
    size = 0
771
770
    s = sha()
772
 
    BUFSIZE = 128 << 10
 
771
    BUFSIZE = 128<<10
773
772
    while True:
774
773
        b = f.read(BUFSIZE)
775
774
        if not b:
785
784
    f = os.open(fname, os.O_RDONLY | O_BINARY | O_NOINHERIT)
786
785
    try:
787
786
        while True:
788
 
            b = os.read(f, 1 << 16)
 
787
            b = os.read(f, 1<<16)
789
788
            if not b:
790
789
                return _hexdigest(s)
791
790
            s.update(b)
820
819
        bi = b.read(BUFSIZE)
821
820
        if ai != bi:
822
821
            return False
823
 
        if not ai:
 
822
        if ai == '':
824
823
            return True
825
824
 
826
825
 
831
830
    offset = datetime.fromtimestamp(t) - datetime.utcfromtimestamp(t)
832
831
    return offset.days * 86400 + offset.seconds
833
832
 
834
 
 
835
833
weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
836
834
_default_format_by_weekday_num = [wd + " %Y-%m-%d %H:%M:%S" for wd in weekdays]
837
835
 
849
847
    :param show_offset: Whether to append the timezone.
850
848
    """
851
849
    (date_fmt, tt, offset_str) = \
852
 
        _format_date(t, offset, timezone, date_fmt, show_offset)
 
850
               _format_date(t, offset, timezone, date_fmt, show_offset)
853
851
    date_fmt = date_fmt.replace('%a', weekdays[tt[6]])
854
852
    date_str = time.strftime(date_fmt, tt)
855
853
    return date_str + offset_str
860
858
 
861
859
 
862
860
def format_date_with_offset_in_original_timezone(t, offset=0,
863
 
                                                 _cache=_offset_cache):
 
861
    _cache=_offset_cache):
864
862
    """Return a formatted date string in the original timezone.
865
863
 
866
864
    This routine may be faster then format_date.
893
891
    :param show_offset: Whether to append the timezone.
894
892
    """
895
893
    (date_fmt, tt, offset_str) = \
896
 
        _format_date(t, offset, timezone, date_fmt, show_offset)
 
894
               _format_date(t, offset, timezone, date_fmt, show_offset)
897
895
    date_str = time.strftime(date_fmt, tt)
898
896
    if not isinstance(date_str, text_type):
899
897
        date_str = date_str.decode(get_user_encoding(), 'replace')
942
940
        delta = -delta
943
941
 
944
942
    seconds = delta
945
 
    if seconds < 90:  # print seconds up to 90 seconds
 
943
    if seconds < 90: # print seconds up to 90 seconds
946
944
        if seconds == 1:
947
945
            return '%d second %s' % (seconds, direction,)
948
946
        else:
954
952
        plural_seconds = ''
955
953
    else:
956
954
        plural_seconds = 's'
957
 
    if minutes < 90:  # print minutes, seconds up to 90 minutes
 
955
    if minutes < 90: # print minutes, seconds up to 90 minutes
958
956
        if minutes == 1:
959
957
            return '%d minute, %d second%s %s' % (
960
 
                minutes, seconds, plural_seconds, direction)
 
958
                    minutes, seconds, plural_seconds, direction)
961
959
        else:
962
960
            return '%d minutes, %d second%s %s' % (
963
 
                minutes, seconds, plural_seconds, direction)
 
961
                    minutes, seconds, plural_seconds, direction)
964
962
 
965
963
    hours = int(minutes / 60)
966
964
    minutes -= 60 * hours
975
973
    return '%d hours, %d minute%s %s' % (hours, minutes,
976
974
                                         plural_minutes, direction)
977
975
 
978
 
 
979
976
def filesize(f):
980
977
    """Return size of given open file."""
981
978
    return os.fstat(f.fileno())[stat.ST_SIZE]
982
979
 
983
980
 
984
 
# Alias os.urandom to support platforms (which?) without /dev/urandom and
 
981
# Alias os.urandom to support platforms (which?) without /dev/urandom and 
985
982
# override if it doesn't work. Avoid checking on windows where there is
986
983
# significant initialisation cost that can be avoided for some bzr calls.
987
984
 
1002
999
 
1003
1000
 
1004
1001
ALNUM = '0123456789abcdefghijklmnopqrstuvwxyz'
1005
 
 
1006
 
 
1007
1002
def rand_chars(num):
1008
1003
    """Return a random string of num alphanumeric characters
1009
1004
 
1019
1014
    return s
1020
1015
 
1021
1016
 
1022
 
# TODO: We could later have path objects that remember their list
1023
 
# decomposition (might be too tricksy though.)
 
1017
## TODO: We could later have path objects that remember their list
 
1018
## decomposition (might be too tricksy though.)
1024
1019
 
1025
1020
def splitpath(p):
1026
1021
    """Turn string into list of parts."""
1027
 
    use_bytes = isinstance(p, bytes)
1028
 
    if os.path.sep == '\\':
1029
 
        # split on either delimiter because people might use either on
1030
 
        # Windows
1031
 
        if use_bytes:
1032
 
            ps = re.split(b'[\\\\/]', p)
1033
 
        else:
1034
 
            ps = re.split(r'[\\/]', p)
1035
 
    else:
1036
 
        if use_bytes:
1037
 
            ps = p.split(b'/')
1038
 
        else:
1039
 
            ps = p.split('/')
1040
 
 
1041
 
    if use_bytes:
1042
 
        parent_dir = b'..'
1043
 
        current_empty_dir = (b'.', b'')
1044
 
    else:
1045
 
        parent_dir = '..'
1046
 
        current_empty_dir = ('.', '')
 
1022
    # split on either delimiter because people might use either on
 
1023
    # Windows
 
1024
    ps = re.split(r'[\\/]', p)
1047
1025
 
1048
1026
    rps = []
1049
1027
    for f in ps:
1050
 
        if f == parent_dir:
 
1028
        if f == '..':
1051
1029
            raise errors.BzrError(gettext("sorry, %r not allowed in path") % f)
1052
 
        elif f in current_empty_dir:
 
1030
        elif (f == '.') or (f == ''):
1053
1031
            pass
1054
1032
        else:
1055
1033
            rps.append(f)
1132
1110
    """Split s into lines, but without removing the newline characters."""
1133
1111
    # Trivially convert a fulltext into a 'chunked' representation, and let
1134
1112
    # chunks_to_lines do the heavy lifting.
1135
 
    if isinstance(s, bytes):
 
1113
    if isinstance(s, str):
1136
1114
        # chunks_to_lines only supports 8-bit strings
1137
1115
        return chunks_to_lines([s])
1138
1116
    else:
1175
1153
    Will delete even if readonly.
1176
1154
    """
1177
1155
    try:
1178
 
        _delete_file_or_dir(path)
 
1156
       _delete_file_or_dir(path)
1179
1157
    except (OSError, IOError) as e:
1180
1158
        if e.errno in (errno.EPERM, errno.EACCES):
1181
1159
            # make writable and try again
1194
1172
    # - root can damage a solaris file system by using unlink,
1195
1173
    # - unlink raises different exceptions on different OSes (linux: EISDIR, win32:
1196
1174
    #   EACCES, OSX: EPERM) when invoked on a directory.
1197
 
    if isdir(path):  # Takes care of symlinks
 
1175
    if isdir(path): # Takes care of symlinks
1198
1176
        os.rmdir(path)
1199
1177
    else:
1200
1178
        os.unlink(path)
1279
1257
    if len(base) < MIN_ABS_PATHLENGTH:
1280
1258
        # must have space for e.g. a drive letter
1281
1259
        raise ValueError(gettext('%r is too short to calculate a relative path')
1282
 
                         % (base,))
 
1260
            % (base,))
1283
1261
 
1284
1262
    rp = abspath(path)
1285
1263
 
1330
1308
        lbit = bit.lower()
1331
1309
        try:
1332
1310
            next_entries = _listdir(current)
1333
 
        except OSError:  # enoent, eperm, etc
 
1311
        except OSError: # enoent, eperm, etc
1334
1312
            # We can't find this in the filesystem, so just append the
1335
1313
            # remaining bits.
1336
1314
            current = pathjoin(current, bit, *list(bit_iter))
1347
1325
            break
1348
1326
    return current[len(abs_base):].lstrip('/')
1349
1327
 
1350
 
 
1351
1328
# XXX - TODO - we need better detection/integration of case-insensitive
1352
1329
# file-systems; Linux often sees FAT32 devices (or NFS-mounted OSX
1353
1330
# filesystems), for example, so could probably benefit from the same basic
1358
1335
else:
1359
1336
    canonical_relpath = relpath
1360
1337
 
1361
 
 
1362
1338
def canonical_relpaths(base, paths):
1363
1339
    """Create an iterable to canonicalize a sequence of relative paths.
1364
1340
 
1426
1402
    :return: None or a utf8 revision id.
1427
1403
    """
1428
1404
    if (unicode_or_utf8_string is None
1429
 
            or unicode_or_utf8_string.__class__ == bytes):
 
1405
        or unicode_or_utf8_string.__class__ == bytes):
1430
1406
        return unicode_or_utf8_string
1431
1407
    raise TypeError('Unicode revision ids are no longer supported. '
1432
1408
                    'Revision id generators should be creating utf8 revision '
1444
1420
    :return: None or a utf8 file id.
1445
1421
    """
1446
1422
    if (unicode_or_utf8_string is None
1447
 
            or unicode_or_utf8_string.__class__ == bytes):
 
1423
        or unicode_or_utf8_string.__class__ == bytes):
1448
1424
        return unicode_or_utf8_string
1449
1425
    raise TypeError('Unicode file ids are no longer supported. '
1450
1426
                    'File id generators should be creating utf8 file ids.')
1478
1454
    can be accessed by that path.
1479
1455
    """
1480
1456
 
1481
 
    if isinstance(path, bytes):
1482
 
        path = path.decode(sys.getfilesystemencoding())
1483
 
    return unicodedata.normalize('NFC', path), True
 
1457
    return unicodedata.normalize('NFC', text_type(path)), True
1484
1458
 
1485
1459
 
1486
1460
def _inaccessible_normalized_filename(path):
1487
1461
    __doc__ = _accessible_normalized_filename.__doc__
1488
1462
 
1489
 
    if isinstance(path, bytes):
1490
 
        path = path.decode(sys.getfilesystemencoding())
1491
 
    normalized = unicodedata.normalize('NFC', path)
 
1463
    normalized = unicodedata.normalize('NFC', text_type(path))
1492
1464
    return normalized, normalized == path
1493
1465
 
1494
1466
 
1517
1489
    except AttributeError:
1518
1490
        # siginterrupt doesn't exist on this platform, or for this version
1519
1491
        # of Python.
1520
 
        def siginterrupt(signum, flag): return None
 
1492
        siginterrupt = lambda signum, flag: None
1521
1493
    if restart_syscall:
1522
1494
        def sig_handler(*args):
1523
1495
            # Python resets the siginterrupt flag when a signal is
1548
1520
_terminal_size_state = 'no_data'
1549
1521
_first_terminal_size = None
1550
1522
 
1551
 
 
1552
1523
def terminal_width():
1553
1524
    """Return terminal width.
1554
1525
 
1635
1606
 
1636
1607
 
1637
1608
def _win32_terminal_size(width, height):
1638
 
    width, height = win32utils.get_console_size(
1639
 
        defaultx=width, defaulty=height)
 
1609
    width, height = win32utils.get_console_size(defaultx=width, defaulty=height)
1640
1610
    return width, height
1641
1611
 
1642
1612
 
1643
1613
def _ioctl_terminal_size(width, height):
1644
1614
    try:
1645
 
        import struct
1646
 
        import fcntl
1647
 
        import termios
 
1615
        import struct, fcntl, termios
1648
1616
        s = struct.pack('HHHH', 0, 0, 0, 0)
1649
1617
        x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
1650
1618
        height, width = struct.unpack('HHHH', x)[0:2]
1652
1620
        pass
1653
1621
    return width, height
1654
1622
 
1655
 
 
1656
1623
_terminal_size = None
1657
1624
"""Returns the terminal size as (width, height).
1658
1625
 
1668
1635
    _terminal_size = _ioctl_terminal_size
1669
1636
 
1670
1637
 
1671
 
def supports_executable(path):
1672
 
    """Return if filesystem at path supports executable bit.
1673
 
 
1674
 
    :param path: Path for which to check the file system
1675
 
    :return: boolean indicating whether executable bit can be stored/relied upon
1676
 
    """
1677
 
    if sys.platform == 'win32':
1678
 
        return False
1679
 
    try:
1680
 
        fs_type = get_fs_type(path)
1681
 
    except errors.DependencyNotPresent as e:
1682
 
        trace.mutter('Unable to get fs type for %r: %s', path, e)
1683
 
    else:
1684
 
        if fs_type in ('vfat', 'ntfs'):
1685
 
            # filesystems known to not support executable bit
1686
 
            return False
1687
 
    return True
1688
 
 
1689
 
 
1690
 
def supports_symlinks(path):
1691
 
    """Return if the filesystem at path supports the creation of symbolic links.
1692
 
 
1693
 
    """
1694
 
    if not has_symlinks():
1695
 
        return False
1696
 
    try:
1697
 
        fs_type = get_fs_type(path)
1698
 
    except errors.DependencyNotPresent as e:
1699
 
        trace.mutter('Unable to get fs type for %r: %s', path, e)
1700
 
    else:
1701
 
        if fs_type in ('vfat', 'ntfs'):
1702
 
            # filesystems known to not support symlinks
1703
 
            return False
1704
 
    return True
 
1638
def supports_executable():
 
1639
    return sys.platform != "win32"
1705
1640
 
1706
1641
 
1707
1642
def supports_posix_readonly():
1750
1685
        raise errors.IllegalPath(path)
1751
1686
 
1752
1687
 
1753
 
_WIN32_ERROR_DIRECTORY = 267  # Similar to errno.ENOTDIR
1754
 
 
 
1688
_WIN32_ERROR_DIRECTORY = 267 # Similar to errno.ENOTDIR
1755
1689
 
1756
1690
def _is_error_enotdir(e):
1757
1691
    """Check if this exception represents ENOTDIR.
1769
1703
    :return: True if this represents an ENOTDIR error. False otherwise.
1770
1704
    """
1771
1705
    en = getattr(e, 'errno', None)
1772
 
    if (en == errno.ENOTDIR or
1773
 
        (sys.platform == 'win32' and
1774
 
            (en == _WIN32_ERROR_DIRECTORY or
1775
 
             (en == errno.EINVAL
1776
 
              and getattr(e, 'winerror', None) == _WIN32_ERROR_DIRECTORY)
1777
 
             ))):
 
1706
    if (en == errno.ENOTDIR
 
1707
        or (sys.platform == 'win32'
 
1708
            and (en == _WIN32_ERROR_DIRECTORY
 
1709
                 or (en == errno.EINVAL
 
1710
                     and getattr(e, 'winerror', None) == _WIN32_ERROR_DIRECTORY)
 
1711
        ))):
1778
1712
        return True
1779
1713
    return False
1780
1714
 
1807
1741
        rooted higher up.
1808
1742
    :return: an iterator over the dirs.
1809
1743
    """
1810
 
    # TODO there is a bit of a smell where the results of the directory-
 
1744
    #TODO there is a bit of a smell where the results of the directory-
1811
1745
    # summary in this, and the path from the root, may not agree
1812
1746
    # depending on top and prefix - i.e. ./foo and foo as a pair leads to
1813
1747
    # potentially confusing output. We should make this more robust - but
1950
1884
        See DirReader.read_dir for details.
1951
1885
        """
1952
1886
        _utf8_encode = self._utf8_encode
1953
 
 
1954
 
        def _fs_decode(s): return s.decode(_fs_enc)
1955
 
 
1956
 
        def _fs_encode(s): return s.encode(_fs_enc)
1957
1887
        _lstat = os.lstat
1958
1888
        _listdir = os.listdir
1959
1889
        _kind_from_mode = file_kind_from_stat_mode
1966
1896
 
1967
1897
        dirblock = []
1968
1898
        append = dirblock.append
1969
 
        for name_native in _listdir(top.encode('utf-8')):
 
1899
        for name in sorted(_listdir(top)):
1970
1900
            try:
1971
 
                name = _fs_decode(name_native)
 
1901
                name_utf8 = _utf8_encode(name)[0]
1972
1902
            except UnicodeDecodeError:
1973
1903
                raise errors.BadFilenameEncoding(
1974
 
                    relprefix + name_native, _fs_enc)
1975
 
            name_utf8 = _utf8_encode(name)[0]
 
1904
                    _utf8_encode(relprefix)[0] + name, _fs_enc)
1976
1905
            abspath = top_slash + name
1977
1906
            statvalue = _lstat(abspath)
1978
1907
            kind = _kind_from_mode(statvalue.st_mode)
1979
1908
            append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
1980
 
        return sorted(dirblock)
 
1909
        return dirblock
1981
1910
 
1982
1911
 
1983
1912
def copy_tree(from_path, to_path, handlers={}):
2011
1940
    real_handlers = {'file': shutil.copy2,
2012
1941
                     'symlink': copy_link,
2013
1942
                     'directory': copy_dir,
2014
 
                     }
 
1943
                    }
2015
1944
    real_handlers.update(handlers)
2016
1945
 
2017
1946
    if not os.path.exists(to_path):
2032
1961
    if chown is None:
2033
1962
        return
2034
1963
 
2035
 
    if src is None:
 
1964
    if src == None:
2036
1965
        src = os.path.dirname(dst)
2037
1966
        if src == '':
2038
1967
            src = '.'
2040
1969
    try:
2041
1970
        s = os.stat(src)
2042
1971
        chown(dst, s.st_uid, s.st_gid)
2043
 
    except OSError:
 
1972
    except OSError as e:
2044
1973
        trace.warning(
2045
1974
            'Unable to copy ownership from "%s" to "%s". '
2046
1975
            'You may want to set it manually.', src, dst)
2059
1988
    """Compare path_a and path_b to generate the same order walkdirs uses."""
2060
1989
    key_a = path_prefix_key(path_a)
2061
1990
    key_b = path_prefix_key(path_b)
2062
 
    return (key_a > key_b) - (key_a < key_b)
 
1991
    return cmp(key_a, key_b)
2063
1992
 
2064
1993
 
2065
1994
_cached_user_encoding = None
2096
2025
                             ' unknown encoding %s.'
2097
2026
                             ' Continuing with ascii encoding.\n'
2098
2027
                             % user_encoding
2099
 
                             )
 
2028
                            )
2100
2029
        user_encoding = 'ascii'
2101
2030
    else:
2102
2031
        # Get 'ascii' when setlocale has not been called or LANG=C or unset.
2146
2075
 
2147
2076
 
2148
2077
def read_bytes_from_socket(sock, report_activity=None,
2149
 
                           max_read_size=MAX_SOCKET_CHUNK):
 
2078
        max_read_size=MAX_SOCKET_CHUNK):
2150
2079
    """Read up to max_read_size of bytes from sock and notify of progress.
2151
2080
 
2152
2081
    Translates "Connection reset by peer" into file-like EOF (return an
2155
2084
    """
2156
2085
    while True:
2157
2086
        try:
2158
 
            data = sock.recv(max_read_size)
 
2087
            bytes = sock.recv(max_read_size)
2159
2088
        except socket.error as e:
2160
2089
            eno = e.args[0]
2161
2090
            if eno in _end_of_stream_errors:
2162
2091
                # The connection was closed by the other side.  Callers expect
2163
2092
                # an empty string to signal end-of-stream.
2164
 
                return b""
 
2093
                return ""
2165
2094
            elif eno == errno.EINTR:
2166
2095
                # Retry the interrupted recv.
2167
2096
                continue
2168
2097
            raise
2169
2098
        else:
2170
2099
            if report_activity is not None:
2171
 
                report_activity(len(data), 'read')
2172
 
            return data
 
2100
                report_activity(len(bytes), 'read')
 
2101
            return bytes
2173
2102
 
2174
2103
 
2175
2104
def recv_all(socket, count):
2182
2111
 
2183
2112
    This isn't optimized and is intended mostly for use in testing.
2184
2113
    """
2185
 
    b = b''
 
2114
    b = ''
2186
2115
    while len(b) < count:
2187
2116
        new = read_bytes_from_socket(socket, None, count - len(b))
2188
 
        if new == b'':
2189
 
            break  # eof
 
2117
        if new == '':
 
2118
            break # eof
2190
2119
        b += new
2191
2120
    return b
2192
2121
 
2209
2138
    view = memoryview(bytes)
2210
2139
    while sent_total < byte_count:
2211
2140
        try:
2212
 
            sent = sock.send(view[sent_total:sent_total + MAX_SOCKET_CHUNK])
 
2141
            sent = sock.send(view[sent_total:sent_total+MAX_SOCKET_CHUNK])
2213
2142
        except (socket.error, IOError) as e:
2214
2143
            if e.args[0] in _end_of_stream_errors:
2215
2144
                raise errors.ConnectionReset(
2241
2170
            sock.connect(sa)
2242
2171
            return sock
2243
2172
 
2244
 
        except socket.error as e:
2245
 
            err = e
 
2173
        except socket.error as err:
2246
2174
            # 'err' is now the most recent error
2247
2175
            if sock is not None:
2248
2176
                sock.close()
2293
2221
    base = dirname(breezy.__file__)
2294
2222
    if getattr(sys, 'frozen', None):    # bzr.exe
2295
2223
        base = abspath(pathjoin(base, '..', '..'))
2296
 
    with open(pathjoin(base, resource_relpath), "rt") as f:
 
2224
    f = file(pathjoin(base, resource_relpath), "rU")
 
2225
    try:
2297
2226
        return f.read()
2298
 
 
 
2227
    finally:
 
2228
        f.close()
2299
2229
 
2300
2230
def file_kind_from_stat_mode_thunk(mode):
2301
2231
    global file_kind_from_stat_mode
2303
2233
        try:
2304
2234
            from ._readdir_pyx import UTF8DirReader
2305
2235
            file_kind_from_stat_mode = UTF8DirReader().kind_from_mode
2306
 
        except ImportError:
 
2236
        except ImportError as e:
2307
2237
            # This is one time where we won't warn that an extension failed to
2308
2238
            # load. The extension is never available on Windows anyway.
2309
2239
            from ._readdir_py import (
2310
2240
                _kind_from_mode as file_kind_from_stat_mode
2311
2241
                )
2312
2242
    return file_kind_from_stat_mode(mode)
2313
 
 
2314
 
 
2315
2243
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
2316
2244
 
2317
 
 
2318
2245
def file_stat(f, _lstat=os.lstat):
2319
2246
    try:
2320
2247
        # XXX cache?
2324
2251
            raise errors.NoSuchFile(f)
2325
2252
        raise
2326
2253
 
2327
 
 
2328
2254
def file_kind(f, _lstat=os.lstat):
2329
2255
    stat_value = file_stat(f, _lstat)
2330
2256
    return file_kind_from_stat_mode(stat_value.st_mode)
2331
2257
 
2332
 
 
2333
2258
def until_no_eintr(f, *a, **kw):
2334
2259
    """Run f(*a, **kw), retrying if an EINTR error occurs.
2335
2260
 
2386
2311
                                stdout=subprocess.PIPE).communicate()[0]
2387
2312
elif sys.platform == 'sunos5':
2388
2313
    def _local_concurrency():
2389
 
        return subprocess.Popen(['psrinfo', '-p', ],
 
2314
        return subprocess.Popen(['psrinfo', '-p',],
2390
2315
                                stdout=subprocess.PIPE).communicate()[0]
2391
2316
elif sys.platform == "win32":
2392
2317
    def _local_concurrency():
2400
2325
 
2401
2326
_cached_local_concurrency = None
2402
2327
 
2403
 
 
2404
2328
def local_concurrency(use_cache=True):
2405
2329
    """Return how many processes can be run concurrently.
2406
2330
 
2428
2352
    except (TypeError, ValueError):
2429
2353
        concurrency = 1
2430
2354
    if use_cache:
2431
 
        _cached_local_concurrency = concurrency
 
2355
        _cached_concurrency = concurrency
2432
2356
    return concurrency
2433
2357
 
2434
2358
 
2446
2370
            data, _ = self.encode(object, self.errors)
2447
2371
            self.stream.write(data)
2448
2372
 
2449
 
 
2450
2373
if sys.platform == 'win32':
2451
2374
    def open_file(filename, mode='r', bufsize=-1):
2452
2375
        """This function is used to override the ``open`` builtin.
2480
2403
            else:
2481
2404
                flags |= os.O_WRONLY
2482
2405
            flags |= os.O_CREAT | os.O_APPEND
2483
 
        else:  # reading
 
2406
        else: #reading
2484
2407
            if updating:
2485
2408
                flags |= os.O_RDWR
2486
2409
            else:
2526
2449
 
2527
2450
def find_executable_on_path(name):
2528
2451
    """Finds an executable on the PATH.
2529
 
 
 
2452
    
2530
2453
    On Windows, this will try to append each extension in the PATHEXT
2531
2454
    environment variable to the name, if it cannot be found with the name
2532
2455
    as given.
2533
 
 
 
2456
    
2534
2457
    :param name: The base name of the executable.
2535
2458
    :return: The path to the executable found or None.
2536
2459
    """
2574
2497
            # exists, though not ours
2575
2498
            return False
2576
2499
        else:
2577
 
            trace.mutter("os.kill(%d, 0) failed: %s" % (pid, e))
 
2500
            mutter("os.kill(%d, 0) failed: %s" % (pid, e))
2578
2501
            # Don't really know.
2579
2502
            return False
2580
2503
    else:
2581
2504
        # Exists and our process: not dead.
2582
2505
        return False
2583
2506
 
2584
 
 
2585
2507
if sys.platform == "win32":
2586
2508
    is_local_pid_dead = win32utils.is_local_pid_dead
2587
2509
else:
2594
2516
 
2595
2517
def fdatasync(fileno):
2596
2518
    """Flush file contents to disk if possible.
2597
 
 
 
2519
    
2598
2520
    :param fileno: Integer OS file handle.
2599
2521
    :raises TransportNotPossible: If flushing to disk is not possible.
2600
2522
    """
2614
2536
 
2615
2537
def ensure_empty_directory_exists(path, exception_class):
2616
2538
    """Make sure a local directory exists and is empty.
2617
 
 
 
2539
    
2618
2540
    If it does not exist, it is created.  If it exists and is not empty, an
2619
2541
    instance of exception_class is raised.
2620
2542
    """
2638
2560
    if sys.platform == "win32" and win32utils._is_pywintypes_error(evalue):
2639
2561
        return True
2640
2562
    return False
2641
 
 
2642
 
 
2643
 
def read_mtab(path):
2644
 
    """Read an fstab-style file and extract mountpoint+filesystem information.
2645
 
 
2646
 
    :param path: Path to read from
2647
 
    :yield: Tuples with mountpoints (as bytestrings) and filesystem names
2648
 
    """
2649
 
    with open(path, 'rb') as f:
2650
 
        for line in f:
2651
 
            if line.startswith(b'#'):
2652
 
                continue
2653
 
            cols = line.split()
2654
 
            if len(cols) < 3:
2655
 
                continue
2656
 
            yield cols[1], cols[2].decode('ascii', 'replace')
2657
 
 
2658
 
 
2659
 
MTAB_PATH = '/etc/mtab'
2660
 
 
2661
 
class FilesystemFinder(object):
2662
 
    """Find the filesystem for a particular path."""
2663
 
 
2664
 
    def __init__(self, mountpoints):
2665
 
        def key(x):
2666
 
            return len(x[0])
2667
 
        self._mountpoints = sorted(mountpoints, key=key, reverse=True)
2668
 
 
2669
 
    @classmethod
2670
 
    def from_mtab(cls):
2671
 
        """Create a FilesystemFinder from an mtab-style file.
2672
 
 
2673
 
        Note that this will silenty ignore mtab if it doesn't exist or can not
2674
 
        be opened.
2675
 
        """
2676
 
        # TODO(jelmer): Use inotify to be notified when /etc/mtab changes and
2677
 
        # we need to re-read it.
2678
 
        try:
2679
 
            return cls(read_mtab(MTAB_PATH))
2680
 
        except EnvironmentError as e:
2681
 
            trace.mutter('Unable to read mtab: %s', e)
2682
 
            return cls([])
2683
 
 
2684
 
    def find(self, path):
2685
 
        """Find the filesystem used by a particular path.
2686
 
 
2687
 
        :param path: Path to find (bytestring or text type)
2688
 
        :return: Filesystem name (as text type) or None, if the filesystem is
2689
 
            unknown.
2690
 
        """
2691
 
        for mountpoint, filesystem in self._mountpoints:
2692
 
            if is_inside(mountpoint, path):
2693
 
                return filesystem
2694
 
        return None
2695
 
 
2696
 
 
2697
 
_FILESYSTEM_FINDER = None
2698
 
 
2699
 
 
2700
 
def get_fs_type(path):
2701
 
    """Return the filesystem type for the partition a path is in.
2702
 
 
2703
 
    :param path: Path to search filesystem type for
2704
 
    :return: A FS type, as string. E.g. "ext2"
2705
 
    """
2706
 
    global _FILESYSTEM_FINDER
2707
 
    if _FILESYSTEM_FINDER is None:
2708
 
        _FILESYSTEM_FINDER = FilesystemFinder.from_mtab()
2709
 
 
2710
 
    if not isinstance(path, bytes):
2711
 
        path = path.encode(_fs_enc)
2712
 
 
2713
 
    return _FILESYSTEM_FINDER.find(path)
2714
 
 
2715
 
 
2716
 
if PY3:
2717
 
    perf_counter = time.perf_counter
2718
 
else:
2719
 
    perf_counter = time.clock