/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

Merge test-run support.

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):
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 not dir.endswith('/'):
 
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.')
1517
1493
    except AttributeError:
1518
1494
        # siginterrupt doesn't exist on this platform, or for this version
1519
1495
        # of Python.
1520
 
        def siginterrupt(signum, flag): return None
 
1496
        siginterrupt = lambda signum, flag: None
1521
1497
    if restart_syscall:
1522
1498
        def sig_handler(*args):
1523
1499
            # Python resets the siginterrupt flag when a signal is
1548
1524
_terminal_size_state = 'no_data'
1549
1525
_first_terminal_size = None
1550
1526
 
1551
 
 
1552
1527
def terminal_width():
1553
1528
    """Return terminal width.
1554
1529
 
1635
1610
 
1636
1611
 
1637
1612
def _win32_terminal_size(width, height):
1638
 
    width, height = win32utils.get_console_size(
1639
 
        defaultx=width, defaulty=height)
 
1613
    width, height = win32utils.get_console_size(defaultx=width, defaulty=height)
1640
1614
    return width, height
1641
1615
 
1642
1616
 
1643
1617
def _ioctl_terminal_size(width, height):
1644
1618
    try:
1645
 
        import struct
1646
 
        import fcntl
1647
 
        import termios
 
1619
        import struct, fcntl, termios
1648
1620
        s = struct.pack('HHHH', 0, 0, 0, 0)
1649
1621
        x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
1650
1622
        height, width = struct.unpack('HHHH', x)[0:2]
1652
1624
        pass
1653
1625
    return width, height
1654
1626
 
1655
 
 
1656
1627
_terminal_size = None
1657
1628
"""Returns the terminal size as (width, height).
1658
1629
 
1668
1639
    _terminal_size = _ioctl_terminal_size
1669
1640
 
1670
1641
 
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
 
1642
def supports_executable():
 
1643
    return sys.platform != "win32"
1705
1644
 
1706
1645
 
1707
1646
def supports_posix_readonly():
1750
1689
        raise errors.IllegalPath(path)
1751
1690
 
1752
1691
 
1753
 
_WIN32_ERROR_DIRECTORY = 267  # Similar to errno.ENOTDIR
1754
 
 
 
1692
_WIN32_ERROR_DIRECTORY = 267 # Similar to errno.ENOTDIR
1755
1693
 
1756
1694
def _is_error_enotdir(e):
1757
1695
    """Check if this exception represents ENOTDIR.
1769
1707
    :return: True if this represents an ENOTDIR error. False otherwise.
1770
1708
    """
1771
1709
    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
 
             ))):
 
1710
    if (en == errno.ENOTDIR
 
1711
        or (sys.platform == 'win32'
 
1712
            and (en == _WIN32_ERROR_DIRECTORY
 
1713
                 or (en == errno.EINVAL
 
1714
                     and getattr(e, 'winerror', None) == _WIN32_ERROR_DIRECTORY)
 
1715
        ))):
1778
1716
        return True
1779
1717
    return False
1780
1718
 
1807
1745
        rooted higher up.
1808
1746
    :return: an iterator over the dirs.
1809
1747
    """
1810
 
    # TODO there is a bit of a smell where the results of the directory-
 
1748
    #TODO there is a bit of a smell where the results of the directory-
1811
1749
    # summary in this, and the path from the root, may not agree
1812
1750
    # depending on top and prefix - i.e. ./foo and foo as a pair leads to
1813
1751
    # potentially confusing output. We should make this more robust - but
1950
1888
        See DirReader.read_dir for details.
1951
1889
        """
1952
1890
        _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
1891
        _lstat = os.lstat
1958
1892
        _listdir = os.listdir
1959
1893
        _kind_from_mode = file_kind_from_stat_mode
1966
1900
 
1967
1901
        dirblock = []
1968
1902
        append = dirblock.append
1969
 
        for name_native in _listdir(top.encode('utf-8')):
 
1903
        for name in sorted(_listdir(top)):
1970
1904
            try:
1971
 
                name = _fs_decode(name_native)
 
1905
                name_utf8 = _utf8_encode(name)[0]
1972
1906
            except UnicodeDecodeError:
1973
1907
                raise errors.BadFilenameEncoding(
1974
 
                    relprefix + name_native, _fs_enc)
1975
 
            name_utf8 = _utf8_encode(name)[0]
 
1908
                    _utf8_encode(relprefix)[0] + name, _fs_enc)
1976
1909
            abspath = top_slash + name
1977
1910
            statvalue = _lstat(abspath)
1978
1911
            kind = _kind_from_mode(statvalue.st_mode)
1979
1912
            append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
1980
 
        return sorted(dirblock)
 
1913
        return dirblock
1981
1914
 
1982
1915
 
1983
1916
def copy_tree(from_path, to_path, handlers={}):
2011
1944
    real_handlers = {'file': shutil.copy2,
2012
1945
                     'symlink': copy_link,
2013
1946
                     'directory': copy_dir,
2014
 
                     }
 
1947
                    }
2015
1948
    real_handlers.update(handlers)
2016
1949
 
2017
1950
    if not os.path.exists(to_path):
2032
1965
    if chown is None:
2033
1966
        return
2034
1967
 
2035
 
    if src is None:
 
1968
    if src == None:
2036
1969
        src = os.path.dirname(dst)
2037
1970
        if src == '':
2038
1971
            src = '.'
2040
1973
    try:
2041
1974
        s = os.stat(src)
2042
1975
        chown(dst, s.st_uid, s.st_gid)
2043
 
    except OSError:
 
1976
    except OSError as e:
2044
1977
        trace.warning(
2045
1978
            'Unable to copy ownership from "%s" to "%s". '
2046
1979
            'You may want to set it manually.', src, dst)
2059
1992
    """Compare path_a and path_b to generate the same order walkdirs uses."""
2060
1993
    key_a = path_prefix_key(path_a)
2061
1994
    key_b = path_prefix_key(path_b)
2062
 
    return (key_a > key_b) - (key_a < key_b)
 
1995
    return cmp(key_a, key_b)
2063
1996
 
2064
1997
 
2065
1998
_cached_user_encoding = None
2096
2029
                             ' unknown encoding %s.'
2097
2030
                             ' Continuing with ascii encoding.\n'
2098
2031
                             % user_encoding
2099
 
                             )
 
2032
                            )
2100
2033
        user_encoding = 'ascii'
2101
2034
    else:
2102
2035
        # Get 'ascii' when setlocale has not been called or LANG=C or unset.
2146
2079
 
2147
2080
 
2148
2081
def read_bytes_from_socket(sock, report_activity=None,
2149
 
                           max_read_size=MAX_SOCKET_CHUNK):
 
2082
        max_read_size=MAX_SOCKET_CHUNK):
2150
2083
    """Read up to max_read_size of bytes from sock and notify of progress.
2151
2084
 
2152
2085
    Translates "Connection reset by peer" into file-like EOF (return an
2186
2119
    while len(b) < count:
2187
2120
        new = read_bytes_from_socket(socket, None, count - len(b))
2188
2121
        if new == b'':
2189
 
            break  # eof
 
2122
            break # eof
2190
2123
        b += new
2191
2124
    return b
2192
2125
 
2209
2142
    view = memoryview(bytes)
2210
2143
    while sent_total < byte_count:
2211
2144
        try:
2212
 
            sent = sock.send(view[sent_total:sent_total + MAX_SOCKET_CHUNK])
 
2145
            sent = sock.send(view[sent_total:sent_total+MAX_SOCKET_CHUNK])
2213
2146
        except (socket.error, IOError) as e:
2214
2147
            if e.args[0] in _end_of_stream_errors:
2215
2148
                raise errors.ConnectionReset(
2293
2226
    base = dirname(breezy.__file__)
2294
2227
    if getattr(sys, 'frozen', None):    # bzr.exe
2295
2228
        base = abspath(pathjoin(base, '..', '..'))
2296
 
    with open(pathjoin(base, resource_relpath), "rt") as f:
 
2229
    with open(pathjoin(base, resource_relpath), "rU") as f:
2297
2230
        return f.read()
2298
2231
 
2299
 
 
2300
2232
def file_kind_from_stat_mode_thunk(mode):
2301
2233
    global file_kind_from_stat_mode
2302
2234
    if file_kind_from_stat_mode is file_kind_from_stat_mode_thunk:
2303
2235
        try:
2304
2236
            from ._readdir_pyx import UTF8DirReader
2305
2237
            file_kind_from_stat_mode = UTF8DirReader().kind_from_mode
2306
 
        except ImportError:
 
2238
        except ImportError as e:
2307
2239
            # This is one time where we won't warn that an extension failed to
2308
2240
            # load. The extension is never available on Windows anyway.
2309
2241
            from ._readdir_py import (
2310
2242
                _kind_from_mode as file_kind_from_stat_mode
2311
2243
                )
2312
2244
    return file_kind_from_stat_mode(mode)
2313
 
 
2314
 
 
2315
2245
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
2316
2246
 
2317
 
 
2318
2247
def file_stat(f, _lstat=os.lstat):
2319
2248
    try:
2320
2249
        # XXX cache?
2324
2253
            raise errors.NoSuchFile(f)
2325
2254
        raise
2326
2255
 
2327
 
 
2328
2256
def file_kind(f, _lstat=os.lstat):
2329
2257
    stat_value = file_stat(f, _lstat)
2330
2258
    return file_kind_from_stat_mode(stat_value.st_mode)
2331
2259
 
2332
 
 
2333
2260
def until_no_eintr(f, *a, **kw):
2334
2261
    """Run f(*a, **kw), retrying if an EINTR error occurs.
2335
2262
 
2386
2313
                                stdout=subprocess.PIPE).communicate()[0]
2387
2314
elif sys.platform == 'sunos5':
2388
2315
    def _local_concurrency():
2389
 
        return subprocess.Popen(['psrinfo', '-p', ],
 
2316
        return subprocess.Popen(['psrinfo', '-p',],
2390
2317
                                stdout=subprocess.PIPE).communicate()[0]
2391
2318
elif sys.platform == "win32":
2392
2319
    def _local_concurrency():
2400
2327
 
2401
2328
_cached_local_concurrency = None
2402
2329
 
2403
 
 
2404
2330
def local_concurrency(use_cache=True):
2405
2331
    """Return how many processes can be run concurrently.
2406
2332
 
2428
2354
    except (TypeError, ValueError):
2429
2355
        concurrency = 1
2430
2356
    if use_cache:
2431
 
        _cached_local_concurrency = concurrency
 
2357
        _cached_concurrency = concurrency
2432
2358
    return concurrency
2433
2359
 
2434
2360
 
2446
2372
            data, _ = self.encode(object, self.errors)
2447
2373
            self.stream.write(data)
2448
2374
 
2449
 
 
2450
2375
if sys.platform == 'win32':
2451
2376
    def open_file(filename, mode='r', bufsize=-1):
2452
2377
        """This function is used to override the ``open`` builtin.
2480
2405
            else:
2481
2406
                flags |= os.O_WRONLY
2482
2407
            flags |= os.O_CREAT | os.O_APPEND
2483
 
        else:  # reading
 
2408
        else: #reading
2484
2409
            if updating:
2485
2410
                flags |= os.O_RDWR
2486
2411
            else:
2526
2451
 
2527
2452
def find_executable_on_path(name):
2528
2453
    """Finds an executable on the PATH.
2529
 
 
 
2454
    
2530
2455
    On Windows, this will try to append each extension in the PATHEXT
2531
2456
    environment variable to the name, if it cannot be found with the name
2532
2457
    as given.
2533
 
 
 
2458
    
2534
2459
    :param name: The base name of the executable.
2535
2460
    :return: The path to the executable found or None.
2536
2461
    """
2574
2499
            # exists, though not ours
2575
2500
            return False
2576
2501
        else:
2577
 
            trace.mutter("os.kill(%d, 0) failed: %s" % (pid, e))
 
2502
            mutter("os.kill(%d, 0) failed: %s" % (pid, e))
2578
2503
            # Don't really know.
2579
2504
            return False
2580
2505
    else:
2581
2506
        # Exists and our process: not dead.
2582
2507
        return False
2583
2508
 
2584
 
 
2585
2509
if sys.platform == "win32":
2586
2510
    is_local_pid_dead = win32utils.is_local_pid_dead
2587
2511
else:
2594
2518
 
2595
2519
def fdatasync(fileno):
2596
2520
    """Flush file contents to disk if possible.
2597
 
 
 
2521
    
2598
2522
    :param fileno: Integer OS file handle.
2599
2523
    :raises TransportNotPossible: If flushing to disk is not possible.
2600
2524
    """
2614
2538
 
2615
2539
def ensure_empty_directory_exists(path, exception_class):
2616
2540
    """Make sure a local directory exists and is empty.
2617
 
 
 
2541
    
2618
2542
    If it does not exist, it is created.  If it exists and is not empty, an
2619
2543
    instance of exception_class is raised.
2620
2544
    """
2638
2562
    if sys.platform == "win32" and win32utils._is_pywintypes_error(evalue):
2639
2563
        return True
2640
2564
    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