~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Martin Pool
  • Date: 2010-02-27 01:34:49 UTC
  • mto: This revision was merged to the branch mainline in revision 5064.
  • Revision ID: mbp@canonical.com-20100227013449-zxostilwfoendxfv
Handle "Directory not empty" from ftp as DirectoryNotEmpty.

FtpTransport._translate_ftp_error can handle all ftp errors; there's no clear
distinction between 'temporary' and 'permament'.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2009 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
21
21
                  S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
22
22
import sys
23
23
import time
 
24
import codecs
 
25
import warnings
24
26
 
25
27
from bzrlib.lazy_import import lazy_import
26
28
lazy_import(globals(), """
27
 
import codecs
28
29
from datetime import datetime
29
30
import errno
30
31
from ntpath import (abspath as _nt_abspath,
38
39
from shutil import (
39
40
    rmtree,
40
41
    )
 
42
import signal
41
43
import subprocess
42
44
import tempfile
43
45
from tempfile import (
70
72
from bzrlib import symbol_versioning
71
73
 
72
74
 
 
75
# Cross platform wall-clock time functionality with decent resolution.
 
76
# On Linux ``time.clock`` returns only CPU time. On Windows, ``time.time()``
 
77
# only has a resolution of ~15ms. Note that ``time.clock()`` is not
 
78
# synchronized with ``time.time()``, this is only meant to be used to find
 
79
# delta times by subtracting from another call to this function.
 
80
timer_func = time.time
 
81
if sys.platform == 'win32':
 
82
    timer_func = time.clock
 
83
 
73
84
# On win32, O_BINARY is used to indicate the file should
74
85
# be opened in binary mode, rather than text mode.
75
86
# On other platforms, O_BINARY doesn't exist, because
168
179
    try:
169
180
        return _kind_marker_map[kind]
170
181
    except KeyError:
171
 
        raise errors.BzrError('invalid file kind %r' % kind)
 
182
        # Slightly faster than using .get(, '') when the common case is that
 
183
        # kind will be found
 
184
        return ''
172
185
 
173
186
 
174
187
lexists = getattr(os.path, 'lexists', None)
191
204
    :param old: The old path, to rename from
192
205
    :param new: The new path, to rename to
193
206
    :param rename_func: The potentially non-atomic rename function
194
 
    :param unlink_func: A way to delete the target file if the full rename succeeds
 
207
    :param unlink_func: A way to delete the target file if the full rename
 
208
        succeeds
195
209
    """
196
 
 
197
210
    # sftp rename doesn't allow overwriting, so play tricks:
198
211
    base = os.path.basename(new)
199
212
    dirname = os.path.dirname(new)
200
 
    tmp_name = u'tmp.%s.%.9f.%d.%s' % (base, time.time(), os.getpid(), rand_chars(10))
 
213
    # callers use different encodings for the paths so the following MUST
 
214
    # respect that. We rely on python upcasting to unicode if new is unicode
 
215
    # and keeping a str if not.
 
216
    tmp_name = 'tmp.%s.%.9f.%d.%s' % (base, time.time(),
 
217
                                      os.getpid(), rand_chars(10))
201
218
    tmp_name = pathjoin(dirname, tmp_name)
202
219
 
203
220
    # Rename the file out of the way, but keep track if it didn't exist
223
240
    else:
224
241
        file_existed = True
225
242
 
 
243
    failure_exc = None
226
244
    success = False
227
245
    try:
228
246
        try:
234
252
            # source and target may be aliases of each other (e.g. on a
235
253
            # case-insensitive filesystem), so we may have accidentally renamed
236
254
            # source by when we tried to rename target
237
 
            if not (file_existed and e.errno in (None, errno.ENOENT)):
238
 
                raise
 
255
            failure_exc = sys.exc_info()
 
256
            if (file_existed and e.errno in (None, errno.ENOENT)
 
257
                and old.lower() == new.lower()):
 
258
                # source and target are the same file on a case-insensitive
 
259
                # filesystem, so we don't generate an exception
 
260
                failure_exc = None
239
261
    finally:
240
262
        if file_existed:
241
263
            # If the file used to exist, rename it back into place
244
266
                unlink_func(tmp_name)
245
267
            else:
246
268
                rename_func(tmp_name, new)
 
269
    if failure_exc is not None:
 
270
        raise failure_exc[0], failure_exc[1], failure_exc[2]
247
271
 
248
272
 
249
273
# In Python 2.4.2 and older, os.path.abspath and os.path.realpath
687
711
    return offset.days * 86400 + offset.seconds
688
712
 
689
713
weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
 
714
_default_format_by_weekday_num = [wd + " %Y-%m-%d %H:%M:%S" for wd in weekdays]
 
715
 
690
716
 
691
717
def format_date(t, offset=0, timezone='original', date_fmt=None,
692
718
                show_offset=True):
706
732
    date_str = time.strftime(date_fmt, tt)
707
733
    return date_str + offset_str
708
734
 
 
735
 
 
736
# Cache of formatted offset strings
 
737
_offset_cache = {}
 
738
 
 
739
 
 
740
def format_date_with_offset_in_original_timezone(t, offset=0,
 
741
    _cache=_offset_cache):
 
742
    """Return a formatted date string in the original timezone.
 
743
 
 
744
    This routine may be faster then format_date.
 
745
 
 
746
    :param t: Seconds since the epoch.
 
747
    :param offset: Timezone offset in seconds east of utc.
 
748
    """
 
749
    if offset is None:
 
750
        offset = 0
 
751
    tt = time.gmtime(t + offset)
 
752
    date_fmt = _default_format_by_weekday_num[tt[6]]
 
753
    date_str = time.strftime(date_fmt, tt)
 
754
    offset_str = _cache.get(offset, None)
 
755
    if offset_str is None:
 
756
        offset_str = ' %+03d%02d' % (offset / 3600, (offset / 60) % 60)
 
757
        _cache[offset] = offset_str
 
758
    return date_str + offset_str
 
759
 
 
760
 
709
761
def format_local_date(t, offset=0, timezone='original', date_fmt=None,
710
762
                      show_offset=True):
711
763
    """Return an unicode date string formatted according to the current locale.
725
777
        date_str = date_str.decode(get_user_encoding(), 'replace')
726
778
    return date_str + offset_str
727
779
 
 
780
 
728
781
def _format_date(t, offset, timezone, date_fmt, show_offset):
729
782
    if timezone == 'utc':
730
783
        tt = time.gmtime(t)
881
934
    return parents
882
935
 
883
936
 
 
937
_extension_load_failures = []
 
938
 
 
939
 
 
940
def failed_to_load_extension(exception):
 
941
    """Handle failing to load a binary extension.
 
942
 
 
943
    This should be called from the ImportError block guarding the attempt to
 
944
    import the native extension.  If this function returns, the pure-Python
 
945
    implementation should be loaded instead::
 
946
 
 
947
    >>> try:
 
948
    >>>     import bzrlib._fictional_extension_pyx
 
949
    >>> except ImportError, e:
 
950
    >>>     bzrlib.osutils.failed_to_load_extension(e)
 
951
    >>>     import bzrlib._fictional_extension_py
 
952
    """
 
953
    # NB: This docstring is just an example, not a doctest, because doctest
 
954
    # currently can't cope with the use of lazy imports in this namespace --
 
955
    # mbp 20090729
 
956
    
 
957
    # This currently doesn't report the failure at the time it occurs, because
 
958
    # they tend to happen very early in startup when we can't check config
 
959
    # files etc, and also we want to report all failures but not spam the user
 
960
    # with 10 warnings.
 
961
    from bzrlib import trace
 
962
    exception_str = str(exception)
 
963
    if exception_str not in _extension_load_failures:
 
964
        trace.mutter("failed to load compiled extension: %s" % exception_str)
 
965
        _extension_load_failures.append(exception_str)
 
966
 
 
967
 
 
968
def report_extension_load_failures():
 
969
    if not _extension_load_failures:
 
970
        return
 
971
    from bzrlib.config import GlobalConfig
 
972
    if GlobalConfig().get_user_option_as_bool('ignore_missing_extensions'):
 
973
        return
 
974
    # the warnings framework should by default show this only once
 
975
    from bzrlib.trace import warning
 
976
    warning(
 
977
        "bzr: warning: some compiled extensions could not be loaded; "
 
978
        "see <https://answers.launchpad.net/bzr/+faq/703>")
 
979
    # we no longer show the specific missing extensions here, because it makes
 
980
    # the message too long and scary - see
 
981
    # https://bugs.launchpad.net/bzr/+bug/430529
 
982
 
 
983
 
884
984
try:
885
985
    from bzrlib._chunks_to_lines_pyx import chunks_to_lines
886
 
except ImportError:
 
986
except ImportError, e:
 
987
    failed_to_load_extension(e)
887
988
    from bzrlib._chunks_to_lines_py import chunks_to_lines
888
989
 
889
990
 
1040
1141
 
1041
1142
    s = []
1042
1143
    head = rp
1043
 
    while len(head) >= len(base):
 
1144
    while True:
 
1145
        if len(head) <= len(base) and head != base:
 
1146
            raise errors.PathNotChild(rp, base)
1044
1147
        if head == base:
1045
1148
            break
1046
 
        head, tail = os.path.split(head)
 
1149
        head, tail = split(head)
1047
1150
        if tail:
1048
 
            s.insert(0, tail)
1049
 
    else:
1050
 
        raise errors.PathNotChild(rp, base)
 
1151
            s.append(tail)
1051
1152
 
1052
1153
    if s:
1053
 
        return pathjoin(*s)
 
1154
        return pathjoin(*reversed(s))
1054
1155
    else:
1055
1156
        return ''
1056
1157
 
1083
1184
    bit_iter = iter(rel.split('/'))
1084
1185
    for bit in bit_iter:
1085
1186
        lbit = bit.lower()
1086
 
        for look in _listdir(current):
 
1187
        try:
 
1188
            next_entries = _listdir(current)
 
1189
        except OSError: # enoent, eperm, etc
 
1190
            # We can't find this in the filesystem, so just append the
 
1191
            # remaining bits.
 
1192
            current = pathjoin(current, bit, *list(bit_iter))
 
1193
            break
 
1194
        for look in next_entries:
1087
1195
            if lbit == look.lower():
1088
1196
                current = pathjoin(current, look)
1089
1197
                break
1093
1201
            # the target of a move, for example).
1094
1202
            current = pathjoin(current, bit, *list(bit_iter))
1095
1203
            break
1096
 
    return current[len(abs_base)+1:]
 
1204
    return current[len(abs_base):].lstrip('/')
1097
1205
 
1098
1206
# XXX - TODO - we need better detection/integration of case-insensitive
1099
1207
# file-systems; Linux often sees FAT32 devices (or NFS-mounted OSX
1238
1346
    normalized_filename = _inaccessible_normalized_filename
1239
1347
 
1240
1348
 
 
1349
default_terminal_width = 80
 
1350
"""The default terminal width for ttys.
 
1351
 
 
1352
This is defined so that higher levels can share a common fallback value when
 
1353
terminal_width() returns None.
 
1354
"""
 
1355
 
 
1356
 
1241
1357
def terminal_width():
1242
 
    """Return estimated terminal width."""
1243
 
    if sys.platform == 'win32':
1244
 
        return win32utils.get_console_size()[0]
1245
 
    width = 0
 
1358
    """Return terminal width.
 
1359
 
 
1360
    None is returned if the width can't established precisely.
 
1361
 
 
1362
    The rules are:
 
1363
    - if BZR_COLUMNS is set, returns its value
 
1364
    - if there is no controlling terminal, returns None
 
1365
    - if COLUMNS is set, returns its value,
 
1366
 
 
1367
    From there, we need to query the OS to get the size of the controlling
 
1368
    terminal.
 
1369
 
 
1370
    Unices:
 
1371
    - get termios.TIOCGWINSZ
 
1372
    - if an error occurs or a negative value is obtained, returns None
 
1373
 
 
1374
    Windows:
 
1375
    
 
1376
    - win32utils.get_console_size() decides,
 
1377
    - returns None on error (provided default value)
 
1378
    """
 
1379
 
 
1380
    # If BZR_COLUMNS is set, take it, user is always right
 
1381
    try:
 
1382
        return int(os.environ['BZR_COLUMNS'])
 
1383
    except (KeyError, ValueError):
 
1384
        pass
 
1385
 
 
1386
    isatty = getattr(sys.stdout, 'isatty', None)
 
1387
    if  isatty is None or not isatty():
 
1388
        # Don't guess, setting BZR_COLUMNS is the recommended way to override.
 
1389
        return None
 
1390
 
 
1391
    # If COLUMNS is set, take it, the terminal knows better (even inside a
 
1392
    # given terminal, the application can decide to set COLUMNS to a lower
 
1393
    # value (splitted screen) or a bigger value (scroll bars))
 
1394
    try:
 
1395
        return int(os.environ['COLUMNS'])
 
1396
    except (KeyError, ValueError):
 
1397
        pass
 
1398
 
 
1399
    width, height = _terminal_size(None, None)
 
1400
    if width <= 0:
 
1401
        # Consider invalid values as meaning no width
 
1402
        return None
 
1403
 
 
1404
    return width
 
1405
 
 
1406
 
 
1407
def _win32_terminal_size(width, height):
 
1408
    width, height = win32utils.get_console_size(defaultx=width, defaulty=height)
 
1409
    return width, height
 
1410
 
 
1411
 
 
1412
def _ioctl_terminal_size(width, height):
1246
1413
    try:
1247
1414
        import struct, fcntl, termios
1248
1415
        s = struct.pack('HHHH', 0, 0, 0, 0)
1249
1416
        x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
1250
 
        width = struct.unpack('HHHH', x)[1]
1251
 
    except IOError:
 
1417
        height, width = struct.unpack('HHHH', x)[0:2]
 
1418
    except (IOError, AttributeError):
1252
1419
        pass
1253
 
    if width <= 0:
1254
 
        try:
1255
 
            width = int(os.environ['COLUMNS'])
1256
 
        except:
 
1420
    return width, height
 
1421
 
 
1422
_terminal_size = None
 
1423
"""Returns the terminal size as (width, height).
 
1424
 
 
1425
:param width: Default value for width.
 
1426
:param height: Default value for height.
 
1427
 
 
1428
This is defined specifically for each OS and query the size of the controlling
 
1429
terminal. If any error occurs, the provided default values should be returned.
 
1430
"""
 
1431
if sys.platform == 'win32':
 
1432
    _terminal_size = _win32_terminal_size
 
1433
else:
 
1434
    _terminal_size = _ioctl_terminal_size
 
1435
 
 
1436
 
 
1437
def _terminal_size_changed(signum, frame):
 
1438
    """Set COLUMNS upon receiving a SIGnal for WINdow size CHange."""
 
1439
    width, height = _terminal_size(None, None)
 
1440
    if width is not None:
 
1441
        os.environ['COLUMNS'] = str(width)
 
1442
 
 
1443
 
 
1444
_registered_sigwinch = False
 
1445
 
 
1446
def watch_sigwinch():
 
1447
    """Register for SIGWINCH, once and only once."""
 
1448
    global _registered_sigwinch
 
1449
    if not _registered_sigwinch:
 
1450
        if sys.platform == 'win32':
 
1451
            # Martin (gz) mentioned WINDOW_BUFFER_SIZE_RECORD from
 
1452
            # ReadConsoleInput but I've no idea how to plug that in
 
1453
            # the current design -- vila 20091216
1257
1454
            pass
1258
 
    if width <= 0:
1259
 
        width = 80
1260
 
 
1261
 
    return width
 
1455
        else:
 
1456
            signal.signal(signal.SIGWINCH, _terminal_size_changed)
 
1457
        _registered_sigwinch = True
1262
1458
 
1263
1459
 
1264
1460
def supports_executable():
1466
1662
            try:
1467
1663
                from bzrlib._readdir_pyx import UTF8DirReader
1468
1664
                _selected_dir_reader = UTF8DirReader()
1469
 
            except ImportError:
 
1665
            except ImportError, e:
 
1666
                failed_to_load_extension(e)
1470
1667
                pass
1471
1668
 
1472
1669
    if _selected_dir_reader is None:
1778
1975
        try:
1779
1976
            from bzrlib._readdir_pyx import UTF8DirReader
1780
1977
            file_kind_from_stat_mode = UTF8DirReader().kind_from_mode
1781
 
        except ImportError:
 
1978
        except ImportError, e:
 
1979
            # This is one time where we won't warn that an extension failed to
 
1980
            # load. The extension is never available on Windows anyway.
1782
1981
            from bzrlib._readdir_py import (
1783
1982
                _kind_from_mode as file_kind_from_stat_mode
1784
1983
                )
1886
2085
    anything goes wrong.
1887
2086
    """
1888
2087
    global _cached_local_concurrency
 
2088
 
1889
2089
    if _cached_local_concurrency is not None and use_cache:
1890
2090
        return _cached_local_concurrency
1891
2091
 
1892
 
    try:
1893
 
        concurrency = _local_concurrency()
1894
 
    except (OSError, IOError):
1895
 
        concurrency = None
 
2092
    concurrency = os.environ.get('BZR_CONCURRENCY', None)
 
2093
    if concurrency is None:
 
2094
        try:
 
2095
            concurrency = _local_concurrency()
 
2096
        except (OSError, IOError):
 
2097
            pass
1896
2098
    try:
1897
2099
        concurrency = int(concurrency)
1898
2100
    except (TypeError, ValueError):
1900
2102
    if use_cache:
1901
2103
        _cached_concurrency = concurrency
1902
2104
    return concurrency
 
2105
 
 
2106
 
 
2107
class UnicodeOrBytesToBytesWriter(codecs.StreamWriter):
 
2108
    """A stream writer that doesn't decode str arguments."""
 
2109
 
 
2110
    def __init__(self, encode, stream, errors='strict'):
 
2111
        codecs.StreamWriter.__init__(self, stream, errors)
 
2112
        self.encode = encode
 
2113
 
 
2114
    def write(self, object):
 
2115
        if type(object) is str:
 
2116
            self.stream.write(object)
 
2117
        else:
 
2118
            data, _ = self.encode(object, self.errors)
 
2119
            self.stream.write(data)