~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-11-17 03:20:35 UTC
  • mfrom: (4792.4.3 456036)
  • Revision ID: pqm@pqm.ubuntu.com-20091117032035-s3sgtlixj1lrminn
(Gordon Tyler) Fix IndexError during 'bzr ignore /' (#456036)

Show diffs side-by-side

added added

removed removed

Lines of Context:
39
39
from shutil import (
40
40
    rmtree,
41
41
    )
42
 
import signal
43
42
import subprocess
44
43
import tempfile
45
44
from tempfile import (
72
71
from bzrlib import symbol_versioning
73
72
 
74
73
 
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
 
 
84
74
# On win32, O_BINARY is used to indicate the file should
85
75
# be opened in binary mode, rather than text mode.
86
76
# On other platforms, O_BINARY doesn't exist, because
202
192
    :param old: The old path, to rename from
203
193
    :param new: The new path, to rename to
204
194
    :param rename_func: The potentially non-atomic rename function
205
 
    :param unlink_func: A way to delete the target file if the full rename
206
 
        succeeds
 
195
    :param unlink_func: A way to delete the target file if the full rename succeeds
207
196
    """
 
197
 
208
198
    # sftp rename doesn't allow overwriting, so play tricks:
209
199
    base = os.path.basename(new)
210
200
    dirname = os.path.dirname(new)
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
    tmp_name = u'tmp.%s.%.9f.%d.%s' % (base, time.time(), os.getpid(), rand_chars(10))
216
202
    tmp_name = pathjoin(dirname, tmp_name)
217
203
 
218
204
    # Rename the file out of the way, but keep track if it didn't exist
709
695
    return offset.days * 86400 + offset.seconds
710
696
 
711
697
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
 
 
714
698
 
715
699
def format_date(t, offset=0, timezone='original', date_fmt=None,
716
700
                show_offset=True):
730
714
    date_str = time.strftime(date_fmt, tt)
731
715
    return date_str + offset_str
732
716
 
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
 
 
759
717
def format_local_date(t, offset=0, timezone='original', date_fmt=None,
760
718
                      show_offset=True):
761
719
    """Return an unicode date string formatted according to the current locale.
775
733
        date_str = date_str.decode(get_user_encoding(), 'replace')
776
734
    return date_str + offset_str
777
735
 
778
 
 
779
736
def _format_date(t, offset, timezone, date_fmt, show_offset):
780
737
    if timezone == 'utc':
781
738
        tt = time.gmtime(t)
1344
1301
    normalized_filename = _inaccessible_normalized_filename
1345
1302
 
1346
1303
 
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
 
 
1355
1304
def terminal_width():
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):
 
1305
    """Return estimated terminal width."""
 
1306
    if sys.platform == 'win32':
 
1307
        return win32utils.get_console_size()[0]
 
1308
    width = 0
1411
1309
    try:
1412
1310
        import struct, fcntl, termios
1413
1311
        s = struct.pack('HHHH', 0, 0, 0, 0)
1414
1312
        x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
1415
 
        height, width = struct.unpack('HHHH', x)[0:2]
1416
 
    except (IOError, AttributeError):
 
1313
        width = struct.unpack('HHHH', x)[1]
 
1314
    except IOError:
1417
1315
        pass
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)
 
1316
    if width <= 0:
 
1317
        try:
 
1318
            width = int(os.environ['COLUMNS'])
 
1319
        except:
 
1320
            pass
 
1321
    if width <= 0:
 
1322
        width = 80
 
1323
 
 
1324
    return width
1447
1325
 
1448
1326
 
1449
1327
def supports_executable():
2074
1952
    anything goes wrong.
2075
1953
    """
2076
1954
    global _cached_local_concurrency
2077
 
 
2078
1955
    if _cached_local_concurrency is not None and use_cache:
2079
1956
        return _cached_local_concurrency
2080
1957
 
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
 
1958
    try:
 
1959
        concurrency = _local_concurrency()
 
1960
    except (OSError, IOError):
 
1961
        concurrency = None
2087
1962
    try:
2088
1963
        concurrency = int(concurrency)
2089
1964
    except (TypeError, ValueError):
2091
1966
    if use_cache:
2092
1967
        _cached_concurrency = concurrency
2093
1968
    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)