~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: 2011-08-17 18:13:57 UTC
  • mfrom: (5268.7.29 transport-segments)
  • Revision ID: pqm@pqm.ubuntu.com-20110817181357-y5q5eth1hk8bl3om
(jelmer) Allow specifying the colocated branch to use in the branch URL,
 and retrieving the branch name using ControlDir._get_selected_branch.
 (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from __future__ import absolute_import
18
 
 
19
17
import errno
20
18
import os
21
19
import re
27
25
from bzrlib.lazy_import import lazy_import
28
26
lazy_import(globals(), """
29
27
from datetime import datetime
30
 
from datetime import timedelta
31
28
import getpass
32
 
import locale
33
29
import ntpath
34
30
import posixpath
35
 
import select
36
31
# We need to import both shutil and rmtree as we export the later on posix
37
32
# and need the former on windows
38
33
import shutil
52
47
    trace,
53
48
    win32utils,
54
49
    )
55
 
from bzrlib.i18n import gettext
56
50
""")
57
51
 
58
52
from bzrlib.symbol_versioning import (
59
 
    DEPRECATED_PARAMETER,
60
53
    deprecated_function,
61
54
    deprecated_in,
62
 
    deprecated_passed,
63
 
    warn as warn_deprecated,
64
55
    )
65
56
 
66
57
from hashlib import (
70
61
 
71
62
 
72
63
import bzrlib
73
 
from bzrlib import symbol_versioning, _fs_enc
 
64
from bzrlib import symbol_versioning
74
65
 
75
66
 
76
67
# Cross platform wall-clock time functionality with decent resolution.
98
89
        user_encoding = get_user_encoding()
99
90
        return [a.decode(user_encoding) for a in sys.argv[1:]]
100
91
    except UnicodeDecodeError:
101
 
        raise errors.BzrError(gettext("Parameter {0!r} encoding is unsupported by {1} "
102
 
            "application locale.").format(a, user_encoding))
 
92
        raise errors.BzrError("Parameter %r encoding is unsupported by %s "
 
93
            "application locale." % (a, user_encoding))
103
94
 
104
95
 
105
96
def make_readonly(filename):
107
98
    mod = os.lstat(filename).st_mode
108
99
    if not stat.S_ISLNK(mod):
109
100
        mod = mod & 0777555
110
 
        chmod_if_possible(filename, mod)
 
101
        os.chmod(filename, mod)
111
102
 
112
103
 
113
104
def make_writable(filename):
114
105
    mod = os.lstat(filename).st_mode
115
106
    if not stat.S_ISLNK(mod):
116
107
        mod = mod | 0200
117
 
        chmod_if_possible(filename, mod)
118
 
 
119
 
 
120
 
def chmod_if_possible(filename, mode):
121
 
    # Set file mode if that can be safely done.
122
 
    # Sometimes even on unix the filesystem won't allow it - see
123
 
    # https://bugs.launchpad.net/bzr/+bug/606537
124
 
    try:
125
 
        # It is probably faster to just do the chmod, rather than
126
 
        # doing a stat, and then trying to compare
127
 
        os.chmod(filename, mode)
128
 
    except (IOError, OSError),e:
129
 
        # Permission/access denied seems to commonly happen on smbfs; there's
130
 
        # probably no point warning about it.
131
 
        # <https://bugs.launchpad.net/bzr/+bug/606537>
132
 
        if getattr(e, 'errno') in (errno.EPERM, errno.EACCES):
133
 
            trace.mutter("ignore error on chmod of %r: %r" % (
134
 
                filename, e))
135
 
            return
136
 
        raise
 
108
        os.chmod(filename, mod)
137
109
 
138
110
 
139
111
def minimum_path_selection(paths):
218
190
            if e.errno == errno.ENOENT:
219
191
                return False;
220
192
            else:
221
 
                raise errors.BzrError(gettext("lstat/stat of ({0!r}): {1!r}").format(f, e))
 
193
                raise errors.BzrError("lstat/stat of (%r): %r" % (f, e))
222
194
 
223
195
 
224
196
def fancy_rename(old, new, rename_func, unlink_func):
300
272
# choke on a Unicode string containing a relative path if
301
273
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
302
274
# string.
 
275
_fs_enc = sys.getfilesystemencoding() or 'utf-8'
303
276
def _posix_abspath(path):
304
277
    # jam 20060426 rather than encoding to fsencoding
305
278
    # copy posixpath.abspath, but use os.getcwdu instead
306
279
    if not posixpath.isabs(path):
307
280
        path = posixpath.join(getcwd(), path)
308
 
    return _posix_normpath(path)
 
281
    return posixpath.normpath(path)
309
282
 
310
283
 
311
284
def _posix_realpath(path):
312
285
    return posixpath.realpath(path.encode(_fs_enc)).decode(_fs_enc)
313
286
 
314
287
 
315
 
def _posix_normpath(path):
316
 
    path = posixpath.normpath(path)
317
 
    # Bug 861008: posixpath.normpath() returns a path normalized according to
318
 
    # the POSIX standard, which stipulates (for compatibility reasons) that two
319
 
    # leading slashes must not be simplified to one, and only if there are 3 or
320
 
    # more should they be simplified as one. So we treat the leading 2 slashes
321
 
    # as a special case here by simply removing the first slash, as we consider
322
 
    # that breaking POSIX compatibility for this obscure feature is acceptable.
323
 
    # This is not a paranoid precaution, as we notably get paths like this when
324
 
    # the repo is hosted at the root of the filesystem, i.e. in "/".    
325
 
    if path.startswith('//'):
326
 
        path = path[1:]
327
 
    return path
328
 
 
329
 
 
330
 
def _posix_path_from_environ(key):
331
 
    """Get unicode path from `key` in environment or None if not present
332
 
 
333
 
    Note that posix systems use arbitrary byte strings for filesystem objects,
334
 
    so a path that raises BadFilenameEncoding here may still be accessible.
335
 
    """
336
 
    val = os.environ.get(key, None)
337
 
    if val is None:
338
 
        return val
339
 
    try:
340
 
        return val.decode(_fs_enc)
341
 
    except UnicodeDecodeError:
342
 
        # GZ 2011-12-12:Ideally want to include `key` in the exception message
343
 
        raise errors.BadFilenameEncoding(val, _fs_enc)
344
 
 
345
 
 
346
 
def _posix_get_home_dir():
347
 
    """Get the home directory of the current user as a unicode path"""
348
 
    path = posixpath.expanduser("~")
349
 
    try:
350
 
        return path.decode(_fs_enc)
351
 
    except UnicodeDecodeError:
352
 
        raise errors.BadFilenameEncoding(path, _fs_enc)
353
 
 
354
 
 
355
 
def _posix_getuser_unicode():
356
 
    """Get username from environment or password database as unicode"""
357
 
    name = getpass.getuser()
358
 
    user_encoding = get_user_encoding()
359
 
    try:
360
 
        return name.decode(user_encoding)
361
 
    except UnicodeDecodeError:
362
 
        raise errors.BzrError("Encoding of username %r is unsupported by %s "
363
 
            "application locale." % (name, user_encoding))
364
 
 
365
 
 
366
288
def _win32_fixdrive(path):
367
289
    """Force drive letters to be consistent.
368
290
 
451
373
    return unicodedata.normalize('NFC', os.getcwdu())
452
374
 
453
375
 
454
 
def _rename_wrap_exception(rename_func):
455
 
    """Adds extra information to any exceptions that come from rename().
456
 
 
457
 
    The exception has an updated message and 'old_filename' and 'new_filename'
458
 
    attributes.
459
 
    """
460
 
 
461
 
    def _rename_wrapper(old, new):
462
 
        try:
463
 
            rename_func(old, new)
464
 
        except OSError, e:
465
 
            detailed_error = OSError(e.errno, e.strerror +
466
 
                                " [occurred when renaming '%s' to '%s']" %
467
 
                                (old, new))
468
 
            detailed_error.old_filename = old
469
 
            detailed_error.new_filename = new
470
 
            raise detailed_error
471
 
 
472
 
    return _rename_wrapper
473
 
 
474
 
# Default rename wraps os.rename()
475
 
rename = _rename_wrap_exception(os.rename)
476
 
 
477
376
# Default is to just use the python builtins, but these can be rebound on
478
377
# particular platforms.
479
378
abspath = _posix_abspath
480
379
realpath = _posix_realpath
481
380
pathjoin = os.path.join
482
 
normpath = _posix_normpath
483
 
path_from_environ = _posix_path_from_environ
484
 
_get_home_dir = _posix_get_home_dir
485
 
getuser_unicode = _posix_getuser_unicode
 
381
normpath = os.path.normpath
486
382
getcwd = os.getcwdu
 
383
rename = os.rename
487
384
dirname = os.path.dirname
488
385
basename = os.path.basename
489
386
split = os.path.split
511
408
    normpath = _win32_normpath
512
409
    getcwd = _win32_getcwd
513
410
    mkdtemp = _win32_mkdtemp
514
 
    rename = _rename_wrap_exception(_win32_rename)
 
411
    rename = _win32_rename
515
412
    try:
516
413
        from bzrlib import _walkdirs_win32
517
414
    except ImportError:
543
440
    f = win32utils.get_unicode_argv     # special function or None
544
441
    if f is not None:
545
442
        get_unicode_argv = f
546
 
    path_from_environ = win32utils.get_environ_unicode
547
 
    _get_home_dir = win32utils.get_home_location
548
 
    getuser_unicode = win32utils.get_user_name
549
443
 
550
444
elif sys.platform == 'darwin':
551
445
    getcwd = _mac_getcwd
828
722
            return True
829
723
 
830
724
 
831
 
def gmtime(seconds=None):
832
 
    """Convert seconds since the Epoch to a time tuple expressing UTC (a.k.a.
833
 
    GMT). When 'seconds' is not passed in, convert the current time instead.
834
 
    Handy replacement for time.gmtime() buggy on Windows and 32-bit platforms.
835
 
    """
836
 
    if seconds is None:
837
 
        seconds = time.time()
838
 
    return (datetime(1970, 1, 1) + timedelta(seconds=seconds)).timetuple()
839
 
 
840
 
 
841
725
def local_time_offset(t=None):
842
726
    """Return offset of local zone from GMT, either at present or at time t."""
843
727
    if t is None:
883
767
    """
884
768
    if offset is None:
885
769
        offset = 0
886
 
    tt = gmtime(t + offset)
 
770
    tt = time.gmtime(t + offset)
887
771
    date_fmt = _default_format_by_weekday_num[tt[6]]
888
772
    date_str = time.strftime(date_fmt, tt)
889
773
    offset_str = _cache.get(offset, None)
915
799
 
916
800
def _format_date(t, offset, timezone, date_fmt, show_offset):
917
801
    if timezone == 'utc':
918
 
        tt = gmtime(t)
 
802
        tt = time.gmtime(t)
919
803
        offset = 0
920
804
    elif timezone == 'original':
921
805
        if offset is None:
922
806
            offset = 0
923
 
        tt = gmtime(t + offset)
 
807
        tt = time.gmtime(t + offset)
924
808
    elif timezone == 'local':
925
809
        tt = time.localtime(t)
926
810
        offset = local_time_offset(t)
936
820
 
937
821
 
938
822
def compact_date(when):
939
 
    return time.strftime('%Y%m%d%H%M%S', gmtime(when))
 
823
    return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
940
824
 
941
825
 
942
826
def format_delta(delta):
993
877
    return os.fstat(f.fileno())[stat.ST_SIZE]
994
878
 
995
879
 
996
 
# Alias os.urandom to support platforms (which?) without /dev/urandom and 
997
 
# override if it doesn't work. Avoid checking on windows where there is
998
 
# significant initialisation cost that can be avoided for some bzr calls.
999
 
 
1000
 
rand_bytes = os.urandom
1001
 
 
1002
 
if rand_bytes.__module__ != "nt":
 
880
# Define rand_bytes based on platform.
 
881
try:
 
882
    # Python 2.4 and later have os.urandom,
 
883
    # but it doesn't work on some arches
 
884
    os.urandom(1)
 
885
    rand_bytes = os.urandom
 
886
except (NotImplementedError, AttributeError):
 
887
    # If python doesn't have os.urandom, or it doesn't work,
 
888
    # then try to first pull random data from /dev/urandom
1003
889
    try:
1004
 
        rand_bytes(1)
1005
 
    except NotImplementedError:
 
890
        rand_bytes = file('/dev/urandom', 'rb').read
 
891
    # Otherwise, use this hack as a last resort
 
892
    except (IOError, OSError):
1006
893
        # not well seeded, but better than nothing
1007
894
        def rand_bytes(n):
1008
895
            import random
1038
925
    rps = []
1039
926
    for f in ps:
1040
927
        if f == '..':
1041
 
            raise errors.BzrError(gettext("sorry, %r not allowed in path") % f)
 
928
            raise errors.BzrError("sorry, %r not allowed in path" % f)
1042
929
        elif (f == '.') or (f == ''):
1043
930
            pass
1044
931
        else:
1049
936
def joinpath(p):
1050
937
    for f in p:
1051
938
        if (f == '..') or (f is None) or (f == ''):
1052
 
            raise errors.BzrError(gettext("sorry, %r not allowed in path") % f)
 
939
            raise errors.BzrError("sorry, %r not allowed in path" % f)
1053
940
    return pathjoin(*p)
1054
941
 
1055
942
 
1267
1154
 
1268
1155
    if len(base) < MIN_ABS_PATHLENGTH:
1269
1156
        # must have space for e.g. a drive letter
1270
 
        raise ValueError(gettext('%r is too short to calculate a relative path')
 
1157
        raise ValueError('%r is too short to calculate a relative path'
1271
1158
            % (base,))
1272
1159
 
1273
1160
    rp = abspath(path)
1848
1735
    """
1849
1736
    global _selected_dir_reader
1850
1737
    if _selected_dir_reader is None:
 
1738
        fs_encoding = _fs_enc.upper()
1851
1739
        if sys.platform == "win32" and win32utils.winver == 'Windows NT':
1852
1740
            # Win98 doesn't have unicode apis like FindFirstFileW
1853
1741
            # TODO: We possibly could support Win98 by falling back to the
1859
1747
                _selected_dir_reader = Win32ReadDir()
1860
1748
            except ImportError:
1861
1749
                pass
1862
 
        elif _fs_enc in ('utf-8', 'ascii'):
 
1750
        elif fs_encoding in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968'):
 
1751
            # ANSI_X3.4-1968 is a form of ASCII
1863
1752
            try:
1864
1753
                from bzrlib._readdir_pyx import UTF8DirReader
1865
1754
                _selected_dir_reader = UTF8DirReader()
2024
1913
_cached_user_encoding = None
2025
1914
 
2026
1915
 
2027
 
def get_user_encoding(use_cache=DEPRECATED_PARAMETER):
 
1916
def get_user_encoding(use_cache=True):
2028
1917
    """Find out what the preferred user encoding is.
2029
1918
 
2030
1919
    This is generally the encoding that is used for command line parameters
2031
1920
    and file contents. This may be different from the terminal encoding
2032
1921
    or the filesystem encoding.
2033
1922
 
 
1923
    :param  use_cache:  Enable cache for detected encoding.
 
1924
                        (This parameter is turned on by default,
 
1925
                        and required only for selftesting)
 
1926
 
2034
1927
    :return: A string defining the preferred user encoding
2035
1928
    """
2036
1929
    global _cached_user_encoding
2037
 
    if deprecated_passed(use_cache):
2038
 
        warn_deprecated("use_cache should only have been used for tests",
2039
 
            DeprecationWarning, stacklevel=2) 
2040
 
    if _cached_user_encoding is not None:
 
1930
    if _cached_user_encoding is not None and use_cache:
2041
1931
        return _cached_user_encoding
2042
1932
 
2043
 
    if os.name == 'posix' and getattr(locale, 'CODESET', None) is not None:
2044
 
        # Use the existing locale settings and call nl_langinfo directly
2045
 
        # rather than going through getpreferredencoding. This avoids
2046
 
        # <http://bugs.python.org/issue6202> on OSX Python 2.6 and the
2047
 
        # possibility of the setlocale call throwing an error.
2048
 
        user_encoding = locale.nl_langinfo(locale.CODESET)
 
1933
    if sys.platform == 'darwin':
 
1934
        # python locale.getpreferredencoding() always return
 
1935
        # 'mac-roman' on darwin. That's a lie.
 
1936
        sys.platform = 'posix'
 
1937
        try:
 
1938
            if os.environ.get('LANG', None) is None:
 
1939
                # If LANG is not set, we end up with 'ascii', which is bad
 
1940
                # ('mac-roman' is more than ascii), so we set a default which
 
1941
                # will give us UTF-8 (which appears to work in all cases on
 
1942
                # OSX). Users are still free to override LANG of course, as
 
1943
                # long as it give us something meaningful. This work-around
 
1944
                # *may* not be needed with python 3k and/or OSX 10.5, but will
 
1945
                # work with them too -- vila 20080908
 
1946
                os.environ['LANG'] = 'en_US.UTF-8'
 
1947
            import locale
 
1948
        finally:
 
1949
            sys.platform = 'darwin'
2049
1950
    else:
2050
 
        # GZ 2011-12-19: On windows could call GetACP directly instead.
2051
 
        user_encoding = locale.getpreferredencoding(False)
 
1951
        import locale
2052
1952
 
2053
1953
    try:
2054
 
        user_encoding = codecs.lookup(user_encoding).name
2055
 
    except LookupError:
2056
 
        if user_encoding not in ("", "cp0"):
 
1954
        user_encoding = locale.getpreferredencoding()
 
1955
    except locale.Error, e:
 
1956
        sys.stderr.write('bzr: warning: %s\n'
 
1957
                         '  Could not determine what text encoding to use.\n'
 
1958
                         '  This error usually means your Python interpreter\n'
 
1959
                         '  doesn\'t support the locale set by $LANG (%s)\n'
 
1960
                         "  Continuing with ascii encoding.\n"
 
1961
                         % (e, os.environ.get('LANG')))
 
1962
        user_encoding = 'ascii'
 
1963
 
 
1964
    # Windows returns 'cp0' to indicate there is no code page. So we'll just
 
1965
    # treat that as ASCII, and not support printing unicode characters to the
 
1966
    # console.
 
1967
    #
 
1968
    # For python scripts run under vim, we get '', so also treat that as ASCII
 
1969
    if user_encoding in (None, 'cp0', ''):
 
1970
        user_encoding = 'ascii'
 
1971
    else:
 
1972
        # check encoding
 
1973
        try:
 
1974
            codecs.lookup(user_encoding)
 
1975
        except LookupError:
2057
1976
            sys.stderr.write('bzr: warning:'
2058
1977
                             ' unknown encoding %s.'
2059
1978
                             ' Continuing with ascii encoding.\n'
2060
1979
                             % user_encoding
2061
1980
                            )
2062
 
        user_encoding = 'ascii'
2063
 
    else:
2064
 
        # Get 'ascii' when setlocale has not been called or LANG=C or unset.
2065
 
        if user_encoding == 'ascii':
2066
 
            if sys.platform == 'darwin':
2067
 
                # OSX is special-cased in Python to have a UTF-8 filesystem
2068
 
                # encoding and previously had LANG set here if not present.
2069
 
                user_encoding = 'utf-8'
2070
 
            # GZ 2011-12-19: Maybe UTF-8 should be the default in this case
2071
 
            #                for some other posix platforms as well.
2072
 
 
2073
 
    _cached_user_encoding = user_encoding
 
1981
            user_encoding = 'ascii'
 
1982
 
 
1983
    if use_cache:
 
1984
        _cached_user_encoding = user_encoding
 
1985
 
2074
1986
    return user_encoding
2075
1987
 
2076
1988
 
2085
1997
    behaves inconsistently on different platforms.
2086
1998
    """
2087
1999
    if sys.platform == "win32":
 
2000
        import win32utils
2088
2001
        return win32utils.get_host_name()
2089
2002
    else:
2090
2003
        import socket
2097
2010
# data at once.
2098
2011
MAX_SOCKET_CHUNK = 64 * 1024
2099
2012
 
2100
 
_end_of_stream_errors = [errno.ECONNRESET, errno.EPIPE, errno.EINVAL]
 
2013
_end_of_stream_errors = [errno.ECONNRESET]
2101
2014
for _eno in ['WSAECONNRESET', 'WSAECONNABORTED']:
2102
2015
    _eno = getattr(errno, _eno, None)
2103
2016
    if _eno is not None:
2169
2082
    while sent_total < byte_count:
2170
2083
        try:
2171
2084
            sent = sock.send(buffer(bytes, sent_total, MAX_SOCKET_CHUNK))
2172
 
        except (socket.error, IOError), e:
2173
 
            if e.args[0] in _end_of_stream_errors:
2174
 
                raise errors.ConnectionReset(
2175
 
                    "Error trying to write to socket", e)
 
2085
        except socket.error, e:
2176
2086
            if e.args[0] != errno.EINTR:
2177
2087
                raise
2178
2088
        else:
2179
 
            if sent == 0:
2180
 
                raise errors.ConnectionReset('Sending to %s returned 0 bytes'
2181
 
                                             % (sock,))
2182
2089
            sent_total += sent
2183
 
            if report_activity is not None:
2184
 
                report_activity(sent, 'write')
 
2090
            report_activity(sent, 'write')
2185
2091
 
2186
2092
 
2187
2093
def connect_socket(address):
2272
2178
    return file_kind_from_stat_mode(mode)
2273
2179
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
2274
2180
 
2275
 
def file_stat(f, _lstat=os.lstat):
 
2181
 
 
2182
def file_kind(f, _lstat=os.lstat):
2276
2183
    try:
2277
 
        # XXX cache?
2278
 
        return _lstat(f)
 
2184
        return file_kind_from_stat_mode(_lstat(f).st_mode)
2279
2185
    except OSError, e:
2280
2186
        if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
2281
2187
            raise errors.NoSuchFile(f)
2282
2188
        raise
2283
2189
 
2284
 
def file_kind(f, _lstat=os.lstat):
2285
 
    stat_value = file_stat(f, _lstat)
2286
 
    return file_kind_from_stat_mode(stat_value.st_mode)
2287
2190
 
2288
2191
def until_no_eintr(f, *a, **kw):
2289
2192
    """Run f(*a, **kw), retrying if an EINTR error occurs.
2333
2236
 
2334
2237
 
2335
2238
if sys.platform == "win32":
 
2239
    import msvcrt
2336
2240
    def getchar():
2337
 
        import msvcrt
2338
2241
        return msvcrt.getch()
2339
2242
else:
 
2243
    import tty
 
2244
    import termios
2340
2245
    def getchar():
2341
 
        import tty
2342
 
        import termios
2343
2246
        fd = sys.stdin.fileno()
2344
2247
        settings = termios.tcgetattr(fd)
2345
2248
        try:
2349
2252
            termios.tcsetattr(fd, termios.TCSADRAIN, settings)
2350
2253
        return ch
2351
2254
 
2352
 
if sys.platform.startswith('linux'):
 
2255
if sys.platform == 'linux2':
2353
2256
    def _local_concurrency():
2354
2257
        try:
2355
2258
            return os.sysconf('SC_NPROCESSORS_ONLN')
2394
2297
    if concurrency is None:
2395
2298
        try:
2396
2299
            import multiprocessing
2397
 
            concurrency = multiprocessing.cpu_count()
2398
 
        except (ImportError, NotImplementedError):
 
2300
        except ImportError:
2399
2301
            # multiprocessing is only available on Python >= 2.6
2400
 
            # and multiprocessing.cpu_count() isn't implemented on all
2401
 
            # platforms
2402
2302
            try:
2403
2303
                concurrency = _local_concurrency()
2404
2304
            except (OSError, IOError):
2405
2305
                pass
 
2306
        else:
 
2307
            concurrency = multiprocessing.cpu_count()
2406
2308
    try:
2407
2309
        concurrency = int(concurrency)
2408
2310
    except (TypeError, ValueError):
2470
2372
    open_file = open
2471
2373
 
2472
2374
 
 
2375
def getuser_unicode():
 
2376
    """Return the username as unicode.
 
2377
    """
 
2378
    try:
 
2379
        user_encoding = get_user_encoding()
 
2380
        username = getpass.getuser().decode(user_encoding)
 
2381
    except UnicodeDecodeError:
 
2382
        raise errors.BzrError("Can't decode username as %s." % \
 
2383
                user_encoding)
 
2384
    except ImportError, e:
 
2385
        if sys.platform != 'win32':
 
2386
            raise
 
2387
        if str(e) != 'No module named pwd':
 
2388
            raise
 
2389
        # https://bugs.launchpad.net/bzr/+bug/660174
 
2390
        # getpass.getuser() is unable to return username on Windows
 
2391
        # if there is no USERNAME environment variable set.
 
2392
        # That could be true if bzr is running as a service,
 
2393
        # e.g. running `bzr serve` as a service on Windows.
 
2394
        # We should not fail with traceback in this case.
 
2395
        username = u'UNKNOWN'
 
2396
    return username
 
2397
 
 
2398
 
2473
2399
def available_backup_name(base, exists):
2474
2400
    """Find a non-existing backup file name.
2475
2401
 
2513
2439
    :param name: The base name of the executable.
2514
2440
    :return: The path to the executable found or None.
2515
2441
    """
 
2442
    path = os.environ.get('PATH')
 
2443
    if path is None:
 
2444
        return None
 
2445
    path = path.split(os.pathsep)
2516
2446
    if sys.platform == 'win32':
2517
2447
        exts = os.environ.get('PATHEXT', '').split(os.pathsep)
2518
2448
        exts = [ext.lower() for ext in exts]
2524
2454
            exts = [ext]
2525
2455
    else:
2526
2456
        exts = ['']
2527
 
    path = os.environ.get('PATH')
2528
 
    if path is not None:
2529
 
        path = path.split(os.pathsep)
2530
 
        for ext in exts:
2531
 
            for d in path:
2532
 
                f = os.path.join(d, name) + ext
2533
 
                if os.access(f, os.X_OK):
2534
 
                    return f
2535
 
    if sys.platform == 'win32':
2536
 
        app_path = win32utils.get_app_path(name)
2537
 
        if app_path != name:
2538
 
            return app_path
 
2457
    for ext in exts:
 
2458
        for d in path:
 
2459
            f = os.path.join(d, name) + ext
 
2460
            if os.access(f, os.X_OK):
 
2461
                return f
2539
2462
    return None
2540
2463
 
2541
2464
 
2565
2488
else:
2566
2489
    is_local_pid_dead = _posix_is_local_pid_dead
2567
2490
 
2568
 
_maybe_ignored = ['EAGAIN', 'EINTR', 'ENOTSUP', 'EOPNOTSUPP', 'EACCES']
2569
 
_fdatasync_ignored = [getattr(errno, name) for name in _maybe_ignored
2570
 
                      if getattr(errno, name, None) is not None]
2571
 
 
2572
2491
 
2573
2492
def fdatasync(fileno):
2574
2493
    """Flush file contents to disk if possible.
2578
2497
    """
2579
2498
    fn = getattr(os, 'fdatasync', getattr(os, 'fsync', None))
2580
2499
    if fn is not None:
2581
 
        try:
2582
 
            fn(fileno)
2583
 
        except IOError, e:
2584
 
            # See bug #1075108, on some platforms fdatasync exists, but can
2585
 
            # raise ENOTSUP. However, we are calling fdatasync to be helpful
2586
 
            # and reduce the chance of corruption-on-powerloss situations. It
2587
 
            # is not a mandatory call, so it is ok to suppress failures.
2588
 
            trace.mutter("ignoring error calling fdatasync: %s" % (e,))
2589
 
            if getattr(e, 'errno', None) not in _fdatasync_ignored:
2590
 
                raise
2591
 
 
2592
 
 
2593
 
def ensure_empty_directory_exists(path, exception_class):
2594
 
    """Make sure a local directory exists and is empty.
2595
 
    
2596
 
    If it does not exist, it is created.  If it exists and is not empty, an
2597
 
    instance of exception_class is raised.
2598
 
    """
2599
 
    try:
2600
 
        os.mkdir(path)
2601
 
    except OSError, e:
2602
 
        if e.errno != errno.EEXIST:
2603
 
            raise
2604
 
        if os.listdir(path) != []:
2605
 
            raise exception_class(path)
2606
 
 
2607
 
 
2608
 
def is_environment_error(evalue):
2609
 
    """True if exception instance is due to a process environment issue
2610
 
 
2611
 
    This includes OSError and IOError, but also other errors that come from
2612
 
    the operating system or core libraries but are not subclasses of those.
2613
 
    """
2614
 
    if isinstance(evalue, (EnvironmentError, select.error)):
2615
 
        return True
2616
 
    if sys.platform == "win32" and win32utils._is_pywintypes_error(evalue):
2617
 
        return True
2618
 
    return False
 
2500
        fn(fileno)