~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Patch Queue Manager
  • Date: 2011-12-18 21:24:45 UTC
  • mfrom: (6379.1.1 no-termios)
  • Revision ID: pqm@pqm.ubuntu.com-20111218212445-onsppr7rdov3cw42
(jelmer) Avoid always importing termios and tty in bzrlib.osutils. (Jelmer
 Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
28
28
import getpass
29
29
import ntpath
30
30
import posixpath
 
31
import select
31
32
# We need to import both shutil and rmtree as we export the later on posix
32
33
# and need the former on windows
33
34
import shutil
42
43
 
43
44
from bzrlib import (
44
45
    cache_utf8,
 
46
    config,
45
47
    errors,
46
48
    trace,
47
49
    win32utils,
48
50
    )
 
51
from bzrlib.i18n import gettext
49
52
""")
50
53
 
51
54
from bzrlib.symbol_versioning import (
53
56
    deprecated_in,
54
57
    )
55
58
 
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
 
        )
 
59
from hashlib import (
 
60
    md5,
 
61
    sha1 as sha,
 
62
    )
68
63
 
69
64
 
70
65
import bzrlib
71
 
from bzrlib import symbol_versioning
 
66
from bzrlib import symbol_versioning, _fs_enc
72
67
 
73
68
 
74
69
# Cross platform wall-clock time functionality with decent resolution.
96
91
        user_encoding = get_user_encoding()
97
92
        return [a.decode(user_encoding) for a in sys.argv[1:]]
98
93
    except UnicodeDecodeError:
99
 
        raise errors.BzrError(("Parameter '%r' is unsupported by the current "
100
 
                                                            "encoding." % a))
 
94
        raise errors.BzrError(gettext("Parameter {0!r} encoding is unsupported by {1} "
 
95
            "application locale.").format(a, user_encoding))
101
96
 
102
97
 
103
98
def make_readonly(filename):
105
100
    mod = os.lstat(filename).st_mode
106
101
    if not stat.S_ISLNK(mod):
107
102
        mod = mod & 0777555
108
 
        os.chmod(filename, mod)
 
103
        chmod_if_possible(filename, mod)
109
104
 
110
105
 
111
106
def make_writable(filename):
112
107
    mod = os.lstat(filename).st_mode
113
108
    if not stat.S_ISLNK(mod):
114
109
        mod = mod | 0200
115
 
        os.chmod(filename, mod)
 
110
        chmod_if_possible(filename, mod)
 
111
 
 
112
 
 
113
def chmod_if_possible(filename, mode):
 
114
    # Set file mode if that can be safely done.
 
115
    # Sometimes even on unix the filesystem won't allow it - see
 
116
    # https://bugs.launchpad.net/bzr/+bug/606537
 
117
    try:
 
118
        # It is probably faster to just do the chmod, rather than
 
119
        # doing a stat, and then trying to compare
 
120
        os.chmod(filename, mode)
 
121
    except (IOError, OSError),e:
 
122
        # Permission/access denied seems to commonly happen on smbfs; there's
 
123
        # probably no point warning about it.
 
124
        # <https://bugs.launchpad.net/bzr/+bug/606537>
 
125
        if getattr(e, 'errno') in (errno.EPERM, errno.EACCES):
 
126
            trace.mutter("ignore error on chmod of %r: %r" % (
 
127
                filename, e))
 
128
            return
 
129
        raise
116
130
 
117
131
 
118
132
def minimum_path_selection(paths):
197
211
            if e.errno == errno.ENOENT:
198
212
                return False;
199
213
            else:
200
 
                raise errors.BzrError("lstat/stat of (%r): %r" % (f, e))
 
214
                raise errors.BzrError(gettext("lstat/stat of ({0!r}): {1!r}").format(f, e))
201
215
 
202
216
 
203
217
def fancy_rename(old, new, rename_func, unlink_func):
269
283
            else:
270
284
                rename_func(tmp_name, new)
271
285
    if failure_exc is not None:
272
 
        raise failure_exc[0], failure_exc[1], failure_exc[2]
 
286
        try:
 
287
            raise failure_exc[0], failure_exc[1], failure_exc[2]
 
288
        finally:
 
289
            del failure_exc
273
290
 
274
291
 
275
292
# In Python 2.4.2 and older, os.path.abspath and os.path.realpath
276
293
# choke on a Unicode string containing a relative path if
277
294
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
278
295
# string.
279
 
_fs_enc = sys.getfilesystemencoding() or 'utf-8'
280
296
def _posix_abspath(path):
281
297
    # jam 20060426 rather than encoding to fsencoding
282
298
    # copy posixpath.abspath, but use os.getcwdu instead
283
299
    if not posixpath.isabs(path):
284
300
        path = posixpath.join(getcwd(), path)
285
 
    return posixpath.normpath(path)
 
301
    return _posix_normpath(path)
286
302
 
287
303
 
288
304
def _posix_realpath(path):
289
305
    return posixpath.realpath(path.encode(_fs_enc)).decode(_fs_enc)
290
306
 
291
307
 
 
308
def _posix_normpath(path):
 
309
    path = posixpath.normpath(path)
 
310
    # Bug 861008: posixpath.normpath() returns a path normalized according to
 
311
    # the POSIX standard, which stipulates (for compatibility reasons) that two
 
312
    # leading slashes must not be simplified to one, and only if there are 3 or
 
313
    # more should they be simplified as one. So we treat the leading 2 slashes
 
314
    # as a special case here by simply removing the first slash, as we consider
 
315
    # that breaking POSIX compatibility for this obscure feature is acceptable.
 
316
    # This is not a paranoid precaution, as we notably get paths like this when
 
317
    # the repo is hosted at the root of the filesystem, i.e. in "/".    
 
318
    if path.startswith('//'):
 
319
        path = path[1:]
 
320
    return path
 
321
 
 
322
 
 
323
def _posix_path_from_environ(key):
 
324
    """Get unicode path from `key` in environment or None if not present
 
325
 
 
326
    Note that posix systems use arbitrary byte strings for filesystem objects,
 
327
    so a path that raises BadFilenameEncoding here may still be accessible.
 
328
    """
 
329
    val = os.environ.get(key, None)
 
330
    if val is None:
 
331
        return val
 
332
    try:
 
333
        return val.decode(_fs_enc)
 
334
    except UnicodeDecodeError:
 
335
        # GZ 2011-12-12:Ideally want to include `key` in the exception message
 
336
        raise errors.BadFilenameEncoding(val, _fs_enc)
 
337
 
 
338
 
 
339
def _posix_getuser_unicode():
 
340
    """Get username from environment or password database as unicode"""
 
341
    name = getpass.getuser()
 
342
    user_encoding = get_user_encoding()
 
343
    try:
 
344
        return name.decode(user_encoding)
 
345
    except UnicodeDecodeError:
 
346
        raise errors.BzrError("Encoding of username %r is unsupported by %s "
 
347
            "application locale." % (name, user_encoding))
 
348
 
 
349
 
292
350
def _win32_fixdrive(path):
293
351
    """Force drive letters to be consistent.
294
352
 
382
440
abspath = _posix_abspath
383
441
realpath = _posix_realpath
384
442
pathjoin = os.path.join
385
 
normpath = os.path.normpath
 
443
normpath = _posix_normpath
 
444
path_from_environ = _posix_path_from_environ
 
445
getuser_unicode = _posix_getuser_unicode
386
446
getcwd = os.getcwdu
387
447
rename = os.rename
388
448
dirname = os.path.dirname
392
452
# These were already lazily imported into local scope
393
453
# mkdtemp = tempfile.mkdtemp
394
454
# rmtree = shutil.rmtree
 
455
lstat = os.lstat
 
456
fstat = os.fstat
 
457
 
 
458
def wrap_stat(st):
 
459
    return st
 
460
 
395
461
 
396
462
MIN_ABS_PATHLENGTH = 1
397
463
 
407
473
    getcwd = _win32_getcwd
408
474
    mkdtemp = _win32_mkdtemp
409
475
    rename = _win32_rename
 
476
    try:
 
477
        from bzrlib import _walkdirs_win32
 
478
    except ImportError:
 
479
        pass
 
480
    else:
 
481
        lstat = _walkdirs_win32.lstat
 
482
        fstat = _walkdirs_win32.fstat
 
483
        wrap_stat = _walkdirs_win32.wrap_stat
410
484
 
411
485
    MIN_ABS_PATHLENGTH = 3
412
486
 
430
504
    f = win32utils.get_unicode_argv     # special function or None
431
505
    if f is not None:
432
506
        get_unicode_argv = f
 
507
    path_from_environ = win32utils.get_environ_unicode
 
508
    getuser_unicode = win32utils.get_user_name
433
509
 
434
510
elif sys.platform == 'darwin':
435
511
    getcwd = _mac_getcwd
915
991
    rps = []
916
992
    for f in ps:
917
993
        if f == '..':
918
 
            raise errors.BzrError("sorry, %r not allowed in path" % f)
 
994
            raise errors.BzrError(gettext("sorry, %r not allowed in path") % f)
919
995
        elif (f == '.') or (f == ''):
920
996
            pass
921
997
        else:
926
1002
def joinpath(p):
927
1003
    for f in p:
928
1004
        if (f == '..') or (f is None) or (f == ''):
929
 
            raise errors.BzrError("sorry, %r not allowed in path" % f)
 
1005
            raise errors.BzrError(gettext("sorry, %r not allowed in path") % f)
930
1006
    return pathjoin(*p)
931
1007
 
932
1008
 
967
1043
    # they tend to happen very early in startup when we can't check config
968
1044
    # files etc, and also we want to report all failures but not spam the user
969
1045
    # with 10 warnings.
970
 
    from bzrlib import trace
971
1046
    exception_str = str(exception)
972
1047
    if exception_str not in _extension_load_failures:
973
1048
        trace.mutter("failed to load compiled extension: %s" % exception_str)
977
1052
def report_extension_load_failures():
978
1053
    if not _extension_load_failures:
979
1054
        return
980
 
    from bzrlib.config import GlobalConfig
981
 
    if GlobalConfig().get_user_option_as_bool('ignore_missing_extensions'):
 
1055
    if config.GlobalStack().get('ignore_missing_extensions'):
982
1056
        return
983
1057
    # the warnings framework should by default show this only once
984
1058
    from bzrlib.trace import warning
1146
1220
 
1147
1221
    if len(base) < MIN_ABS_PATHLENGTH:
1148
1222
        # must have space for e.g. a drive letter
1149
 
        raise ValueError('%r is too short to calculate a relative path'
 
1223
        raise ValueError(gettext('%r is too short to calculate a relative path')
1150
1224
            % (base,))
1151
1225
 
1152
1226
    rp = abspath(path)
1462
1536
    # a similar effect.
1463
1537
 
1464
1538
    # If BZR_COLUMNS is set, take it, user is always right
 
1539
    # Except if they specified 0 in which case, impose no limit here
1465
1540
    try:
1466
 
        return int(os.environ['BZR_COLUMNS'])
 
1541
        width = int(os.environ['BZR_COLUMNS'])
1467
1542
    except (KeyError, ValueError):
1468
 
        pass
 
1543
        width = None
 
1544
    if width is not None:
 
1545
        if width > 0:
 
1546
            return width
 
1547
        else:
 
1548
            return None
1469
1549
 
1470
1550
    isatty = getattr(sys.stdout, 'isatty', None)
1471
1551
    if isatty is None or not isatty():
1721
1801
    """
1722
1802
    global _selected_dir_reader
1723
1803
    if _selected_dir_reader is None:
1724
 
        fs_encoding = _fs_enc.upper()
1725
1804
        if sys.platform == "win32" and win32utils.winver == 'Windows NT':
1726
1805
            # Win98 doesn't have unicode apis like FindFirstFileW
1727
1806
            # TODO: We possibly could support Win98 by falling back to the
1733
1812
                _selected_dir_reader = Win32ReadDir()
1734
1813
            except ImportError:
1735
1814
                pass
1736
 
        elif fs_encoding in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968'):
1737
 
            # ANSI_X3.4-1968 is a form of ASCII
 
1815
        elif _fs_enc in ('utf-8', 'ascii'):
1738
1816
            try:
1739
1817
                from bzrlib._readdir_pyx import UTF8DirReader
1740
1818
                _selected_dir_reader = UTF8DirReader()
1875
1953
        s = os.stat(src)
1876
1954
        chown(dst, s.st_uid, s.st_gid)
1877
1955
    except OSError, e:
1878
 
        trace.warning("Unable to copy ownership from '%s' to '%s': IOError: %s." % (src, dst, e))
 
1956
        trace.warning(
 
1957
            'Unable to copy ownership from "%s" to "%s". '
 
1958
            'You may want to set it manually.', src, dst)
 
1959
        trace.log_exception_quietly()
1879
1960
 
1880
1961
 
1881
1962
def path_prefix_key(path):
1973
2054
    return get_terminal_encoding()
1974
2055
 
1975
2056
 
 
2057
_message_encoding = None
 
2058
 
 
2059
 
 
2060
def get_message_encoding():
 
2061
    """Return the encoding used for messages
 
2062
 
 
2063
    While the message encoding is a general setting it should usually only be
 
2064
    needed for decoding system error strings such as from OSError instances.
 
2065
    """
 
2066
    global _message_encoding
 
2067
    if _message_encoding is None:
 
2068
        if os.name == "posix":
 
2069
            import locale
 
2070
            # This is a process-global setting that can change, but should in
 
2071
            # general just get set once at process startup then be constant.
 
2072
            _message_encoding = locale.getlocale(locale.LC_MESSAGES)[1]
 
2073
        else:
 
2074
            # On windows want the result of GetACP() which this boils down to.
 
2075
            _message_encoding = get_user_encoding()
 
2076
    return _message_encoding or "ascii"
 
2077
        
 
2078
 
1976
2079
def get_host_name():
1977
2080
    """Return the current unicode host name.
1978
2081
 
1993
2096
# data at once.
1994
2097
MAX_SOCKET_CHUNK = 64 * 1024
1995
2098
 
 
2099
_end_of_stream_errors = [errno.ECONNRESET]
 
2100
for _eno in ['WSAECONNRESET', 'WSAECONNABORTED']:
 
2101
    _eno = getattr(errno, _eno, None)
 
2102
    if _eno is not None:
 
2103
        _end_of_stream_errors.append(_eno)
 
2104
del _eno
 
2105
 
 
2106
 
1996
2107
def read_bytes_from_socket(sock, report_activity=None,
1997
2108
        max_read_size=MAX_SOCKET_CHUNK):
1998
2109
    """Read up to max_read_size of bytes from sock and notify of progress.
2006
2117
            bytes = sock.recv(max_read_size)
2007
2118
        except socket.error, e:
2008
2119
            eno = e.args[0]
2009
 
            if eno == getattr(errno, "WSAECONNRESET", errno.ECONNRESET):
 
2120
            if eno in _end_of_stream_errors:
2010
2121
                # The connection was closed by the other side.  Callers expect
2011
2122
                # an empty string to signal end-of-stream.
2012
2123
                return ""
2065
2176
            report_activity(sent, 'write')
2066
2177
 
2067
2178
 
 
2179
def connect_socket(address):
 
2180
    # Slight variation of the socket.create_connection() function (provided by
 
2181
    # python-2.6) that can fail if getaddrinfo returns an empty list. We also
 
2182
    # provide it for previous python versions. Also, we don't use the timeout
 
2183
    # parameter (provided by the python implementation) so we don't implement
 
2184
    # it either).
 
2185
    err = socket.error('getaddrinfo returns an empty list')
 
2186
    host, port = address
 
2187
    for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
 
2188
        af, socktype, proto, canonname, sa = res
 
2189
        sock = None
 
2190
        try:
 
2191
            sock = socket.socket(af, socktype, proto)
 
2192
            sock.connect(sa)
 
2193
            return sock
 
2194
 
 
2195
        except socket.error, err:
 
2196
            # 'err' is now the most recent error
 
2197
            if sock is not None:
 
2198
                sock.close()
 
2199
    raise err
 
2200
 
 
2201
 
2068
2202
def dereference_path(path):
2069
2203
    """Determine the real path to a file.
2070
2204
 
2130
2264
    return file_kind_from_stat_mode(mode)
2131
2265
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
2132
2266
 
2133
 
 
2134
 
def file_kind(f, _lstat=os.lstat):
 
2267
def file_stat(f, _lstat=os.lstat):
2135
2268
    try:
2136
 
        return file_kind_from_stat_mode(_lstat(f).st_mode)
 
2269
        # XXX cache?
 
2270
        return _lstat(f)
2137
2271
    except OSError, e:
2138
2272
        if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
2139
2273
            raise errors.NoSuchFile(f)
2140
2274
        raise
2141
2275
 
 
2276
def file_kind(f, _lstat=os.lstat):
 
2277
    stat_value = file_stat(f, _lstat)
 
2278
    return file_kind_from_stat_mode(stat_value.st_mode)
2142
2279
 
2143
2280
def until_no_eintr(f, *a, **kw):
2144
2281
    """Run f(*a, **kw), retrying if an EINTR error occurs.
2188
2325
 
2189
2326
 
2190
2327
if sys.platform == "win32":
2191
 
    import msvcrt
2192
2328
    def getchar():
 
2329
        import msvcrt
2193
2330
        return msvcrt.getch()
2194
2331
else:
2195
 
    import tty
2196
 
    import termios
2197
2332
    def getchar():
 
2333
        import tty
 
2334
        import termios
2198
2335
        fd = sys.stdin.fileno()
2199
2336
        settings = termios.tcgetattr(fd)
2200
2337
        try:
2204
2341
            termios.tcsetattr(fd, termios.TCSADRAIN, settings)
2205
2342
        return ch
2206
2343
 
2207
 
 
2208
 
if sys.platform == 'linux2':
 
2344
if sys.platform.startswith('linux'):
2209
2345
    def _local_concurrency():
2210
 
        concurrency = None
2211
 
        prefix = 'processor'
2212
 
        for line in file('/proc/cpuinfo', 'rb'):
2213
 
            if line.startswith(prefix):
2214
 
                concurrency = int(line[line.find(':')+1:]) + 1
2215
 
        return concurrency
 
2346
        try:
 
2347
            return os.sysconf('SC_NPROCESSORS_ONLN')
 
2348
        except (ValueError, OSError, AttributeError):
 
2349
            return None
2216
2350
elif sys.platform == 'darwin':
2217
2351
    def _local_concurrency():
2218
2352
        return subprocess.Popen(['sysctl', '-n', 'hw.availcpu'],
2219
2353
                                stdout=subprocess.PIPE).communicate()[0]
2220
 
elif sys.platform[0:7] == 'freebsd':
 
2354
elif "bsd" in sys.platform:
2221
2355
    def _local_concurrency():
2222
2356
        return subprocess.Popen(['sysctl', '-n', 'hw.ncpu'],
2223
2357
                                stdout=subprocess.PIPE).communicate()[0]
2251
2385
    concurrency = os.environ.get('BZR_CONCURRENCY', None)
2252
2386
    if concurrency is None:
2253
2387
        try:
2254
 
            concurrency = _local_concurrency()
2255
 
        except (OSError, IOError):
2256
 
            pass
 
2388
            import multiprocessing
 
2389
            concurrency = multiprocessing.cpu_count()
 
2390
        except (ImportError, NotImplementedError):
 
2391
            # multiprocessing is only available on Python >= 2.6
 
2392
            # and multiprocessing.cpu_count() isn't implemented on all
 
2393
            # platforms
 
2394
            try:
 
2395
                concurrency = _local_concurrency()
 
2396
            except (OSError, IOError):
 
2397
                pass
2257
2398
    try:
2258
2399
        concurrency = int(concurrency)
2259
2400
    except (TypeError, ValueError):
2321
2462
    open_file = open
2322
2463
 
2323
2464
 
2324
 
def getuser_unicode():
2325
 
    """Return the username as unicode.
2326
 
    """
2327
 
    try:
2328
 
        user_encoding = get_user_encoding()
2329
 
        username = getpass.getuser().decode(user_encoding)
2330
 
    except UnicodeDecodeError:
2331
 
        raise errors.BzrError("Can't decode username as %s." % \
2332
 
                user_encoding)
2333
 
    return username
 
2465
def available_backup_name(base, exists):
 
2466
    """Find a non-existing backup file name.
 
2467
 
 
2468
    This will *not* create anything, this only return a 'free' entry.  This
 
2469
    should be used for checking names in a directory below a locked
 
2470
    tree/branch/repo to avoid race conditions. This is LBYL (Look Before You
 
2471
    Leap) and generally discouraged.
 
2472
 
 
2473
    :param base: The base name.
 
2474
 
 
2475
    :param exists: A callable returning True if the path parameter exists.
 
2476
    """
 
2477
    counter = 1
 
2478
    name = "%s.~%d~" % (base, counter)
 
2479
    while exists(name):
 
2480
        counter += 1
 
2481
        name = "%s.~%d~" % (base, counter)
 
2482
    return name
 
2483
 
 
2484
 
 
2485
def set_fd_cloexec(fd):
 
2486
    """Set a Unix file descriptor's FD_CLOEXEC flag.  Do nothing if platform
 
2487
    support for this is not available.
 
2488
    """
 
2489
    try:
 
2490
        import fcntl
 
2491
        old = fcntl.fcntl(fd, fcntl.F_GETFD)
 
2492
        fcntl.fcntl(fd, fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
 
2493
    except (ImportError, AttributeError):
 
2494
        # Either the fcntl module or specific constants are not present
 
2495
        pass
 
2496
 
 
2497
 
 
2498
def find_executable_on_path(name):
 
2499
    """Finds an executable on the PATH.
 
2500
    
 
2501
    On Windows, this will try to append each extension in the PATHEXT
 
2502
    environment variable to the name, if it cannot be found with the name
 
2503
    as given.
 
2504
    
 
2505
    :param name: The base name of the executable.
 
2506
    :return: The path to the executable found or None.
 
2507
    """
 
2508
    path = os.environ.get('PATH')
 
2509
    if path is None:
 
2510
        return None
 
2511
    path = path.split(os.pathsep)
 
2512
    if sys.platform == 'win32':
 
2513
        exts = os.environ.get('PATHEXT', '').split(os.pathsep)
 
2514
        exts = [ext.lower() for ext in exts]
 
2515
        base, ext = os.path.splitext(name)
 
2516
        if ext != '':
 
2517
            if ext.lower() not in exts:
 
2518
                return None
 
2519
            name = base
 
2520
            exts = [ext]
 
2521
    else:
 
2522
        exts = ['']
 
2523
    for ext in exts:
 
2524
        for d in path:
 
2525
            f = os.path.join(d, name) + ext
 
2526
            if os.access(f, os.X_OK):
 
2527
                return f
 
2528
    return None
 
2529
 
 
2530
 
 
2531
def _posix_is_local_pid_dead(pid):
 
2532
    """True if pid doesn't correspond to live process on this machine"""
 
2533
    try:
 
2534
        # Special meaning of unix kill: just check if it's there.
 
2535
        os.kill(pid, 0)
 
2536
    except OSError, e:
 
2537
        if e.errno == errno.ESRCH:
 
2538
            # On this machine, and really not found: as sure as we can be
 
2539
            # that it's dead.
 
2540
            return True
 
2541
        elif e.errno == errno.EPERM:
 
2542
            # exists, though not ours
 
2543
            return False
 
2544
        else:
 
2545
            mutter("os.kill(%d, 0) failed: %s" % (pid, e))
 
2546
            # Don't really know.
 
2547
            return False
 
2548
    else:
 
2549
        # Exists and our process: not dead.
 
2550
        return False
 
2551
 
 
2552
if sys.platform == "win32":
 
2553
    is_local_pid_dead = win32utils.is_local_pid_dead
 
2554
else:
 
2555
    is_local_pid_dead = _posix_is_local_pid_dead
 
2556
 
 
2557
 
 
2558
def fdatasync(fileno):
 
2559
    """Flush file contents to disk if possible.
 
2560
    
 
2561
    :param fileno: Integer OS file handle.
 
2562
    :raises TransportNotPossible: If flushing to disk is not possible.
 
2563
    """
 
2564
    fn = getattr(os, 'fdatasync', getattr(os, 'fsync', None))
 
2565
    if fn is not None:
 
2566
        fn(fileno)
 
2567
 
 
2568
 
 
2569
def ensure_empty_directory_exists(path, exception_class):
 
2570
    """Make sure a local directory exists and is empty.
 
2571
    
 
2572
    If it does not exist, it is created.  If it exists and is not empty, an
 
2573
    instance of exception_class is raised.
 
2574
    """
 
2575
    try:
 
2576
        os.mkdir(path)
 
2577
    except OSError, e:
 
2578
        if e.errno != errno.EEXIST:
 
2579
            raise
 
2580
        if os.listdir(path) != []:
 
2581
            raise exception_class(path)
 
2582
 
 
2583
 
 
2584
def is_environment_error(evalue):
 
2585
    """True if exception instance is due to a process environment issue
 
2586
 
 
2587
    This includes OSError and IOError, but also other errors that come from
 
2588
    the operating system or core libraries but are not subclasses of those.
 
2589
    """
 
2590
    if isinstance(evalue, (EnvironmentError, select.error)):
 
2591
        return True
 
2592
    if sys.platform == "win32" and win32utils._is_pywintypes_error(evalue):
 
2593
        return True
 
2594
    return False