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
17
from __future__ import absolute_import
73
# Cross platform wall-clock time functionality with decent resolution.
74
# On Linux ``time.clock`` returns only CPU time. On Windows, ``time.time()``
75
# only has a resolution of ~15ms. Note that ``time.clock()`` is not
76
# synchronized with ``time.time()``, this is only meant to be used to find
77
# delta times by subtracting from another call to this function.
78
timer_func = time.time
79
if sys.platform == 'win32':
80
timer_func = time.clock
82
66
# On win32, O_BINARY is used to indicate the file should
83
67
# be opened in binary mode, rather than text mode.
84
68
# On other platforms, O_BINARY doesn't exist, because
102
86
def get_unicode_argv():
106
user_encoding = get_user_encoding()
107
return [a.decode(user_encoding) for a in sys.argv[1:]]
108
except UnicodeDecodeError:
109
raise errors.BzrError(gettext("Parameter {0!r} encoding is unsupported by {1} "
110
"application locale.").format(a, user_encoding))
113
90
def make_readonly(filename):
225
206
except OSError as e:
226
207
if e.errno == errno.ENOENT:
229
raise errors.BzrError(gettext("lstat/stat of ({0!r}): {1!r}").format(f, e))
210
raise errors.BzrError(
211
gettext("lstat/stat of ({0!r}): {1!r}").format(f, e))
232
214
def fancy_rename(old, new, rename_func, unlink_func):
256
238
file_existed = False
258
240
rename_func(new, tmp_name)
259
except (errors.NoSuchFile,) as e:
241
except (errors.NoSuchFile,):
261
243
except IOError as e:
262
244
# RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
267
249
except Exception as e:
268
250
if (getattr(e, 'errno', None) is None
269
or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
251
or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
272
254
file_existed = True
282
264
# case-insensitive filesystem), so we may have accidentally renamed
283
265
# source by when we tried to rename target
284
266
if (file_existed and e.errno in (None, errno.ENOENT)
285
and old.lower() == new.lower()):
267
and old.lower() == new.lower()):
286
268
# source and target are the same file on a case-insensitive
287
269
# filesystem, so we don't generate an exception
323
305
# as a special case here by simply removing the first slash, as we consider
324
306
# that breaking POSIX compatibility for this obscure feature is acceptable.
325
307
# This is not a paranoid precaution, as we notably get paths like this when
326
# the repo is hosted at the root of the filesystem, i.e. in "/".
308
# the repo is hosted at the root of the filesystem, i.e. in "/".
327
309
if path.startswith('//'):
335
317
Note that posix systems use arbitrary byte strings for filesystem objects,
336
318
so a path that raises BadFilenameEncoding here may still be accessible.
338
val = os.environ.get(key, None)
339
if PY3 or val is None:
342
return val.decode(_fs_enc)
343
except UnicodeDecodeError:
344
# GZ 2011-12-12:Ideally want to include `key` in the exception message
345
raise errors.BadFilenameEncoding(val, _fs_enc)
320
return os.environ.get(key, None)
348
323
def _posix_get_home_dir():
359
334
def _posix_getuser_unicode():
360
335
"""Get username from environment or password database as unicode"""
361
name = getpass.getuser()
364
user_encoding = get_user_encoding()
366
return name.decode(user_encoding)
367
except UnicodeDecodeError:
368
raise errors.BzrError("Encoding of username %r is unsupported by %s "
369
"application locale." % (name, user_encoding))
336
return getpass.getuser()
372
339
def _win32_fixdrive(path):
385
352
def _win32_abspath(path):
386
353
# Real ntpath.abspath doesn't have a problem with a unicode cwd
387
return _win32_fixdrive(ntpath.abspath(unicode(path)).replace('\\', '/'))
354
return _win32_fixdrive(ntpath.abspath(path).replace('\\', '/'))
390
357
def _win32_realpath(path):
391
358
# Real ntpath.realpath doesn't have a problem with a unicode cwd
392
return _win32_fixdrive(ntpath.realpath(unicode(path)).replace('\\', '/'))
359
return _win32_fixdrive(ntpath.realpath(path).replace('\\', '/'))
395
362
def _win32_pathjoin(*args):
442
409
rename_func(old, new)
443
410
except OSError as e:
444
411
detailed_error = OSError(e.errno, e.strerror +
445
" [occurred when renaming '%s' to '%s']" %
412
" [occurred when renaming '%s' to '%s']" %
447
414
detailed_error.old_filename = old
448
415
detailed_error.new_filename = new
449
416
raise detailed_error
513
478
exception = excinfo[1]
514
479
if function in (os.remove, os.rmdir) \
515
and isinstance(exception, OSError) \
516
and exception.errno == errno.EACCES:
480
and isinstance(exception, OSError) \
481
and exception.errno == errno.EACCES:
517
482
make_writable(path)
523
488
"""Replacer for shutil.rmtree: could remove readonly dirs/files"""
524
489
return shutil.rmtree(path, ignore_errors, onerror)
526
f = win32utils.get_unicode_argv # special function or None
491
get_unicode_argv = getattr(win32utils, 'get_unicode_argv', get_unicode_argv)
529
492
path_from_environ = win32utils.get_environ_unicode
530
493
_get_home_dir = win32utils.get_home_location
531
494
getuser_unicode = win32utils.get_user_name
557
520
output_encoding = get_user_encoding()
559
522
mutter('encoding stdout as osutils.get_user_encoding() %r',
562
525
output_encoding = input_encoding
564
527
mutter('encoding stdout as sys.stdin encoding %r',
568
531
mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
571
534
output_encoding = get_user_encoding()
573
536
mutter('cp0 is invalid encoding.'
574
' encoding stdout as osutils.get_user_encoding() %r',
537
' encoding stdout as osutils.get_user_encoding() %r',
578
541
codecs.lookup(output_encoding)
719
688
# writes fail on some platforms (e.g. Windows with SMB mounted
721
690
if not segment_size:
722
segment_size = 5242880 # 5MB
691
segment_size = 5242880 # 5MB
723
692
offsets = range(0, len(bytes), segment_size)
724
693
view = memoryview(bytes)
725
694
write = file_handle.write
726
695
for offset in offsets:
727
write(view[offset:offset+segment_size])
696
write(view[offset:offset + segment_size])
730
699
def file_iterator(input_file, readsize=32768):
738
707
# GZ 2017-09-16: Makes sense in general for hexdigest() result to be text, but
739
708
# used as bytes through most interfaces so encode with this wrapper.
741
def _hexdigest(hashobj):
742
return hashobj.hexdigest().encode()
744
def _hexdigest(hashobj):
745
return hashobj.hexdigest()
709
def _hexdigest(hashobj):
710
return hashobj.hexdigest().encode()
830
795
offset = datetime.fromtimestamp(t) - datetime.utcfromtimestamp(t)
831
796
return offset.days * 86400 + offset.seconds
833
799
weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
834
800
_default_format_by_weekday_num = [wd + " %Y-%m-%d %H:%M:%S" for wd in weekdays]
847
813
:param show_offset: Whether to append the timezone.
849
815
(date_fmt, tt, offset_str) = \
850
_format_date(t, offset, timezone, date_fmt, show_offset)
816
_format_date(t, offset, timezone, date_fmt, show_offset)
851
817
date_fmt = date_fmt.replace('%a', weekdays[tt[6]])
852
818
date_str = time.strftime(date_fmt, tt)
853
819
return date_str + offset_str
891
857
:param show_offset: Whether to append the timezone.
893
859
(date_fmt, tt, offset_str) = \
894
_format_date(t, offset, timezone, date_fmt, show_offset)
860
_format_date(t, offset, timezone, date_fmt, show_offset)
895
861
date_str = time.strftime(date_fmt, tt)
896
if not isinstance(date_str, text_type):
862
if not isinstance(date_str, str):
897
863
date_str = date_str.decode(get_user_encoding(), 'replace')
898
864
return date_str + offset_str
952
918
plural_seconds = ''
954
920
plural_seconds = 's'
955
if minutes < 90: # print minutes, seconds up to 90 minutes
921
if minutes < 90: # print minutes, seconds up to 90 minutes
957
923
return '%d minute, %d second%s %s' % (
958
minutes, seconds, plural_seconds, direction)
924
minutes, seconds, plural_seconds, direction)
960
926
return '%d minutes, %d second%s %s' % (
961
minutes, seconds, plural_seconds, direction)
927
minutes, seconds, plural_seconds, direction)
963
929
hours = int(minutes / 60)
964
930
minutes -= 60 * hours
973
939
return '%d hours, %d minute%s %s' % (hours, minutes,
974
940
plural_minutes, direction)
977
944
"""Return size of given open file."""
978
945
return os.fstat(f.fileno())[stat.ST_SIZE]
981
# Alias os.urandom to support platforms (which?) without /dev/urandom and
948
# Alias os.urandom to support platforms (which?) without /dev/urandom and
982
949
# override if it doesn't work. Avoid checking on windows where there is
983
950
# significant initialisation cost that can be avoided for some bzr calls.
1009
978
for raw_byte in rand_bytes(num):
1011
s += ALNUM[ord(raw_byte) % 36]
1013
s += ALNUM[raw_byte % 36]
979
s += ALNUM[raw_byte % 36]
1017
## TODO: We could later have path objects that remember their list
1018
## decomposition (might be too tricksy though.)
983
# TODO: We could later have path objects that remember their list
984
# decomposition (might be too tricksy though.)
1020
986
def splitpath(p):
1021
987
"""Turn string into list of parts."""
1022
# split on either delimiter because people might use either on
1024
ps = re.split(r'[\\/]', p)
988
use_bytes = isinstance(p, bytes)
989
if os.path.sep == '\\':
990
# split on either delimiter because people might use either on
993
ps = re.split(b'[\\\\/]', p)
995
ps = re.split(r'[\\/]', p)
1004
current_empty_dir = (b'.', b'')
1007
current_empty_dir = ('.', '')
1029
1012
raise errors.BzrError(gettext("sorry, %r not allowed in path") % f)
1030
elif (f == '.') or (f == ''):
1013
elif f in current_empty_dir:
1110
1093
"""Split s into lines, but without removing the newline characters."""
1111
1094
# Trivially convert a fulltext into a 'chunked' representation, and let
1112
1095
# chunks_to_lines do the heavy lifting.
1113
if isinstance(s, str):
1096
if isinstance(s, bytes):
1114
1097
# chunks_to_lines only supports 8-bit strings
1115
1098
return chunks_to_lines([s])
1153
1136
Will delete even if readonly.
1156
_delete_file_or_dir(path)
1139
_delete_file_or_dir(path)
1157
1140
except (OSError, IOError) as e:
1158
1141
if e.errno in (errno.EPERM, errno.EACCES):
1159
1142
# make writable and try again
1172
1155
# - root can damage a solaris file system by using unlink,
1173
1156
# - unlink raises different exceptions on different OSes (linux: EISDIR, win32:
1174
1157
# EACCES, OSX: EPERM) when invoked on a directory.
1175
if isdir(path): # Takes care of symlinks
1158
if isdir(path): # Takes care of symlinks
1178
1161
os.unlink(path)
1301
1284
abs_base = abspath(base)
1302
1285
current = abs_base
1303
_listdir = os.listdir
1305
1287
# use an explicit iterator so we can easily consume the rest on early exit.
1306
1288
bit_iter = iter(rel.split('/'))
1307
1289
for bit in bit_iter:
1308
1290
lbit = bit.lower()
1310
next_entries = _listdir(current)
1311
except OSError: # enoent, eperm, etc
1292
next_entries = scandir(current)
1293
except OSError: # enoent, eperm, etc
1312
1294
# We can't find this in the filesystem, so just append the
1313
1295
# remaining bits.
1314
1296
current = pathjoin(current, bit, *list(bit_iter))
1316
for look in next_entries:
1317
if lbit == look.lower():
1318
current = pathjoin(current, look)
1298
for entry in next_entries:
1299
if lbit == entry.name.lower():
1300
current = entry.path
1321
1303
# got to the end, nothing matched, so we just return the
1326
1308
return current[len(abs_base):].lstrip('/')
1328
1311
# XXX - TODO - we need better detection/integration of case-insensitive
1329
1312
# file-systems; Linux often sees FAT32 devices (or NFS-mounted OSX
1330
1313
# filesystems), for example, so could probably benefit from the same basic
1352
1336
Otherwise it is decoded from the the filesystem's encoding. If decoding
1353
1337
fails, a errors.BadFilenameEncoding exception is raised.
1355
if isinstance(filename, text_type):
1339
if isinstance(filename, str):
1356
1340
return filename
1358
1342
return filename.decode(_fs_enc)
1367
1351
Otherwise it is decoded from utf-8. If decoding fails, the exception is
1368
1352
wrapped in a BzrBadParameterNotUnicode exception.
1370
if isinstance(unicode_or_utf8_string, text_type):
1354
if isinstance(unicode_or_utf8_string, str):
1371
1355
return unicode_or_utf8_string
1373
1357
return unicode_or_utf8_string.decode('utf8')
1402
1386
:return: None or a utf8 revision id.
1404
1388
if (unicode_or_utf8_string is None
1405
or unicode_or_utf8_string.__class__ == bytes):
1389
or unicode_or_utf8_string.__class__ == bytes):
1406
1390
return unicode_or_utf8_string
1407
1391
raise TypeError('Unicode revision ids are no longer supported. '
1408
1392
'Revision id generators should be creating utf8 revision '
1420
1404
:return: None or a utf8 file id.
1422
1406
if (unicode_or_utf8_string is None
1423
or unicode_or_utf8_string.__class__ == bytes):
1407
or unicode_or_utf8_string.__class__ == bytes):
1424
1408
return unicode_or_utf8_string
1425
1409
raise TypeError('Unicode file ids are no longer supported. '
1426
1410
'File id generators should be creating utf8 file ids.')
1454
1438
can be accessed by that path.
1457
return unicodedata.normalize('NFC', text_type(path)), True
1441
if isinstance(path, bytes):
1442
path = path.decode(sys.getfilesystemencoding())
1443
return unicodedata.normalize('NFC', path), True
1460
1446
def _inaccessible_normalized_filename(path):
1461
1447
__doc__ = _accessible_normalized_filename.__doc__
1463
normalized = unicodedata.normalize('NFC', text_type(path))
1449
if isinstance(path, bytes):
1450
path = path.decode(sys.getfilesystemencoding())
1451
normalized = unicodedata.normalize('NFC', path)
1464
1452
return normalized, normalized == path
1489
1477
except AttributeError:
1490
1478
# siginterrupt doesn't exist on this platform, or for this version
1492
siginterrupt = lambda signum, flag: None
1480
def siginterrupt(signum, flag): return None
1493
1481
if restart_syscall:
1494
1482
def sig_handler(*args):
1495
1483
# Python resets the siginterrupt flag when a signal is
1608
1597
def _win32_terminal_size(width, height):
1609
width, height = win32utils.get_console_size(defaultx=width, defaulty=height)
1598
width, height = win32utils.get_console_size(
1599
defaultx=width, defaulty=height)
1610
1600
return width, height
1613
1603
def _ioctl_terminal_size(width, height):
1615
import struct, fcntl, termios
1616
1608
s = struct.pack('HHHH', 0, 0, 0, 0)
1617
1609
x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
1618
1610
height, width = struct.unpack('HHHH', x)[0:2]
1635
1628
_terminal_size = _ioctl_terminal_size
1638
def supports_executable():
1639
return sys.platform != "win32"
1631
def supports_executable(path):
1632
"""Return if filesystem at path supports executable bit.
1634
:param path: Path for which to check the file system
1635
:return: boolean indicating whether executable bit can be stored/relied upon
1637
if sys.platform == 'win32':
1640
fs_type = get_fs_type(path)
1641
except errors.DependencyNotPresent as e:
1642
trace.mutter('Unable to get fs type for %r: %s', path, e)
1644
if fs_type in ('vfat', 'ntfs'):
1645
# filesystems known to not support executable bit
1650
def supports_symlinks(path):
1651
"""Return if the filesystem at path supports the creation of symbolic links.
1654
if not has_symlinks():
1657
fs_type = get_fs_type(path)
1658
except errors.DependencyNotPresent as e:
1659
trace.mutter('Unable to get fs type for %r: %s', path, e)
1661
if fs_type in ('vfat', 'ntfs'):
1662
# filesystems known to not support symlinks
1642
1667
def supports_posix_readonly():
1685
1708
raise errors.IllegalPath(path)
1688
_WIN32_ERROR_DIRECTORY = 267 # Similar to errno.ENOTDIR
1711
_WIN32_ERROR_DIRECTORY = 267 # Similar to errno.ENOTDIR
1715
scandir = os.scandir
1716
except AttributeError: # Python < 3
1717
lazy_import(globals(), """\
1718
from scandir import scandir
1690
1722
def _is_error_enotdir(e):
1691
1723
"""Check if this exception represents ENOTDIR.
1703
1735
:return: True if this represents an ENOTDIR error. False otherwise.
1705
1737
en = getattr(e, 'errno', None)
1706
if (en == errno.ENOTDIR
1707
or (sys.platform == 'win32'
1708
and (en == _WIN32_ERROR_DIRECTORY
1709
or (en == errno.EINVAL
1710
and getattr(e, 'winerror', None) == _WIN32_ERROR_DIRECTORY)
1738
if (en == errno.ENOTDIR or
1739
(sys.platform == 'win32' and
1740
(en == _WIN32_ERROR_DIRECTORY or
1742
and getattr(e, 'winerror', None) == _WIN32_ERROR_DIRECTORY)
1741
1773
rooted higher up.
1742
1774
:return: an iterator over the dirs.
1744
#TODO there is a bit of a smell where the results of the directory-
1776
# TODO there is a bit of a smell where the results of the directory-
1745
1777
# summary in this, and the path from the root, may not agree
1746
1778
# depending on top and prefix - i.e. ./foo and foo as a pair leads to
1747
1779
# potentially confusing output. We should make this more robust - but
1748
1780
# not at a speed cost. RBC 20060731
1750
1781
_directory = _directory_kind
1751
_listdir = os.listdir
1752
_kind_from_mode = file_kind_from_stat_mode
1753
1782
pending = [(safe_unicode(prefix), "", _directory, None, safe_unicode(top))]
1755
1784
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1761
1790
top_slash = top + u'/'
1764
append = dirblock.append
1766
names = sorted(map(decode_filename, _listdir(top)))
1794
for entry in scandir(top):
1795
name = decode_filename(entry.name)
1796
statvalue = entry.stat(follow_symlinks=False)
1797
kind = file_kind_from_stat_mode(statvalue.st_mode)
1798
dirblock.append((relprefix + name, name, kind, statvalue, entry.path))
1767
1799
except OSError as e:
1768
1800
if not _is_error_enotdir(e):
1772
abspath = top_slash + name
1773
statvalue = _lstat(abspath)
1774
kind = _kind_from_mode(statvalue.st_mode)
1775
append((relprefix + name, name, kind, statvalue, abspath))
1802
except UnicodeDecodeError as e:
1803
raise errors.BadFilenameEncoding(e.object, _fs_enc)
1776
1805
yield (relroot, top), dirblock
1778
1807
# push the user specified dirs from dirblock
1884
1913
See DirReader.read_dir for details.
1886
1915
_utf8_encode = self._utf8_encode
1888
_listdir = os.listdir
1889
_kind_from_mode = file_kind_from_stat_mode
1917
def _fs_decode(s): return s.decode(_fs_enc)
1919
def _fs_encode(s): return s.encode(_fs_enc)
1892
1922
relprefix = prefix + b'/'
1898
1928
append = dirblock.append
1899
for name in sorted(_listdir(top)):
1929
for entry in scandir(safe_utf8(top)):
1901
name_utf8 = _utf8_encode(name)[0]
1931
name = _fs_decode(entry.name)
1902
1932
except UnicodeDecodeError:
1903
1933
raise errors.BadFilenameEncoding(
1904
_utf8_encode(relprefix)[0] + name, _fs_enc)
1934
relprefix + entry.name, _fs_enc)
1905
1935
abspath = top_slash + name
1906
statvalue = _lstat(abspath)
1907
kind = _kind_from_mode(statvalue.st_mode)
1936
name_utf8 = _utf8_encode(name)[0]
1937
statvalue = entry.stat(follow_symlinks=False)
1938
kind = file_kind_from_stat_mode(statvalue.st_mode)
1908
1939
append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
1940
return sorted(dirblock)
1912
1943
def copy_tree(from_path, to_path, handlers={}):
1970
2001
s = os.stat(src)
1971
2002
chown(dst, s.st_uid, s.st_gid)
1972
except OSError as e:
1974
2005
'Unable to copy ownership from "%s" to "%s". '
1975
2006
'You may want to set it manually.', src, dst)
1988
2019
"""Compare path_a and path_b to generate the same order walkdirs uses."""
1989
2020
key_a = path_prefix_key(path_a)
1990
2021
key_b = path_prefix_key(path_b)
1991
return cmp(key_a, key_b)
2022
return (key_a > key_b) - (key_a < key_b)
1994
2025
_cached_user_encoding = None
2055
2086
return win32utils.get_host_name()
2059
return socket.gethostname()
2060
return socket.gethostname().decode(get_user_encoding())
2089
return socket.gethostname()
2063
2092
# We must not read/write any more than 64k at a time from/to a socket so we
2077
2106
def read_bytes_from_socket(sock, report_activity=None,
2078
max_read_size=MAX_SOCKET_CHUNK):
2107
max_read_size=MAX_SOCKET_CHUNK):
2079
2108
"""Read up to max_read_size of bytes from sock and notify of progress.
2081
2110
Translates "Connection reset by peer" into file-like EOF (return an
2087
bytes = sock.recv(max_read_size)
2116
data = sock.recv(max_read_size)
2088
2117
except socket.error as e:
2089
2118
eno = e.args[0]
2090
2119
if eno in _end_of_stream_errors:
2091
2120
# The connection was closed by the other side. Callers expect
2092
2121
# an empty string to signal end-of-stream.
2094
2123
elif eno == errno.EINTR:
2095
2124
# Retry the interrupted recv.
2099
2128
if report_activity is not None:
2100
report_activity(len(bytes), 'read')
2129
report_activity(len(data), 'read')
2104
2133
def recv_all(socket, count):
2138
2167
view = memoryview(bytes)
2139
2168
while sent_total < byte_count:
2141
sent = sock.send(view[sent_total:sent_total+MAX_SOCKET_CHUNK])
2170
sent = sock.send(view[sent_total:sent_total + MAX_SOCKET_CHUNK])
2142
2171
except (socket.error, IOError) as e:
2143
2172
if e.args[0] in _end_of_stream_errors:
2144
2173
raise errors.ConnectionReset(
2221
2251
base = dirname(breezy.__file__)
2222
2252
if getattr(sys, 'frozen', None): # bzr.exe
2223
2253
base = abspath(pathjoin(base, '..', '..'))
2224
f = file(pathjoin(base, resource_relpath), "rU")
2254
with open(pathjoin(base, resource_relpath), "rt") as f:
2226
2255
return f.read()
2230
2258
def file_kind_from_stat_mode_thunk(mode):
2231
2259
global file_kind_from_stat_mode
2234
2262
from ._readdir_pyx import UTF8DirReader
2235
2263
file_kind_from_stat_mode = UTF8DirReader().kind_from_mode
2236
except ImportError as e:
2237
2265
# This is one time where we won't warn that an extension failed to
2238
2266
# load. The extension is never available on Windows anyway.
2239
2267
from ._readdir_py import (
2240
2268
_kind_from_mode as file_kind_from_stat_mode
2242
2270
return file_kind_from_stat_mode(mode)
2243
2273
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
2245
2276
def file_stat(f, _lstat=os.lstat):
2251
2282
raise errors.NoSuchFile(f)
2254
2286
def file_kind(f, _lstat=os.lstat):
2255
2287
stat_value = file_stat(f, _lstat)
2256
2288
return file_kind_from_stat_mode(stat_value.st_mode)
2258
2291
def until_no_eintr(f, *a, **kw):
2259
2292
"""Run f(*a, **kw), retrying if an EINTR error occurs.
2311
2344
stdout=subprocess.PIPE).communicate()[0]
2312
2345
elif sys.platform == 'sunos5':
2313
2346
def _local_concurrency():
2314
return subprocess.Popen(['psrinfo', '-p',],
2347
return subprocess.Popen(['psrinfo', '-p', ],
2315
2348
stdout=subprocess.PIPE).communicate()[0]
2316
2349
elif sys.platform == "win32":
2317
2350
def _local_concurrency():
2370
2404
data, _ = self.encode(object, self.errors)
2371
2405
self.stream.write(data)
2373
2408
if sys.platform == 'win32':
2374
2409
def open_file(filename, mode='r', bufsize=-1):
2375
2410
"""This function is used to override the ``open`` builtin.
2450
2485
def find_executable_on_path(name):
2451
2486
"""Finds an executable on the PATH.
2453
2488
On Windows, this will try to append each extension in the PATHEXT
2454
2489
environment variable to the name, if it cannot be found with the name
2457
2492
:param name: The base name of the executable.
2458
2493
:return: The path to the executable found or None.
2497
2532
# exists, though not ours
2500
mutter("os.kill(%d, 0) failed: %s" % (pid, e))
2535
trace.mutter("os.kill(%d, 0) failed: %s" % (pid, e))
2501
2536
# Don't really know.
2504
2539
# Exists and our process: not dead.
2507
2543
if sys.platform == "win32":
2508
2544
is_local_pid_dead = win32utils.is_local_pid_dead
2537
2573
def ensure_empty_directory_exists(path, exception_class):
2538
2574
"""Make sure a local directory exists and is empty.
2540
2576
If it does not exist, it is created. If it exists and is not empty, an
2541
2577
instance of exception_class is raised.
2560
2596
if sys.platform == "win32" and win32utils._is_pywintypes_error(evalue):
2601
def read_mtab(path):
2602
"""Read an fstab-style file and extract mountpoint+filesystem information.
2604
:param path: Path to read from
2605
:yield: Tuples with mountpoints (as bytestrings) and filesystem names
2607
with open(path, 'rb') as f:
2609
if line.startswith(b'#'):
2614
yield cols[1], cols[2].decode('ascii', 'replace')
2617
MTAB_PATH = '/etc/mtab'
2619
class FilesystemFinder(object):
2620
"""Find the filesystem for a particular path."""
2622
def __init__(self, mountpoints):
2625
self._mountpoints = sorted(mountpoints, key=key, reverse=True)
2629
"""Create a FilesystemFinder from an mtab-style file.
2631
Note that this will silenty ignore mtab if it doesn't exist or can not
2634
# TODO(jelmer): Use inotify to be notified when /etc/mtab changes and
2635
# we need to re-read it.
2637
return cls(read_mtab(MTAB_PATH))
2638
except EnvironmentError as e:
2639
trace.mutter('Unable to read mtab: %s', e)
2642
def find(self, path):
2643
"""Find the filesystem used by a particular path.
2645
:param path: Path to find (bytestring or text type)
2646
:return: Filesystem name (as text type) or None, if the filesystem is
2649
for mountpoint, filesystem in self._mountpoints:
2650
if is_inside(mountpoint, path):
2655
_FILESYSTEM_FINDER = None
2658
def get_fs_type(path):
2659
"""Return the filesystem type for the partition a path is in.
2661
:param path: Path to search filesystem type for
2662
:return: A FS type, as string. E.g. "ext2"
2664
global _FILESYSTEM_FINDER
2665
if _FILESYSTEM_FINDER is None:
2666
_FILESYSTEM_FINDER = FilesystemFinder.from_mtab()
2668
if not isinstance(path, bytes):
2669
path = path.encode(_fs_enc)
2671
return _FILESYSTEM_FINDER.find(path)
2674
perf_counter = time.perf_counter