~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Martin Packman
  • Date: 2012-01-05 09:50:04 UTC
  • mfrom: (6424 +trunk)
  • mto: This revision was merged to the branch mainline in revision 6426.
  • Revision ID: martin.packman@canonical.com-20120105095004-mia9xb7y0efmto0v
Merge bzr.dev to resolve conflicts in bzrlib.builtins

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
 
17
19
import errno
18
20
import os
19
21
import re
28
30
import getpass
29
31
import ntpath
30
32
import posixpath
 
33
import select
31
34
# We need to import both shutil and rmtree as we export the later on posix
32
35
# and need the former on windows
33
36
import shutil
47
50
    trace,
48
51
    win32utils,
49
52
    )
 
53
from bzrlib.i18n import gettext
50
54
""")
51
55
 
52
56
from bzrlib.symbol_versioning import (
61
65
 
62
66
 
63
67
import bzrlib
64
 
from bzrlib import symbol_versioning
 
68
from bzrlib import symbol_versioning, _fs_enc
65
69
 
66
70
 
67
71
# Cross platform wall-clock time functionality with decent resolution.
89
93
        user_encoding = get_user_encoding()
90
94
        return [a.decode(user_encoding) for a in sys.argv[1:]]
91
95
    except UnicodeDecodeError:
92
 
        raise errors.BzrError("Parameter %r encoding is unsupported by %s "
93
 
            "application locale." % (a, user_encoding))
 
96
        raise errors.BzrError(gettext("Parameter {0!r} encoding is unsupported by {1} "
 
97
            "application locale.").format(a, user_encoding))
94
98
 
95
99
 
96
100
def make_readonly(filename):
98
102
    mod = os.lstat(filename).st_mode
99
103
    if not stat.S_ISLNK(mod):
100
104
        mod = mod & 0777555
101
 
        os.chmod(filename, mod)
 
105
        chmod_if_possible(filename, mod)
102
106
 
103
107
 
104
108
def make_writable(filename):
105
109
    mod = os.lstat(filename).st_mode
106
110
    if not stat.S_ISLNK(mod):
107
111
        mod = mod | 0200
108
 
        os.chmod(filename, mod)
 
112
        chmod_if_possible(filename, mod)
 
113
 
 
114
 
 
115
def chmod_if_possible(filename, mode):
 
116
    # Set file mode if that can be safely done.
 
117
    # Sometimes even on unix the filesystem won't allow it - see
 
118
    # https://bugs.launchpad.net/bzr/+bug/606537
 
119
    try:
 
120
        # It is probably faster to just do the chmod, rather than
 
121
        # doing a stat, and then trying to compare
 
122
        os.chmod(filename, mode)
 
123
    except (IOError, OSError),e:
 
124
        # Permission/access denied seems to commonly happen on smbfs; there's
 
125
        # probably no point warning about it.
 
126
        # <https://bugs.launchpad.net/bzr/+bug/606537>
 
127
        if getattr(e, 'errno') in (errno.EPERM, errno.EACCES):
 
128
            trace.mutter("ignore error on chmod of %r: %r" % (
 
129
                filename, e))
 
130
            return
 
131
        raise
109
132
 
110
133
 
111
134
def minimum_path_selection(paths):
190
213
            if e.errno == errno.ENOENT:
191
214
                return False;
192
215
            else:
193
 
                raise errors.BzrError("lstat/stat of (%r): %r" % (f, e))
 
216
                raise errors.BzrError(gettext("lstat/stat of ({0!r}): {1!r}").format(f, e))
194
217
 
195
218
 
196
219
def fancy_rename(old, new, rename_func, unlink_func):
272
295
# choke on a Unicode string containing a relative path if
273
296
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
274
297
# string.
275
 
_fs_enc = sys.getfilesystemencoding() or 'utf-8'
276
298
def _posix_abspath(path):
277
299
    # jam 20060426 rather than encoding to fsencoding
278
300
    # copy posixpath.abspath, but use os.getcwdu instead
279
301
    if not posixpath.isabs(path):
280
302
        path = posixpath.join(getcwd(), path)
281
 
    return posixpath.normpath(path)
 
303
    return _posix_normpath(path)
282
304
 
283
305
 
284
306
def _posix_realpath(path):
285
307
    return posixpath.realpath(path.encode(_fs_enc)).decode(_fs_enc)
286
308
 
287
309
 
 
310
def _posix_normpath(path):
 
311
    path = posixpath.normpath(path)
 
312
    # Bug 861008: posixpath.normpath() returns a path normalized according to
 
313
    # the POSIX standard, which stipulates (for compatibility reasons) that two
 
314
    # leading slashes must not be simplified to one, and only if there are 3 or
 
315
    # more should they be simplified as one. So we treat the leading 2 slashes
 
316
    # as a special case here by simply removing the first slash, as we consider
 
317
    # that breaking POSIX compatibility for this obscure feature is acceptable.
 
318
    # This is not a paranoid precaution, as we notably get paths like this when
 
319
    # the repo is hosted at the root of the filesystem, i.e. in "/".    
 
320
    if path.startswith('//'):
 
321
        path = path[1:]
 
322
    return path
 
323
 
 
324
 
 
325
def _posix_path_from_environ(key):
 
326
    """Get unicode path from `key` in environment or None if not present
 
327
 
 
328
    Note that posix systems use arbitrary byte strings for filesystem objects,
 
329
    so a path that raises BadFilenameEncoding here may still be accessible.
 
330
    """
 
331
    val = os.environ.get(key, None)
 
332
    if val is None:
 
333
        return val
 
334
    try:
 
335
        return val.decode(_fs_enc)
 
336
    except UnicodeDecodeError:
 
337
        # GZ 2011-12-12:Ideally want to include `key` in the exception message
 
338
        raise errors.BadFilenameEncoding(val, _fs_enc)
 
339
 
 
340
 
 
341
def _posix_getuser_unicode():
 
342
    """Get username from environment or password database as unicode"""
 
343
    name = getpass.getuser()
 
344
    user_encoding = get_user_encoding()
 
345
    try:
 
346
        return name.decode(user_encoding)
 
347
    except UnicodeDecodeError:
 
348
        raise errors.BzrError("Encoding of username %r is unsupported by %s "
 
349
            "application locale." % (name, user_encoding))
 
350
 
 
351
 
288
352
def _win32_fixdrive(path):
289
353
    """Force drive letters to be consistent.
290
354
 
378
442
abspath = _posix_abspath
379
443
realpath = _posix_realpath
380
444
pathjoin = os.path.join
381
 
normpath = os.path.normpath
 
445
normpath = _posix_normpath
 
446
path_from_environ = _posix_path_from_environ
 
447
getuser_unicode = _posix_getuser_unicode
382
448
getcwd = os.getcwdu
383
449
rename = os.rename
384
450
dirname = os.path.dirname
440
506
    f = win32utils.get_unicode_argv     # special function or None
441
507
    if f is not None:
442
508
        get_unicode_argv = f
 
509
    path_from_environ = win32utils.get_environ_unicode
 
510
    getuser_unicode = win32utils.get_user_name
443
511
 
444
512
elif sys.platform == 'darwin':
445
513
    getcwd = _mac_getcwd
877
945
    return os.fstat(f.fileno())[stat.ST_SIZE]
878
946
 
879
947
 
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
 
948
# Alias os.urandom to support platforms (which?) without /dev/urandom and 
 
949
# override if it doesn't work. Avoid checking on windows where there is
 
950
# significant initialisation cost that can be avoided for some bzr calls.
 
951
 
 
952
rand_bytes = os.urandom
 
953
 
 
954
if rand_bytes.__module__ != "nt":
889
955
    try:
890
 
        rand_bytes = file('/dev/urandom', 'rb').read
891
 
    # Otherwise, use this hack as a last resort
892
 
    except (IOError, OSError):
 
956
        rand_bytes(1)
 
957
    except NotImplementedError:
893
958
        # not well seeded, but better than nothing
894
959
        def rand_bytes(n):
895
960
            import random
925
990
    rps = []
926
991
    for f in ps:
927
992
        if f == '..':
928
 
            raise errors.BzrError("sorry, %r not allowed in path" % f)
 
993
            raise errors.BzrError(gettext("sorry, %r not allowed in path") % f)
929
994
        elif (f == '.') or (f == ''):
930
995
            pass
931
996
        else:
936
1001
def joinpath(p):
937
1002
    for f in p:
938
1003
        if (f == '..') or (f is None) or (f == ''):
939
 
            raise errors.BzrError("sorry, %r not allowed in path" % f)
 
1004
            raise errors.BzrError(gettext("sorry, %r not allowed in path") % f)
940
1005
    return pathjoin(*p)
941
1006
 
942
1007
 
1154
1219
 
1155
1220
    if len(base) < MIN_ABS_PATHLENGTH:
1156
1221
        # must have space for e.g. a drive letter
1157
 
        raise ValueError('%r is too short to calculate a relative path'
 
1222
        raise ValueError(gettext('%r is too short to calculate a relative path')
1158
1223
            % (base,))
1159
1224
 
1160
1225
    rp = abspath(path)
1735
1800
    """
1736
1801
    global _selected_dir_reader
1737
1802
    if _selected_dir_reader is None:
1738
 
        fs_encoding = _fs_enc.upper()
1739
1803
        if sys.platform == "win32" and win32utils.winver == 'Windows NT':
1740
1804
            # Win98 doesn't have unicode apis like FindFirstFileW
1741
1805
            # TODO: We possibly could support Win98 by falling back to the
1747
1811
                _selected_dir_reader = Win32ReadDir()
1748
1812
            except ImportError:
1749
1813
                pass
1750
 
        elif fs_encoding in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968'):
1751
 
            # ANSI_X3.4-1968 is a form of ASCII
 
1814
        elif _fs_enc in ('utf-8', 'ascii'):
1752
1815
            try:
1753
1816
                from bzrlib._readdir_pyx import UTF8DirReader
1754
1817
                _selected_dir_reader = UTF8DirReader()
1990
2053
    return get_terminal_encoding()
1991
2054
 
1992
2055
 
 
2056
_message_encoding = None
 
2057
 
 
2058
 
 
2059
def get_message_encoding():
 
2060
    """Return the encoding used for messages
 
2061
 
 
2062
    While the message encoding is a general setting it should usually only be
 
2063
    needed for decoding system error strings such as from OSError instances.
 
2064
    """
 
2065
    global _message_encoding
 
2066
    if _message_encoding is None:
 
2067
        if os.name == "posix":
 
2068
            import locale
 
2069
            # This is a process-global setting that can change, but should in
 
2070
            # general just get set once at process startup then be constant.
 
2071
            _message_encoding = locale.getlocale(locale.LC_MESSAGES)[1]
 
2072
        else:
 
2073
            # On windows want the result of GetACP() which this boils down to.
 
2074
            _message_encoding = get_user_encoding()
 
2075
    return _message_encoding or "ascii"
 
2076
 
 
2077
 
1993
2078
def get_host_name():
1994
2079
    """Return the current unicode host name.
1995
2080
 
1997
2082
    behaves inconsistently on different platforms.
1998
2083
    """
1999
2084
    if sys.platform == "win32":
2000
 
        import win32utils
2001
2085
        return win32utils.get_host_name()
2002
2086
    else:
2003
2087
        import socket
2239
2323
 
2240
2324
 
2241
2325
if sys.platform == "win32":
2242
 
    import msvcrt
2243
2326
    def getchar():
 
2327
        import msvcrt
2244
2328
        return msvcrt.getch()
2245
2329
else:
2246
 
    import tty
2247
 
    import termios
2248
2330
    def getchar():
 
2331
        import tty
 
2332
        import termios
2249
2333
        fd = sys.stdin.fileno()
2250
2334
        settings = termios.tcgetattr(fd)
2251
2335
        try:
2300
2384
    if concurrency is None:
2301
2385
        try:
2302
2386
            import multiprocessing
2303
 
        except ImportError:
 
2387
            concurrency = multiprocessing.cpu_count()
 
2388
        except (ImportError, NotImplementedError):
2304
2389
            # multiprocessing is only available on Python >= 2.6
 
2390
            # and multiprocessing.cpu_count() isn't implemented on all
 
2391
            # platforms
2305
2392
            try:
2306
2393
                concurrency = _local_concurrency()
2307
2394
            except (OSError, IOError):
2308
2395
                pass
2309
 
        else:
2310
 
            concurrency = multiprocessing.cpu_count()
2311
2396
    try:
2312
2397
        concurrency = int(concurrency)
2313
2398
    except (TypeError, ValueError):
2375
2460
    open_file = open
2376
2461
 
2377
2462
 
2378
 
def getuser_unicode():
2379
 
    """Return the username as unicode.
2380
 
    """
2381
 
    try:
2382
 
        user_encoding = get_user_encoding()
2383
 
        username = getpass.getuser().decode(user_encoding)
2384
 
    except UnicodeDecodeError:
2385
 
        raise errors.BzrError("Can't decode username as %s." % \
2386
 
                user_encoding)
2387
 
    except ImportError, e:
2388
 
        if sys.platform != 'win32':
2389
 
            raise
2390
 
        if str(e) != 'No module named pwd':
2391
 
            raise
2392
 
        # https://bugs.launchpad.net/bzr/+bug/660174
2393
 
        # getpass.getuser() is unable to return username on Windows
2394
 
        # if there is no USERNAME environment variable set.
2395
 
        # That could be true if bzr is running as a service,
2396
 
        # e.g. running `bzr serve` as a service on Windows.
2397
 
        # We should not fail with traceback in this case.
2398
 
        username = u'UNKNOWN'
2399
 
    return username
2400
 
 
2401
 
 
2402
2463
def available_backup_name(base, exists):
2403
2464
    """Find a non-existing backup file name.
2404
2465
 
2501
2562
    fn = getattr(os, 'fdatasync', getattr(os, 'fsync', None))
2502
2563
    if fn is not None:
2503
2564
        fn(fileno)
 
2565
 
 
2566
 
 
2567
def ensure_empty_directory_exists(path, exception_class):
 
2568
    """Make sure a local directory exists and is empty.
 
2569
    
 
2570
    If it does not exist, it is created.  If it exists and is not empty, an
 
2571
    instance of exception_class is raised.
 
2572
    """
 
2573
    try:
 
2574
        os.mkdir(path)
 
2575
    except OSError, e:
 
2576
        if e.errno != errno.EEXIST:
 
2577
            raise
 
2578
        if os.listdir(path) != []:
 
2579
            raise exception_class(path)
 
2580
 
 
2581
 
 
2582
def is_environment_error(evalue):
 
2583
    """True if exception instance is due to a process environment issue
 
2584
 
 
2585
    This includes OSError and IOError, but also other errors that come from
 
2586
    the operating system or core libraries but are not subclasses of those.
 
2587
    """
 
2588
    if isinstance(evalue, (EnvironmentError, select.error)):
 
2589
        return True
 
2590
    if sys.platform == "win32" and win32utils._is_pywintypes_error(evalue):
 
2591
        return True
 
2592
    return False