~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Vincent Ladeuil
  • Date: 2010-02-09 20:33:43 UTC
  • mto: (5029.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5030.
  • Revision ID: v.ladeuil+lp@free.fr-20100209203343-ktxx7t0xvptvjnt1
Move TestingPathFilteringServer to bzrlib.tests.test_server

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
                  S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
22
22
import sys
23
23
import time
 
24
import warnings
24
25
 
25
26
from bzrlib.lazy_import import lazy_import
26
27
lazy_import(globals(), """
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
191
202
    :param old: The old path, to rename from
192
203
    :param new: The new path, to rename to
193
204
    :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
 
205
    :param unlink_func: A way to delete the target file if the full rename
 
206
        succeeds
195
207
    """
196
 
 
197
208
    # sftp rename doesn't allow overwriting, so play tricks:
198
209
    base = os.path.basename(new)
199
210
    dirname = os.path.dirname(new)
200
 
    tmp_name = u'tmp.%s.%.9f.%d.%s' % (base, time.time(), os.getpid(), rand_chars(10))
 
211
    # callers use different encodings for the paths so the following MUST
 
212
    # respect that. We rely on python upcasting to unicode if new is unicode
 
213
    # and keeping a str if not.
 
214
    tmp_name = 'tmp.%s.%.9f.%d.%s' % (base, time.time(),
 
215
                                      os.getpid(), rand_chars(10))
201
216
    tmp_name = pathjoin(dirname, tmp_name)
202
217
 
203
218
    # Rename the file out of the way, but keep track if it didn't exist
223
238
    else:
224
239
        file_existed = True
225
240
 
 
241
    failure_exc = None
226
242
    success = False
227
243
    try:
228
244
        try:
234
250
            # source and target may be aliases of each other (e.g. on a
235
251
            # case-insensitive filesystem), so we may have accidentally renamed
236
252
            # source by when we tried to rename target
237
 
            if not (file_existed and e.errno in (None, errno.ENOENT)):
238
 
                raise
 
253
            failure_exc = sys.exc_info()
 
254
            if (file_existed and e.errno in (None, errno.ENOENT)
 
255
                and old.lower() == new.lower()):
 
256
                # source and target are the same file on a case-insensitive
 
257
                # filesystem, so we don't generate an exception
 
258
                failure_exc = None
239
259
    finally:
240
260
        if file_existed:
241
261
            # If the file used to exist, rename it back into place
244
264
                unlink_func(tmp_name)
245
265
            else:
246
266
                rename_func(tmp_name, new)
 
267
    if failure_exc is not None:
 
268
        raise failure_exc[0], failure_exc[1], failure_exc[2]
247
269
 
248
270
 
249
271
# In Python 2.4.2 and older, os.path.abspath and os.path.realpath
687
709
    return offset.days * 86400 + offset.seconds
688
710
 
689
711
weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
 
712
_default_format_by_weekday_num = [wd + " %Y-%m-%d %H:%M:%S" for wd in weekdays]
 
713
 
690
714
 
691
715
def format_date(t, offset=0, timezone='original', date_fmt=None,
692
716
                show_offset=True):
706
730
    date_str = time.strftime(date_fmt, tt)
707
731
    return date_str + offset_str
708
732
 
 
733
 
 
734
# Cache of formatted offset strings
 
735
_offset_cache = {}
 
736
 
 
737
 
 
738
def format_date_with_offset_in_original_timezone(t, offset=0,
 
739
    _cache=_offset_cache):
 
740
    """Return a formatted date string in the original timezone.
 
741
 
 
742
    This routine may be faster then format_date.
 
743
 
 
744
    :param t: Seconds since the epoch.
 
745
    :param offset: Timezone offset in seconds east of utc.
 
746
    """
 
747
    if offset is None:
 
748
        offset = 0
 
749
    tt = time.gmtime(t + offset)
 
750
    date_fmt = _default_format_by_weekday_num[tt[6]]
 
751
    date_str = time.strftime(date_fmt, tt)
 
752
    offset_str = _cache.get(offset, None)
 
753
    if offset_str is None:
 
754
        offset_str = ' %+03d%02d' % (offset / 3600, (offset / 60) % 60)
 
755
        _cache[offset] = offset_str
 
756
    return date_str + offset_str
 
757
 
 
758
 
709
759
def format_local_date(t, offset=0, timezone='original', date_fmt=None,
710
760
                      show_offset=True):
711
761
    """Return an unicode date string formatted according to the current locale.
725
775
        date_str = date_str.decode(get_user_encoding(), 'replace')
726
776
    return date_str + offset_str
727
777
 
 
778
 
728
779
def _format_date(t, offset, timezone, date_fmt, show_offset):
729
780
    if timezone == 'utc':
730
781
        tt = time.gmtime(t)
881
932
    return parents
882
933
 
883
934
 
 
935
_extension_load_failures = []
 
936
 
 
937
 
 
938
def failed_to_load_extension(exception):
 
939
    """Handle failing to load a binary extension.
 
940
 
 
941
    This should be called from the ImportError block guarding the attempt to
 
942
    import the native extension.  If this function returns, the pure-Python
 
943
    implementation should be loaded instead::
 
944
 
 
945
    >>> try:
 
946
    >>>     import bzrlib._fictional_extension_pyx
 
947
    >>> except ImportError, e:
 
948
    >>>     bzrlib.osutils.failed_to_load_extension(e)
 
949
    >>>     import bzrlib._fictional_extension_py
 
950
    """
 
951
    # NB: This docstring is just an example, not a doctest, because doctest
 
952
    # currently can't cope with the use of lazy imports in this namespace --
 
953
    # mbp 20090729
 
954
    
 
955
    # This currently doesn't report the failure at the time it occurs, because
 
956
    # they tend to happen very early in startup when we can't check config
 
957
    # files etc, and also we want to report all failures but not spam the user
 
958
    # with 10 warnings.
 
959
    from bzrlib import trace
 
960
    exception_str = str(exception)
 
961
    if exception_str not in _extension_load_failures:
 
962
        trace.mutter("failed to load compiled extension: %s" % exception_str)
 
963
        _extension_load_failures.append(exception_str)
 
964
 
 
965
 
 
966
def report_extension_load_failures():
 
967
    if not _extension_load_failures:
 
968
        return
 
969
    from bzrlib.config import GlobalConfig
 
970
    if GlobalConfig().get_user_option_as_bool('ignore_missing_extensions'):
 
971
        return
 
972
    # the warnings framework should by default show this only once
 
973
    from bzrlib.trace import warning
 
974
    warning(
 
975
        "bzr: warning: some compiled extensions could not be loaded; "
 
976
        "see <https://answers.launchpad.net/bzr/+faq/703>")
 
977
    # we no longer show the specific missing extensions here, because it makes
 
978
    # the message too long and scary - see
 
979
    # https://bugs.launchpad.net/bzr/+bug/430529
 
980
 
 
981
 
884
982
try:
885
983
    from bzrlib._chunks_to_lines_pyx import chunks_to_lines
886
 
except ImportError:
 
984
except ImportError, e:
 
985
    failed_to_load_extension(e)
887
986
    from bzrlib._chunks_to_lines_py import chunks_to_lines
888
987
 
889
988
 
1083
1182
    bit_iter = iter(rel.split('/'))
1084
1183
    for bit in bit_iter:
1085
1184
        lbit = bit.lower()
1086
 
        for look in _listdir(current):
 
1185
        try:
 
1186
            next_entries = _listdir(current)
 
1187
        except OSError: # enoent, eperm, etc
 
1188
            # We can't find this in the filesystem, so just append the
 
1189
            # remaining bits.
 
1190
            current = pathjoin(current, bit, *list(bit_iter))
 
1191
            break
 
1192
        for look in next_entries:
1087
1193
            if lbit == look.lower():
1088
1194
                current = pathjoin(current, look)
1089
1195
                break
1093
1199
            # the target of a move, for example).
1094
1200
            current = pathjoin(current, bit, *list(bit_iter))
1095
1201
            break
1096
 
    return current[len(abs_base)+1:]
 
1202
    return current[len(abs_base):].lstrip('/')
1097
1203
 
1098
1204
# XXX - TODO - we need better detection/integration of case-insensitive
1099
1205
# file-systems; Linux often sees FAT32 devices (or NFS-mounted OSX
1238
1344
    normalized_filename = _inaccessible_normalized_filename
1239
1345
 
1240
1346
 
 
1347
default_terminal_width = 80
 
1348
"""The default terminal width for ttys.
 
1349
 
 
1350
This is defined so that higher levels can share a common fallback value when
 
1351
terminal_width() returns None.
 
1352
"""
 
1353
 
 
1354
 
1241
1355
def terminal_width():
1242
 
    """Return estimated terminal width."""
1243
 
    if sys.platform == 'win32':
1244
 
        return win32utils.get_console_size()[0]
1245
 
    width = 0
 
1356
    """Return terminal width.
 
1357
 
 
1358
    None is returned if the width can't established precisely.
 
1359
 
 
1360
    The rules are:
 
1361
    - if BZR_COLUMNS is set, returns its value
 
1362
    - if there is no controlling terminal, returns None
 
1363
    - if COLUMNS is set, returns its value,
 
1364
 
 
1365
    From there, we need to query the OS to get the size of the controlling
 
1366
    terminal.
 
1367
 
 
1368
    Unices:
 
1369
    - get termios.TIOCGWINSZ
 
1370
    - if an error occurs or a negative value is obtained, returns None
 
1371
 
 
1372
    Windows:
 
1373
    
 
1374
    - win32utils.get_console_size() decides,
 
1375
    - returns None on error (provided default value)
 
1376
    """
 
1377
 
 
1378
    # If BZR_COLUMNS is set, take it, user is always right
 
1379
    try:
 
1380
        return int(os.environ['BZR_COLUMNS'])
 
1381
    except (KeyError, ValueError):
 
1382
        pass
 
1383
 
 
1384
    isatty = getattr(sys.stdout, 'isatty', None)
 
1385
    if  isatty is None or not isatty():
 
1386
        # Don't guess, setting BZR_COLUMNS is the recommended way to override.
 
1387
        return None
 
1388
 
 
1389
    # If COLUMNS is set, take it, the terminal knows better (even inside a
 
1390
    # given terminal, the application can decide to set COLUMNS to a lower
 
1391
    # value (splitted screen) or a bigger value (scroll bars))
 
1392
    try:
 
1393
        return int(os.environ['COLUMNS'])
 
1394
    except (KeyError, ValueError):
 
1395
        pass
 
1396
 
 
1397
    width, height = _terminal_size(None, None)
 
1398
    if width <= 0:
 
1399
        # Consider invalid values as meaning no width
 
1400
        return None
 
1401
 
 
1402
    return width
 
1403
 
 
1404
 
 
1405
def _win32_terminal_size(width, height):
 
1406
    width, height = win32utils.get_console_size(defaultx=width, defaulty=height)
 
1407
    return width, height
 
1408
 
 
1409
 
 
1410
def _ioctl_terminal_size(width, height):
1246
1411
    try:
1247
1412
        import struct, fcntl, termios
1248
1413
        s = struct.pack('HHHH', 0, 0, 0, 0)
1249
1414
        x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
1250
 
        width = struct.unpack('HHHH', x)[1]
1251
 
    except IOError:
 
1415
        height, width = struct.unpack('HHHH', x)[0:2]
 
1416
    except (IOError, AttributeError):
1252
1417
        pass
1253
 
    if width <= 0:
1254
 
        try:
1255
 
            width = int(os.environ['COLUMNS'])
1256
 
        except:
1257
 
            pass
1258
 
    if width <= 0:
1259
 
        width = 80
1260
 
 
1261
 
    return width
 
1418
    return width, height
 
1419
 
 
1420
_terminal_size = None
 
1421
"""Returns the terminal size as (width, height).
 
1422
 
 
1423
:param width: Default value for width.
 
1424
:param height: Default value for height.
 
1425
 
 
1426
This is defined specifically for each OS and query the size of the controlling
 
1427
terminal. If any error occurs, the provided default values should be returned.
 
1428
"""
 
1429
if sys.platform == 'win32':
 
1430
    _terminal_size = _win32_terminal_size
 
1431
else:
 
1432
    _terminal_size = _ioctl_terminal_size
 
1433
 
 
1434
 
 
1435
def _terminal_size_changed(signum, frame):
 
1436
    """Set COLUMNS upon receiving a SIGnal for WINdow size CHange."""
 
1437
    width, height = _terminal_size(None, None)
 
1438
    if width is not None:
 
1439
        os.environ['COLUMNS'] = str(width)
 
1440
 
 
1441
if sys.platform == 'win32':
 
1442
    # Martin (gz) mentioned WINDOW_BUFFER_SIZE_RECORD from ReadConsoleInput but
 
1443
    # I've no idea how to plug that in the current design -- vila 20091216
 
1444
    pass
 
1445
else:
 
1446
    signal.signal(signal.SIGWINCH, _terminal_size_changed)
1262
1447
 
1263
1448
 
1264
1449
def supports_executable():
1466
1651
            try:
1467
1652
                from bzrlib._readdir_pyx import UTF8DirReader
1468
1653
                _selected_dir_reader = UTF8DirReader()
1469
 
            except ImportError:
 
1654
            except ImportError, e:
 
1655
                failed_to_load_extension(e)
1470
1656
                pass
1471
1657
 
1472
1658
    if _selected_dir_reader is None:
1778
1964
        try:
1779
1965
            from bzrlib._readdir_pyx import UTF8DirReader
1780
1966
            file_kind_from_stat_mode = UTF8DirReader().kind_from_mode
1781
 
        except ImportError:
 
1967
        except ImportError, e:
 
1968
            # This is one time where we won't warn that an extension failed to
 
1969
            # load. The extension is never available on Windows anyway.
1782
1970
            from bzrlib._readdir_py import (
1783
1971
                _kind_from_mode as file_kind_from_stat_mode
1784
1972
                )
1886
2074
    anything goes wrong.
1887
2075
    """
1888
2076
    global _cached_local_concurrency
 
2077
 
1889
2078
    if _cached_local_concurrency is not None and use_cache:
1890
2079
        return _cached_local_concurrency
1891
2080
 
1892
 
    try:
1893
 
        concurrency = _local_concurrency()
1894
 
    except (OSError, IOError):
1895
 
        concurrency = None
 
2081
    concurrency = os.environ.get('BZR_CONCURRENCY', None)
 
2082
    if concurrency is None:
 
2083
        try:
 
2084
            concurrency = _local_concurrency()
 
2085
        except (OSError, IOError):
 
2086
            pass
1896
2087
    try:
1897
2088
        concurrency = int(concurrency)
1898
2089
    except (TypeError, ValueError):
1900
2091
    if use_cache:
1901
2092
        _cached_concurrency = concurrency
1902
2093
    return concurrency
 
2094
 
 
2095
 
 
2096
class UnicodeOrBytesToBytesWriter(codecs.StreamWriter):
 
2097
    """A stream writer that doesn't decode str arguments."""
 
2098
 
 
2099
    def __init__(self, encode, stream, errors='strict'):
 
2100
        codecs.StreamWriter.__init__(self, stream, errors)
 
2101
        self.encode = encode
 
2102
 
 
2103
    def write(self, object):
 
2104
        if type(object) is str:
 
2105
            self.stream.write(object)
 
2106
        else:
 
2107
            data, _ = self.encode(object, self.errors)
 
2108
            self.stream.write(data)