~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Vincent Ladeuil
  • Date: 2007-11-04 15:29:17 UTC
  • mfrom: (2961 +trunk)
  • mto: (2961.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 2962.
  • Revision ID: v.ladeuil+lp@free.fr-20071104152917-nrsumxpk3dikso2c
Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
53
53
    )
54
54
""")
55
55
 
56
 
 
57
56
import bzrlib
58
57
from bzrlib import symbol_versioning
59
58
from bzrlib.symbol_versioning import (
149
148
    try:
150
149
        return _mapper(_lstat(f).st_mode)
151
150
    except OSError, e:
152
 
        if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
 
151
        if getattr(e, 'errno', None) == errno.ENOENT:
153
152
            raise errors.NoSuchFile(f)
154
153
        raise
155
154
 
234
233
 
235
234
    success = False
236
235
    try:
237
 
        try:
238
 
            # This may throw an exception, in which case success will
239
 
            # not be set.
240
 
            rename_func(old, new)
241
 
            success = True
242
 
        except (IOError, OSError), e:
243
 
            # source and target may be aliases of each other (e.g. on a
244
 
            # case-insensitive filesystem), so we may have accidentally renamed
245
 
            # source by when we tried to rename target
246
 
            if not (file_existed and e.errno in (None, errno.ENOENT)):
247
 
                raise
 
236
        # This may throw an exception, in which case success will
 
237
        # not be set.
 
238
        rename_func(old, new)
 
239
        success = True
248
240
    finally:
249
241
        if file_existed:
250
242
            # If the file used to exist, rename it back into place
360
352
 
361
353
 
362
354
def _mac_getcwd():
363
 
    return unicodedata.normalize('NFC', os.getcwdu())
 
355
    return unicodedata.normalize('NFKC', os.getcwdu())
364
356
 
365
357
 
366
358
# Default is to just use the python builtins, but these can be rebound on
468
460
        return pathjoin(F(p), e)
469
461
 
470
462
 
 
463
def backup_file(fn):
 
464
    """Copy a file to a backup.
 
465
 
 
466
    Backups are named in GNU-style, with a ~ suffix.
 
467
 
 
468
    If the file is already a backup, it's not copied.
 
469
    """
 
470
    if fn[-1] == '~':
 
471
        return
 
472
    bfn = fn + '~'
 
473
 
 
474
    if has_symlinks() and os.path.islink(fn):
 
475
        target = os.readlink(fn)
 
476
        os.symlink(target, bfn)
 
477
        return
 
478
    inf = file(fn, 'rb')
 
479
    try:
 
480
        content = inf.read()
 
481
    finally:
 
482
        inf.close()
 
483
    
 
484
    outf = file(bfn, 'wb')
 
485
    try:
 
486
        outf.write(content)
 
487
    finally:
 
488
        outf.close()
 
489
 
 
490
 
471
491
def isdir(f):
472
492
    """True if f is an accessible directory."""
473
493
    try:
530
550
    return False
531
551
 
532
552
 
533
 
def pumpfile(from_file, to_file, read_length=-1, buff_size=32768):
 
553
def pumpfile(fromfile, tofile):
534
554
    """Copy contents of one file to another.
535
 
 
536
 
    The read_length can either be -1 to read to end-of-file (EOF) or
537
 
    it can specify the maximum number of bytes to read.
538
 
 
539
 
    The buff_size represents the maximum size for each read operation
540
 
    performed on from_file.
541
 
 
 
555
    
542
556
    :return: The number of bytes copied.
543
557
    """
 
558
    BUFSIZE = 32768
544
559
    length = 0
545
 
    if read_length >= 0:
546
 
        # read specified number of bytes
547
 
 
548
 
        while read_length > 0:
549
 
            num_bytes_to_read = min(read_length, buff_size)
550
 
 
551
 
            block = from_file.read(num_bytes_to_read)
552
 
            if not block:
553
 
                # EOF reached
554
 
                break
555
 
            to_file.write(block)
556
 
 
557
 
            actual_bytes_read = len(block)
558
 
            read_length -= actual_bytes_read
559
 
            length += actual_bytes_read
560
 
    else:
561
 
        # read to EOF
562
 
        while True:
563
 
            block = from_file.read(buff_size)
564
 
            if not block:
565
 
                # EOF reached
566
 
                break
567
 
            to_file.write(block)
568
 
            length += len(block)
 
560
    while True:
 
561
        b = fromfile.read(BUFSIZE)
 
562
        if not b:
 
563
            break
 
564
        tofile.write(b)
 
565
        length += len(b)
569
566
    return length
570
567
 
571
568
 
572
 
def pump_string_file(bytes, file_handle, segment_size=None):
573
 
    """Write bytes to file_handle in many smaller writes.
574
 
 
575
 
    :param bytes: The string to write.
576
 
    :param file_handle: The file to write to.
577
 
    """
578
 
    # Write data in chunks rather than all at once, because very large
579
 
    # writes fail on some platforms (e.g. Windows with SMB  mounted
580
 
    # drives).
581
 
    if not segment_size:
582
 
        segment_size = 5242880 # 5MB
583
 
    segments = range(len(bytes) / segment_size + 1)
584
 
    write = file_handle.write
585
 
    for segment_index in segments:
586
 
        segment = buffer(bytes, segment_index * segment_size, segment_size)
587
 
        write(segment)
588
 
 
589
 
 
590
569
def file_iterator(input_file, readsize=32768):
591
570
    while True:
592
571
        b = input_file.read(readsize)
596
575
 
597
576
 
598
577
def sha_file(f):
599
 
    """Calculate the hexdigest of an open file.
600
 
 
601
 
    The file cursor should be already at the start.
602
 
    """
 
578
    if getattr(f, 'tell', None) is not None:
 
579
        assert f.tell() == 0
603
580
    s = sha.new()
604
581
    BUFSIZE = 128<<10
605
582
    while True:
660
637
    offset = datetime.fromtimestamp(t) - datetime.utcfromtimestamp(t)
661
638
    return offset.days * 86400 + offset.seconds
662
639
 
663
 
weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
664
640
    
665
641
def format_date(t, offset=0, timezone='original', date_fmt=None,
666
642
                show_offset=True):
685
661
        tt = time.localtime(t)
686
662
        offset = local_time_offset(t)
687
663
    else:
688
 
        raise errors.UnsupportedTimezoneFormat(timezone)
 
664
        raise errors.BzrError("unsupported timezone format %r" % timezone,
 
665
                              ['options are "utc", "original", "local"'])
689
666
    if date_fmt is None:
690
667
        date_fmt = "%a %Y-%m-%d %H:%M:%S"
691
668
    if show_offset:
692
669
        offset_str = ' %+03d%02d' % (offset / 3600, (offset / 60) % 60)
693
670
    else:
694
671
        offset_str = ''
695
 
    # day of week depends on locale, so we do this ourself
696
 
    date_fmt = date_fmt.replace('%a', weekdays[tt[6]])
697
672
    return (time.strftime(date_fmt, tt) +  offset_str)
698
673
 
699
674
 
796
771
 
797
772
def splitpath(p):
798
773
    """Turn string into list of parts."""
 
774
    assert isinstance(p, basestring)
 
775
 
799
776
    # split on either delimiter because people might use either on
800
777
    # Windows
801
778
    ps = re.split(r'[\\/]', p)
811
788
    return rps
812
789
 
813
790
def joinpath(p):
 
791
    assert isinstance(p, (list, tuple))
814
792
    for f in p:
815
793
        if (f == '..') or (f is None) or (f == ''):
816
794
            raise errors.BzrError("sorry, %r not allowed in path" % f)
863
841
        return False
864
842
 
865
843
 
866
 
def has_hardlinks():
867
 
    if getattr(os, 'link', None) is not None:
868
 
        return True
869
 
    else:
870
 
        return False
871
 
 
872
 
 
873
 
def host_os_dereferences_symlinks():
874
 
    return (has_symlinks()
875
 
            and sys.platform not in ('cygwin', 'win32'))
876
 
 
877
 
 
878
844
def contains_whitespace(s):
879
845
    """True if there are any whitespace characters in s."""
880
846
    # string.whitespace can include '\xa0' in certain locales, because it is
915
881
    avoids that problem.
916
882
    """
917
883
 
918
 
    if len(base) < MIN_ABS_PATHLENGTH:
919
 
        # must have space for e.g. a drive letter
920
 
        raise ValueError('%r is too short to calculate a relative path'
921
 
            % (base,))
 
884
    assert len(base) >= MIN_ABS_PATHLENGTH, ('Length of base must be equal or'
 
885
        ' exceed the platform minimum length (which is %d)' % 
 
886
        MIN_ABS_PATHLENGTH)
922
887
 
923
888
    rp = abspath(path)
924
889
 
1041
1006
    On platforms where the system does not normalize filenames 
1042
1007
    (Windows, Linux), you have to access a file by its exact path.
1043
1008
 
1044
 
    Internally, bzr only supports NFC normalization, since that is 
 
1009
    Internally, bzr only supports NFC/NFKC normalization, since that is 
1045
1010
    the standard for XML documents.
1046
1011
 
1047
1012
    So return the normalized path, and a flag indicating if the file
1048
1013
    can be accessed by that path.
1049
1014
    """
1050
1015
 
1051
 
    return unicodedata.normalize('NFC', unicode(path)), True
 
1016
    return unicodedata.normalize('NFKC', unicode(path)), True
1052
1017
 
1053
1018
 
1054
1019
def _inaccessible_normalized_filename(path):
1055
1020
    __doc__ = _accessible_normalized_filename.__doc__
1056
1021
 
1057
 
    normalized = unicodedata.normalize('NFC', unicode(path))
 
1022
    normalized = unicodedata.normalize('NFKC', unicode(path))
1058
1023
    return normalized, normalized == path
1059
1024
 
1060
1025
 
1137
1102
        raise errors.IllegalPath(path)
1138
1103
 
1139
1104
 
1140
 
_WIN32_ERROR_DIRECTORY = 267 # Similar to errno.ENOTDIR
1141
 
 
1142
 
def _is_error_enotdir(e):
1143
 
    """Check if this exception represents ENOTDIR.
1144
 
 
1145
 
    Unfortunately, python is very inconsistent about the exception
1146
 
    here. The cases are:
1147
 
      1) Linux, Mac OSX all versions seem to set errno == ENOTDIR
1148
 
      2) Windows, Python2.4, uses errno == ERROR_DIRECTORY (267)
1149
 
         which is the windows error code.
1150
 
      3) Windows, Python2.5 uses errno == EINVAL and
1151
 
         winerror == ERROR_DIRECTORY
1152
 
 
1153
 
    :param e: An Exception object (expected to be OSError with an errno
1154
 
        attribute, but we should be able to cope with anything)
1155
 
    :return: True if this represents an ENOTDIR error. False otherwise.
1156
 
    """
1157
 
    en = getattr(e, 'errno', None)
1158
 
    if (en == errno.ENOTDIR
1159
 
        or (sys.platform == 'win32'
1160
 
            and (en == _WIN32_ERROR_DIRECTORY
1161
 
                 or (en == errno.EINVAL
1162
 
                     and getattr(e, 'winerror', None) == _WIN32_ERROR_DIRECTORY)
1163
 
        ))):
1164
 
        return True
1165
 
    return False
1166
 
 
1167
 
 
1168
1105
def walkdirs(top, prefix=""):
1169
1106
    """Yield data about all the directories in a tree.
1170
1107
    
1214
1151
 
1215
1152
        dirblock = []
1216
1153
        append = dirblock.append
1217
 
        try:
1218
 
            names = sorted(_listdir(top))
1219
 
        except OSError, e:
1220
 
            if not _is_error_enotdir(e):
1221
 
                raise
1222
 
        else:
1223
 
            for name in names:
1224
 
                abspath = top_slash + name
1225
 
                statvalue = _lstat(abspath)
1226
 
                kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1227
 
                append((relprefix + name, name, kind, statvalue, abspath))
 
1154
        for name in sorted(_listdir(top)):
 
1155
            abspath = top_slash + name
 
1156
            statvalue = _lstat(abspath)
 
1157
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1158
            append((relprefix + name, name, kind, statvalue, abspath))
1228
1159
        yield (relroot, top), dirblock
1229
1160
 
1230
1161
        # push the user specified dirs from dirblock
1231
1162
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1232
1163
 
1233
1164
 
1234
 
_real_walkdirs_utf8 = None
1235
 
 
1236
1165
def _walkdirs_utf8(top, prefix=""):
1237
1166
    """Yield data about all the directories in a tree.
1238
1167
 
1247
1176
        path-from-top might be unicode or utf8, but it is the correct path to
1248
1177
        pass to os functions to affect the file in question. (such as os.lstat)
1249
1178
    """
1250
 
    global _real_walkdirs_utf8
1251
 
    if _real_walkdirs_utf8 is None:
1252
 
        fs_encoding = _fs_enc.upper()
1253
 
        if win32utils.winver == 'Windows NT':
1254
 
            # Win98 doesn't have unicode apis like FindFirstFileW
1255
 
            # TODO: We possibly could support Win98 by falling back to the
1256
 
            #       original FindFirstFile, and using TCHAR instead of WCHAR,
1257
 
            #       but that gets a bit tricky, and requires custom compiling
1258
 
            #       for win98 anyway.
1259
 
            try:
1260
 
                from bzrlib._walkdirs_win32 import _walkdirs_utf8_win32_find_file
1261
 
            except ImportError:
1262
 
                _real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
1263
 
            else:
1264
 
                _real_walkdirs_utf8 = _walkdirs_utf8_win32_find_file
1265
 
        elif fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968'):
1266
 
            # ANSI_X3.4-1968 is a form of ASCII
1267
 
            _real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
1268
 
        else:
1269
 
            _real_walkdirs_utf8 = _walkdirs_fs_utf8
1270
 
    return _real_walkdirs_utf8(top, prefix=prefix)
 
1179
    fs_encoding = _fs_enc.upper()
 
1180
    if (sys.platform == 'win32' or
 
1181
        fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968')): # ascii
 
1182
        return _walkdirs_unicode_to_utf8(top, prefix=prefix)
 
1183
    else:
 
1184
        return _walkdirs_fs_utf8(top, prefix=prefix)
1271
1185
 
1272
1186
 
1273
1187
def _walkdirs_fs_utf8(top, prefix=""):
1278
1192
    """
1279
1193
    _lstat = os.lstat
1280
1194
    _directory = _directory_kind
1281
 
    # Use C accelerated directory listing.
1282
 
    _listdir = _read_dir
 
1195
    _listdir = os.listdir
1283
1196
    _kind_from_mode = _formats.get
1284
1197
 
1285
1198
    # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1295
1208
 
1296
1209
        dirblock = []
1297
1210
        append = dirblock.append
1298
 
        # read_dir supplies in should-stat order.
1299
 
        for _, name in sorted(_listdir(top)):
 
1211
        for name in sorted(_listdir(top)):
1300
1212
            abspath = top_slash + name
1301
1213
            statvalue = _lstat(abspath)
1302
1214
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1303
1215
            append((relprefix + name, name, kind, statvalue, abspath))
1304
 
        dirblock.sort()
1305
1216
        yield (relroot, top), dirblock
1306
1217
 
1307
1218
        # push the user specified dirs from dirblock
1448
1359
    # Windows returns 'cp0' to indicate there is no code page. So we'll just
1449
1360
    # treat that as ASCII, and not support printing unicode characters to the
1450
1361
    # console.
1451
 
    #
1452
 
    # For python scripts run under vim, we get '', so also treat that as ASCII
1453
 
    if user_encoding in (None, 'cp0', ''):
 
1362
    if user_encoding in (None, 'cp0'):
1454
1363
        user_encoding = 'ascii'
1455
1364
    else:
1456
1365
        # check encoding
1470
1379
    return user_encoding
1471
1380
 
1472
1381
 
1473
 
def get_host_name():
1474
 
    """Return the current unicode host name.
1475
 
 
1476
 
    This is meant to be used in place of socket.gethostname() because that
1477
 
    behaves inconsistently on different platforms.
1478
 
    """
1479
 
    if sys.platform == "win32":
1480
 
        import win32utils
1481
 
        return win32utils.get_host_name()
1482
 
    else:
1483
 
        import socket
1484
 
        return socket.gethostname().decode(get_user_encoding())
1485
 
 
1486
 
 
1487
1382
def recv_all(socket, bytes):
1488
1383
    """Receive an exact number of bytes.
1489
1384
 
1502
1397
        b += new
1503
1398
    return b
1504
1399
 
1505
 
 
1506
 
def send_all(socket, bytes):
1507
 
    """Send all bytes on a socket.
1508
 
 
1509
 
    Regular socket.sendall() can give socket error 10053 on Windows.  This
1510
 
    implementation sends no more than 64k at a time, which avoids this problem.
1511
 
    """
1512
 
    chunk_size = 2**16
1513
 
    for pos in xrange(0, len(bytes), chunk_size):
1514
 
        socket.sendall(bytes[pos:pos+chunk_size])
1515
 
 
1516
 
 
1517
1400
def dereference_path(path):
1518
1401
    """Determine the real path to a file.
1519
1402
 
1531
1414
def supports_mapi():
1532
1415
    """Return True if we can use MAPI to launch a mail client."""
1533
1416
    return sys.platform == "win32"
1534
 
 
1535
 
 
1536
 
def resource_string(package, resource_name):
1537
 
    """Load a resource from a package and return it as a string.
1538
 
 
1539
 
    Note: Only packages that start with bzrlib are currently supported.
1540
 
 
1541
 
    This is designed to be a lightweight implementation of resource
1542
 
    loading in a way which is API compatible with the same API from
1543
 
    pkg_resources. See
1544
 
    http://peak.telecommunity.com/DevCenter/PkgResources#basic-resource-access.
1545
 
    If and when pkg_resources becomes a standard library, this routine
1546
 
    can delegate to it.
1547
 
    """
1548
 
    # Check package name is within bzrlib
1549
 
    if package == "bzrlib":
1550
 
        resource_relpath = resource_name
1551
 
    elif package.startswith("bzrlib."):
1552
 
        package = package[len("bzrlib."):].replace('.', os.sep)
1553
 
        resource_relpath = pathjoin(package, resource_name)
1554
 
    else:
1555
 
        raise errors.BzrError('resource package %s not in bzrlib' % package)
1556
 
 
1557
 
    # Map the resource to a file and read its contents
1558
 
    base = dirname(bzrlib.__file__)
1559
 
    if getattr(sys, 'frozen', None):    # bzr.exe
1560
 
        base = abspath(pathjoin(base, '..', '..'))
1561
 
    filename = pathjoin(base, resource_relpath)
1562
 
    return open(filename, 'rU').read()
1563
 
 
1564
 
 
1565
 
try:
1566
 
    from bzrlib._readdir_pyx import read_dir as _read_dir
1567
 
except ImportError:
1568
 
    from bzrlib._readdir_py import read_dir as _read_dir