/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: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2020-07-28 02:47:10 UTC
  • mfrom: (7519.1.1 merge-3.1)
  • Revision ID: breezy.the.bot@gmail.com-20200728024710-a2ylds219f1lsl62
Merge lp:brz/3.1.

Merged from https://code.launchpad.net/~jelmer/brz/merge-3.1/+merge/388173

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from __future__ import absolute_import
18
 
 
19
17
import errno
20
18
import os
21
19
import re
52
50
from breezy.i18n import gettext
53
51
""")
54
52
 
55
 
from .sixish import (
56
 
    PY3,
57
 
    text_type,
58
 
    )
59
 
 
60
53
from hashlib import (
61
54
    md5,
62
55
    sha1 as sha,
90
83
        self.timezone = timezone
91
84
 
92
85
 
93
 
def get_unicode_argv():
94
 
    if PY3:
95
 
        return sys.argv[1:]
96
 
    try:
97
 
        user_encoding = get_user_encoding()
98
 
        return [a.decode(user_encoding) for a in sys.argv[1:]]
99
 
    except UnicodeDecodeError:
100
 
        raise errors.BzrError(gettext("Parameter {0!r} encoding is unsupported by {1} "
101
 
                                      "application locale.").format(a, user_encoding))
102
 
 
103
 
 
104
86
def make_readonly(filename):
105
87
    """Make a filename read-only."""
106
88
    mod = os.lstat(filename).st_mode
325
307
    return path
326
308
 
327
309
 
328
 
def _posix_path_from_environ(key):
329
 
    """Get unicode path from `key` in environment or None if not present
330
 
 
331
 
    Note that posix systems use arbitrary byte strings for filesystem objects,
332
 
    so a path that raises BadFilenameEncoding here may still be accessible.
333
 
    """
334
 
    val = os.environ.get(key, None)
335
 
    if PY3 or val is None:
336
 
        return val
337
 
    try:
338
 
        return val.decode(_fs_enc)
339
 
    except UnicodeDecodeError:
340
 
        # GZ 2011-12-12:Ideally want to include `key` in the exception message
341
 
        raise errors.BadFilenameEncoding(val, _fs_enc)
342
 
 
343
 
 
344
310
def _posix_get_home_dir():
345
311
    """Get the home directory of the current user as a unicode path"""
346
312
    path = posixpath.expanduser("~")
354
320
 
355
321
def _posix_getuser_unicode():
356
322
    """Get username from environment or password database as unicode"""
357
 
    name = getpass.getuser()
358
 
    if PY3:
359
 
        return name
360
 
    user_encoding = get_user_encoding()
361
 
    try:
362
 
        return name.decode(user_encoding)
363
 
    except UnicodeDecodeError:
364
 
        raise errors.BzrError("Encoding of username %r is unsupported by %s "
365
 
                              "application locale." % (name, user_encoding))
 
323
    return getpass.getuser()
366
324
 
367
325
 
368
326
def _win32_fixdrive(path):
447
405
    return _rename_wrapper
448
406
 
449
407
 
450
 
if sys.version_info > (3,):
451
 
    _getcwd = os.getcwd
452
 
else:
453
 
    _getcwd = os.getcwdu
 
408
_getcwd = os.getcwd
454
409
 
455
410
 
456
411
# Default rename wraps os.rename()
462
417
realpath = _posix_realpath
463
418
pathjoin = os.path.join
464
419
normpath = _posix_normpath
465
 
path_from_environ = _posix_path_from_environ
466
420
_get_home_dir = _posix_get_home_dir
467
421
getuser_unicode = _posix_getuser_unicode
468
422
getcwd = _getcwd
520
474
        """Replacer for shutil.rmtree: could remove readonly dirs/files"""
521
475
        return shutil.rmtree(path, ignore_errors, onerror)
522
476
 
523
 
    get_unicode_argv = getattr(win32utils, 'get_unicode_argv', get_unicode_argv)
524
 
    path_from_environ = win32utils.get_environ_unicode
525
477
    _get_home_dir = win32utils.get_home_location
526
478
    getuser_unicode = win32utils.get_user_name
527
479
 
738
690
 
739
691
# GZ 2017-09-16: Makes sense in general for hexdigest() result to be text, but
740
692
# used as bytes through most interfaces so encode with this wrapper.
741
 
if PY3:
742
 
    def _hexdigest(hashobj):
743
 
        return hashobj.hexdigest().encode()
744
 
else:
745
 
    def _hexdigest(hashobj):
746
 
        return hashobj.hexdigest()
 
693
def _hexdigest(hashobj):
 
694
    return hashobj.hexdigest().encode()
747
695
 
748
696
 
749
697
def sha_file(f):
895
843
    (date_fmt, tt, offset_str) = \
896
844
        _format_date(t, offset, timezone, date_fmt, show_offset)
897
845
    date_str = time.strftime(date_fmt, tt)
898
 
    if not isinstance(date_str, text_type):
 
846
    if not isinstance(date_str, str):
899
847
        date_str = date_str.decode(get_user_encoding(), 'replace')
900
848
    return date_str + offset_str
901
849
 
1012
960
    """
1013
961
    s = ''
1014
962
    for raw_byte in rand_bytes(num):
1015
 
        if not PY3:
1016
 
            s += ALNUM[ord(raw_byte) % 36]
1017
 
        else:
1018
 
            s += ALNUM[raw_byte % 36]
 
963
        s += ALNUM[raw_byte % 36]
1019
964
    return s
1020
965
 
1021
966
 
1322
1267
 
1323
1268
    abs_base = abspath(base)
1324
1269
    current = abs_base
1325
 
    _listdir = os.listdir
1326
1270
 
1327
1271
    # use an explicit iterator so we can easily consume the rest on early exit.
1328
1272
    bit_iter = iter(rel.split('/'))
1329
1273
    for bit in bit_iter:
1330
1274
        lbit = bit.lower()
1331
1275
        try:
1332
 
            next_entries = _listdir(current)
 
1276
            next_entries = scandir(current)
1333
1277
        except OSError:  # enoent, eperm, etc
1334
1278
            # We can't find this in the filesystem, so just append the
1335
1279
            # remaining bits.
1336
1280
            current = pathjoin(current, bit, *list(bit_iter))
1337
1281
            break
1338
 
        for look in next_entries:
1339
 
            if lbit == look.lower():
1340
 
                current = pathjoin(current, look)
 
1282
        for entry in next_entries:
 
1283
            if lbit == entry.name.lower():
 
1284
                current = entry.path
1341
1285
                break
1342
1286
        else:
1343
1287
            # got to the end, nothing matched, so we just return the
1376
1320
    Otherwise it is decoded from the the filesystem's encoding. If decoding
1377
1321
    fails, a errors.BadFilenameEncoding exception is raised.
1378
1322
    """
1379
 
    if isinstance(filename, text_type):
 
1323
    if isinstance(filename, str):
1380
1324
        return filename
1381
1325
    try:
1382
1326
        return filename.decode(_fs_enc)
1391
1335
    Otherwise it is decoded from utf-8. If decoding fails, the exception is
1392
1336
    wrapped in a BzrBadParameterNotUnicode exception.
1393
1337
    """
1394
 
    if isinstance(unicode_or_utf8_string, text_type):
 
1338
    if isinstance(unicode_or_utf8_string, str):
1395
1339
        return unicode_or_utf8_string
1396
1340
    try:
1397
1341
        return unicode_or_utf8_string.decode('utf8')
1418
1362
    return unicode_or_utf8_string.encode('utf-8')
1419
1363
 
1420
1364
 
1421
 
def safe_revision_id(unicode_or_utf8_string):
1422
 
    """Revision ids should now be utf8, but at one point they were unicode.
1423
 
 
1424
 
    :param unicode_or_utf8_string: A possibly Unicode revision_id. (can also be
1425
 
        utf8 or None).
1426
 
    :return: None or a utf8 revision id.
1427
 
    """
1428
 
    if (unicode_or_utf8_string is None
1429
 
            or unicode_or_utf8_string.__class__ == bytes):
1430
 
        return unicode_or_utf8_string
1431
 
    raise TypeError('Unicode revision ids are no longer supported. '
1432
 
                    'Revision id generators should be creating utf8 revision '
1433
 
                    'ids.')
1434
 
 
1435
 
 
1436
 
def safe_file_id(unicode_or_utf8_string):
1437
 
    """File ids should now be utf8, but at one point they were unicode.
1438
 
 
1439
 
    This is the same as safe_utf8, except it uses the cached encode functions
1440
 
    to save a little bit of performance.
1441
 
 
1442
 
    :param unicode_or_utf8_string: A possibly Unicode file_id. (can also be
1443
 
        utf8 or None).
1444
 
    :return: None or a utf8 file id.
1445
 
    """
1446
 
    if (unicode_or_utf8_string is None
1447
 
            or unicode_or_utf8_string.__class__ == bytes):
1448
 
        return unicode_or_utf8_string
1449
 
    raise TypeError('Unicode file ids are no longer supported. '
1450
 
                    'File id generators should be creating utf8 file ids.')
1451
 
 
1452
 
 
1453
1365
_platform_normalizes_filenames = False
1454
1366
if sys.platform == 'darwin':
1455
1367
    _platform_normalizes_filenames = True
1730
1642
        if orig_val is not None:
1731
1643
            del os.environ[env_variable]
1732
1644
    else:
1733
 
        if not PY3 and isinstance(value, text_type):
1734
 
            value = value.encode(get_user_encoding())
1735
1645
        os.environ[env_variable] = value
1736
1646
    return orig_val
1737
1647
 
1753
1663
_WIN32_ERROR_DIRECTORY = 267  # Similar to errno.ENOTDIR
1754
1664
 
1755
1665
 
 
1666
try:
 
1667
    scandir = os.scandir
 
1668
except AttributeError:  # Python < 3
 
1669
    lazy_import(globals(), """\
 
1670
from scandir import scandir
 
1671
""")
 
1672
 
 
1673
 
1756
1674
def _is_error_enotdir(e):
1757
1675
    """Check if this exception represents ENOTDIR.
1758
1676
 
1812
1730
    # depending on top and prefix - i.e. ./foo and foo as a pair leads to
1813
1731
    # potentially confusing output. We should make this more robust - but
1814
1732
    # not at a speed cost. RBC 20060731
1815
 
    _lstat = os.lstat
1816
1733
    _directory = _directory_kind
1817
 
    _listdir = os.listdir
1818
 
    _kind_from_mode = file_kind_from_stat_mode
1819
1734
    pending = [(safe_unicode(prefix), "", _directory, None, safe_unicode(top))]
1820
1735
    while pending:
1821
1736
        # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1827
1742
        top_slash = top + u'/'
1828
1743
 
1829
1744
        dirblock = []
1830
 
        append = dirblock.append
1831
1745
        try:
1832
 
            names = sorted(map(decode_filename, _listdir(top)))
 
1746
            for entry in scandir(top):
 
1747
                name = decode_filename(entry.name)
 
1748
                statvalue = entry.stat(follow_symlinks=False)
 
1749
                kind = file_kind_from_stat_mode(statvalue.st_mode)
 
1750
                dirblock.append((relprefix + name, name, kind, statvalue, entry.path))
1833
1751
        except OSError as e:
1834
1752
            if not _is_error_enotdir(e):
1835
1753
                raise
1836
 
        else:
1837
 
            for name in names:
1838
 
                abspath = top_slash + name
1839
 
                statvalue = _lstat(abspath)
1840
 
                kind = _kind_from_mode(statvalue.st_mode)
1841
 
                append((relprefix + name, name, kind, statvalue, abspath))
 
1754
        except UnicodeDecodeError as e:
 
1755
            raise errors.BadFilenameEncoding(e.object, _fs_enc)
 
1756
        dirblock.sort()
1842
1757
        yield (relroot, top), dirblock
1843
1758
 
1844
1759
        # push the user specified dirs from dirblock
1954
1869
        def _fs_decode(s): return s.decode(_fs_enc)
1955
1870
 
1956
1871
        def _fs_encode(s): return s.encode(_fs_enc)
1957
 
        _lstat = os.lstat
1958
 
        _listdir = os.listdir
1959
 
        _kind_from_mode = file_kind_from_stat_mode
1960
1872
 
1961
1873
        if prefix:
1962
1874
            relprefix = prefix + b'/'
1966
1878
 
1967
1879
        dirblock = []
1968
1880
        append = dirblock.append
1969
 
        for name_native in _listdir(top.encode('utf-8')):
 
1881
        for entry in scandir(safe_utf8(top)):
1970
1882
            try:
1971
 
                name = _fs_decode(name_native)
 
1883
                name = _fs_decode(entry.name)
1972
1884
            except UnicodeDecodeError:
1973
1885
                raise errors.BadFilenameEncoding(
1974
 
                    relprefix + name_native, _fs_enc)
 
1886
                    relprefix + entry.name, _fs_enc)
 
1887
            abspath = top_slash + name
1975
1888
            name_utf8 = _utf8_encode(name)[0]
1976
 
            abspath = top_slash + name
1977
 
            statvalue = _lstat(abspath)
1978
 
            kind = _kind_from_mode(statvalue.st_mode)
 
1889
            statvalue = entry.stat(follow_symlinks=False)
 
1890
            kind = file_kind_from_stat_mode(statvalue.st_mode)
1979
1891
            append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
1980
1892
        return sorted(dirblock)
1981
1893
 
2126
2038
        return win32utils.get_host_name()
2127
2039
    else:
2128
2040
        import socket
2129
 
        if PY3:
2130
 
            return socket.gethostname()
2131
 
        return socket.gethostname().decode(get_user_encoding())
 
2041
        return socket.gethostname()
2132
2042
 
2133
2043
 
2134
2044
# We must not read/write any more than 64k at a time from/to a socket so we
2627
2537
            raise exception_class(path)
2628
2538
 
2629
2539
 
2630
 
def is_environment_error(evalue):
2631
 
    """True if exception instance is due to a process environment issue
2632
 
 
2633
 
    This includes OSError and IOError, but also other errors that come from
2634
 
    the operating system or core libraries but are not subclasses of those.
2635
 
    """
2636
 
    if isinstance(evalue, (EnvironmentError, select.error)):
2637
 
        return True
2638
 
    if sys.platform == "win32" and win32utils._is_pywintypes_error(evalue):
2639
 
        return True
2640
 
    return False
2641
 
 
2642
 
 
2643
2540
def read_mtab(path):
2644
2541
    """Read an fstab-style file and extract mountpoint+filesystem information.
2645
2542
 
2713
2610
    return _FILESYSTEM_FINDER.find(path)
2714
2611
 
2715
2612
 
2716
 
if PY3:
2717
 
    perf_counter = time.perf_counter
2718
 
else:
2719
 
    perf_counter = time.clock
 
2613
perf_counter = time.perf_counter