~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Martin Packman
  • Date: 2011-10-06 16:41:45 UTC
  • mfrom: (6015.33.10 2.4)
  • mto: This revision was merged to the branch mainline in revision 6202.
  • Revision ID: martin.packman@canonical.com-20111006164145-o98oqn32440extgt
Merge 2.4 into dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
42
42
 
43
43
from bzrlib import (
44
44
    cache_utf8,
 
45
    config,
45
46
    errors,
46
47
    trace,
47
48
    win32utils,
48
49
    )
 
50
from bzrlib.i18n import gettext
49
51
""")
50
52
 
51
53
from bzrlib.symbol_versioning import (
53
55
    deprecated_in,
54
56
    )
55
57
 
56
 
# sha and md5 modules are deprecated in python2.6 but hashlib is available as
57
 
# of 2.5
58
 
if sys.version_info < (2, 5):
59
 
    import md5 as _mod_md5
60
 
    md5 = _mod_md5.new
61
 
    import sha as _mod_sha
62
 
    sha = _mod_sha.new
63
 
else:
64
 
    from hashlib import (
65
 
        md5,
66
 
        sha1 as sha,
67
 
        )
 
58
from hashlib import (
 
59
    md5,
 
60
    sha1 as sha,
 
61
    )
68
62
 
69
63
 
70
64
import bzrlib
96
90
        user_encoding = get_user_encoding()
97
91
        return [a.decode(user_encoding) for a in sys.argv[1:]]
98
92
    except UnicodeDecodeError:
99
 
        raise errors.BzrError(("Parameter '%r' is unsupported by the current "
100
 
                                                            "encoding." % a))
 
93
        raise errors.BzrError(gettext("Parameter {0!r} encoding is unsupported by {1} "
 
94
            "application locale.").format(a, user_encoding))
101
95
 
102
96
 
103
97
def make_readonly(filename):
197
191
            if e.errno == errno.ENOENT:
198
192
                return False;
199
193
            else:
200
 
                raise errors.BzrError("lstat/stat of (%r): %r" % (f, e))
 
194
                raise errors.BzrError(gettext("lstat/stat of ({0!r}): {1!r}").format(f, e))
201
195
 
202
196
 
203
197
def fancy_rename(old, new, rename_func, unlink_func):
269
263
            else:
270
264
                rename_func(tmp_name, new)
271
265
    if failure_exc is not None:
272
 
        raise failure_exc[0], failure_exc[1], failure_exc[2]
 
266
        try:
 
267
            raise failure_exc[0], failure_exc[1], failure_exc[2]
 
268
        finally:
 
269
            del failure_exc
273
270
 
274
271
 
275
272
# In Python 2.4.2 and older, os.path.abspath and os.path.realpath
282
279
    # copy posixpath.abspath, but use os.getcwdu instead
283
280
    if not posixpath.isabs(path):
284
281
        path = posixpath.join(getcwd(), path)
285
 
    return posixpath.normpath(path)
 
282
    return _posix_normpath(path)
286
283
 
287
284
 
288
285
def _posix_realpath(path):
289
286
    return posixpath.realpath(path.encode(_fs_enc)).decode(_fs_enc)
290
287
 
291
288
 
 
289
def _posix_normpath(path):
 
290
    path = posixpath.normpath(path)
 
291
    # Bug 861008: posixpath.normpath() returns a path normalized according to
 
292
    # the POSIX standard, which stipulates (for compatibility reasons) that two
 
293
    # leading slashes must not be simplified to one, and only if there are 3 or
 
294
    # more should they be simplified as one. So we treat the leading 2 slashes
 
295
    # as a special case here by simply removing the first slash, as we consider
 
296
    # that breaking POSIX compatibility for this obscure feature is acceptable.
 
297
    # This is not a paranoid precaution, as we notably get paths like this when
 
298
    # the repo is hosted at the root of the filesystem, i.e. in "/".    
 
299
    if path.startswith('//'):
 
300
        path = path[1:]
 
301
    return path
 
302
 
 
303
 
292
304
def _win32_fixdrive(path):
293
305
    """Force drive letters to be consistent.
294
306
 
382
394
abspath = _posix_abspath
383
395
realpath = _posix_realpath
384
396
pathjoin = os.path.join
385
 
normpath = os.path.normpath
 
397
normpath = _posix_normpath
386
398
getcwd = os.getcwdu
387
399
rename = os.rename
388
400
dirname = os.path.dirname
392
404
# These were already lazily imported into local scope
393
405
# mkdtemp = tempfile.mkdtemp
394
406
# rmtree = shutil.rmtree
 
407
lstat = os.lstat
 
408
fstat = os.fstat
 
409
 
 
410
def wrap_stat(st):
 
411
    return st
 
412
 
395
413
 
396
414
MIN_ABS_PATHLENGTH = 1
397
415
 
407
425
    getcwd = _win32_getcwd
408
426
    mkdtemp = _win32_mkdtemp
409
427
    rename = _win32_rename
 
428
    try:
 
429
        from bzrlib import _walkdirs_win32
 
430
    except ImportError:
 
431
        pass
 
432
    else:
 
433
        lstat = _walkdirs_win32.lstat
 
434
        fstat = _walkdirs_win32.fstat
 
435
        wrap_stat = _walkdirs_win32.wrap_stat
410
436
 
411
437
    MIN_ABS_PATHLENGTH = 3
412
438
 
915
941
    rps = []
916
942
    for f in ps:
917
943
        if f == '..':
918
 
            raise errors.BzrError("sorry, %r not allowed in path" % f)
 
944
            raise errors.BzrError(gettext("sorry, %r not allowed in path") % f)
919
945
        elif (f == '.') or (f == ''):
920
946
            pass
921
947
        else:
926
952
def joinpath(p):
927
953
    for f in p:
928
954
        if (f == '..') or (f is None) or (f == ''):
929
 
            raise errors.BzrError("sorry, %r not allowed in path" % f)
 
955
            raise errors.BzrError(gettext("sorry, %r not allowed in path") % f)
930
956
    return pathjoin(*p)
931
957
 
932
958
 
976
1002
def report_extension_load_failures():
977
1003
    if not _extension_load_failures:
978
1004
        return
979
 
    from bzrlib.config import GlobalConfig
980
 
    if GlobalConfig().get_user_option_as_bool('ignore_missing_extensions'):
 
1005
    if config.GlobalStack().get('ignore_missing_extensions'):
981
1006
        return
982
1007
    # the warnings framework should by default show this only once
983
1008
    from bzrlib.trace import warning
1145
1170
 
1146
1171
    if len(base) < MIN_ABS_PATHLENGTH:
1147
1172
        # must have space for e.g. a drive letter
1148
 
        raise ValueError('%r is too short to calculate a relative path'
 
1173
        raise ValueError(gettext('%r is too short to calculate a relative path')
1149
1174
            % (base,))
1150
1175
 
1151
1176
    rp = abspath(path)
2169
2194
    return file_kind_from_stat_mode(mode)
2170
2195
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
2171
2196
 
2172
 
 
2173
 
def file_kind(f, _lstat=os.lstat):
 
2197
def file_stat(f, _lstat=os.lstat):
2174
2198
    try:
2175
 
        return file_kind_from_stat_mode(_lstat(f).st_mode)
 
2199
        # XXX cache?
 
2200
        return _lstat(f)
2176
2201
    except OSError, e:
2177
2202
        if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
2178
2203
            raise errors.NoSuchFile(f)
2179
2204
        raise
2180
2205
 
 
2206
def file_kind(f, _lstat=os.lstat):
 
2207
    stat_value = file_stat(f, _lstat)
 
2208
    return file_kind_from_stat_mode(stat_value.st_mode)
2181
2209
 
2182
2210
def until_no_eintr(f, *a, **kw):
2183
2211
    """Run f(*a, **kw), retrying if an EINTR error occurs.
2243
2271
            termios.tcsetattr(fd, termios.TCSADRAIN, settings)
2244
2272
        return ch
2245
2273
 
2246
 
 
2247
 
if sys.platform == 'linux2':
 
2274
if sys.platform.startswith('linux'):
2248
2275
    def _local_concurrency():
2249
 
        concurrency = None
2250
 
        prefix = 'processor'
2251
 
        for line in file('/proc/cpuinfo', 'rb'):
2252
 
            if line.startswith(prefix):
2253
 
                concurrency = int(line[line.find(':')+1:]) + 1
2254
 
        return concurrency
 
2276
        try:
 
2277
            return os.sysconf('SC_NPROCESSORS_ONLN')
 
2278
        except (ValueError, OSError, AttributeError):
 
2279
            return None
2255
2280
elif sys.platform == 'darwin':
2256
2281
    def _local_concurrency():
2257
2282
        return subprocess.Popen(['sysctl', '-n', 'hw.availcpu'],
2258
2283
                                stdout=subprocess.PIPE).communicate()[0]
2259
 
elif sys.platform[0:7] == 'freebsd':
 
2284
elif "bsd" in sys.platform:
2260
2285
    def _local_concurrency():
2261
2286
        return subprocess.Popen(['sysctl', '-n', 'hw.ncpu'],
2262
2287
                                stdout=subprocess.PIPE).communicate()[0]
2290
2315
    concurrency = os.environ.get('BZR_CONCURRENCY', None)
2291
2316
    if concurrency is None:
2292
2317
        try:
2293
 
            concurrency = _local_concurrency()
2294
 
        except (OSError, IOError):
2295
 
            pass
 
2318
            import multiprocessing
 
2319
        except ImportError:
 
2320
            # multiprocessing is only available on Python >= 2.6
 
2321
            try:
 
2322
                concurrency = _local_concurrency()
 
2323
            except (OSError, IOError):
 
2324
                pass
 
2325
        else:
 
2326
            concurrency = multiprocessing.cpu_count()
2296
2327
    try:
2297
2328
        concurrency = int(concurrency)
2298
2329
    except (TypeError, ValueError):
2369
2400
    except UnicodeDecodeError:
2370
2401
        raise errors.BzrError("Can't decode username as %s." % \
2371
2402
                user_encoding)
 
2403
    except ImportError, e:
 
2404
        if sys.platform != 'win32':
 
2405
            raise
 
2406
        if str(e) != 'No module named pwd':
 
2407
            raise
 
2408
        # https://bugs.launchpad.net/bzr/+bug/660174
 
2409
        # getpass.getuser() is unable to return username on Windows
 
2410
        # if there is no USERNAME environment variable set.
 
2411
        # That could be true if bzr is running as a service,
 
2412
        # e.g. running `bzr serve` as a service on Windows.
 
2413
        # We should not fail with traceback in this case.
 
2414
        username = u'UNKNOWN'
2372
2415
    return username
2373
2416
 
2374
2417
 
2436
2479
            if os.access(f, os.X_OK):
2437
2480
                return f
2438
2481
    return None
 
2482
 
 
2483
 
 
2484
def _posix_is_local_pid_dead(pid):
 
2485
    """True if pid doesn't correspond to live process on this machine"""
 
2486
    try:
 
2487
        # Special meaning of unix kill: just check if it's there.
 
2488
        os.kill(pid, 0)
 
2489
    except OSError, e:
 
2490
        if e.errno == errno.ESRCH:
 
2491
            # On this machine, and really not found: as sure as we can be
 
2492
            # that it's dead.
 
2493
            return True
 
2494
        elif e.errno == errno.EPERM:
 
2495
            # exists, though not ours
 
2496
            return False
 
2497
        else:
 
2498
            mutter("os.kill(%d, 0) failed: %s" % (pid, e))
 
2499
            # Don't really know.
 
2500
            return False
 
2501
    else:
 
2502
        # Exists and our process: not dead.
 
2503
        return False
 
2504
 
 
2505
if sys.platform == "win32":
 
2506
    is_local_pid_dead = win32utils.is_local_pid_dead
 
2507
else:
 
2508
    is_local_pid_dead = _posix_is_local_pid_dead
 
2509
 
 
2510
 
 
2511
def fdatasync(fileno):
 
2512
    """Flush file contents to disk if possible.
 
2513
    
 
2514
    :param fileno: Integer OS file handle.
 
2515
    :raises TransportNotPossible: If flushing to disk is not possible.
 
2516
    """
 
2517
    fn = getattr(os, 'fdatasync', getattr(os, 'fsync', None))
 
2518
    if fn is not None:
 
2519
        fn(fileno)