72
# Cross platform wall-clock time functionality with decent resolution.
73
# On Linux ``time.clock`` returns only CPU time. On Windows, ``time.time()``
74
# only has a resolution of ~15ms. Note that ``time.clock()`` is not
75
# synchronized with ``time.time()``, this is only meant to be used to find
76
# delta times by subtracting from another call to this function.
77
timer_func = time.time
78
if sys.platform == 'win32':
79
timer_func = time.clock
66
81
# On win32, O_BINARY is used to indicate the file should
67
82
# be opened in binary mode, rather than text mode.
74
89
O_NOINHERIT = getattr(os, 'O_NOINHERIT', 0)
77
class UnsupportedTimezoneFormat(errors.BzrError):
79
_fmt = ('Unsupported timezone format "%(timezone)s", '
80
'options are "utc", "original", "local".')
82
def __init__(self, timezone):
83
self.timezone = timezone
92
def get_unicode_argv():
96
user_encoding = get_user_encoding()
97
return [a.decode(user_encoding) for a in sys.argv[1:]]
98
except UnicodeDecodeError:
99
raise errors.BzrError(gettext("Parameter {0!r} encoding is unsupported by {1} "
100
"application locale.").format(a, user_encoding))
86
103
def make_readonly(filename):
202
215
except OSError as e:
203
216
if e.errno == errno.ENOENT:
206
raise errors.BzrError(
207
gettext("lstat/stat of ({0!r}): {1!r}").format(f, e))
219
raise errors.BzrError(gettext("lstat/stat of ({0!r}): {1!r}").format(f, e))
210
222
def fancy_rename(old, new, rename_func, unlink_func):
260
272
# case-insensitive filesystem), so we may have accidentally renamed
261
273
# source by when we tried to rename target
262
274
if (file_existed and e.errno in (None, errno.ENOENT)
263
and old.lower() == new.lower()):
275
and old.lower() == new.lower()):
264
276
# source and target are the same file on a case-insensitive
265
277
# filesystem, so we don't generate an exception
301
313
# as a special case here by simply removing the first slash, as we consider
302
314
# that breaking POSIX compatibility for this obscure feature is acceptable.
303
315
# This is not a paranoid precaution, as we notably get paths like this when
304
# the repo is hosted at the root of the filesystem, i.e. in "/".
316
# the repo is hosted at the root of the filesystem, i.e. in "/".
305
317
if path.startswith('//'):
322
def _posix_path_from_environ(key):
323
"""Get unicode path from `key` in environment or None if not present
325
Note that posix systems use arbitrary byte strings for filesystem objects,
326
so a path that raises BadFilenameEncoding here may still be accessible.
328
val = os.environ.get(key, None)
329
if PY3 or val is None:
332
return val.decode(_fs_enc)
333
except UnicodeDecodeError:
334
# GZ 2011-12-12:Ideally want to include `key` in the exception message
335
raise errors.BadFilenameEncoding(val, _fs_enc)
310
338
def _posix_get_home_dir():
311
339
"""Get the home directory of the current user as a unicode path"""
312
340
path = posixpath.expanduser("~")
321
349
def _posix_getuser_unicode():
322
350
"""Get username from environment or password database as unicode"""
323
return getpass.getuser()
351
name = getpass.getuser()
352
user_encoding = get_user_encoding()
354
return name.decode(user_encoding)
355
except UnicodeDecodeError:
356
raise errors.BzrError("Encoding of username %r is unsupported by %s "
357
"application locale." % (name, user_encoding))
326
360
def _win32_fixdrive(path):
339
373
def _win32_abspath(path):
340
374
# Real ntpath.abspath doesn't have a problem with a unicode cwd
341
return _win32_fixdrive(ntpath.abspath(path).replace('\\', '/'))
375
return _win32_fixdrive(ntpath.abspath(unicode(path)).replace('\\', '/'))
344
378
def _win32_realpath(path):
345
379
# Real ntpath.realpath doesn't have a problem with a unicode cwd
346
return _win32_fixdrive(ntpath.realpath(path).replace('\\', '/'))
380
return _win32_fixdrive(ntpath.realpath(unicode(path)).replace('\\', '/'))
349
383
def _win32_pathjoin(*args):
464
501
exception = excinfo[1]
465
502
if function in (os.remove, os.rmdir) \
466
and isinstance(exception, OSError) \
467
and exception.errno == errno.EACCES:
503
and isinstance(exception, OSError) \
504
and exception.errno == errno.EACCES:
468
505
make_writable(path)
474
511
"""Replacer for shutil.rmtree: could remove readonly dirs/files"""
475
512
return shutil.rmtree(path, ignore_errors, onerror)
514
f = win32utils.get_unicode_argv # special function or None
517
path_from_environ = win32utils.get_environ_unicode
477
518
_get_home_dir = win32utils.get_home_location
478
519
getuser_unicode = win32utils.get_user_name
504
545
output_encoding = get_user_encoding()
506
547
mutter('encoding stdout as osutils.get_user_encoding() %r',
509
550
output_encoding = input_encoding
511
552
mutter('encoding stdout as sys.stdin encoding %r',
515
556
mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
672
707
# writes fail on some platforms (e.g. Windows with SMB mounted
674
709
if not segment_size:
675
segment_size = 5242880 # 5MB
710
segment_size = 5242880 # 5MB
676
711
offsets = range(0, len(bytes), segment_size)
677
712
view = memoryview(bytes)
678
713
write = file_handle.write
679
714
for offset in offsets:
680
write(view[offset:offset + segment_size])
715
write(view[offset:offset+segment_size])
683
718
def file_iterator(input_file, readsize=32768):
747
776
for string in strings:
752
781
def sha_string(f, _factory=sha):
753
# GZ 2017-09-16: Dodgy if factory is ever not sha, probably shouldn't be.
754
return _hexdigest(_factory(f))
782
return _factory(f).hexdigest()
757
785
def fingerprint_file(f):
759
787
return {'size': len(b),
760
'sha1': _hexdigest(sha(b))}
788
'sha1': sha(b).hexdigest()}
763
791
def compare_files(a, b):
768
796
bi = b.read(BUFSIZE)
803
def gmtime(seconds=None):
804
"""Convert seconds since the Epoch to a time tuple expressing UTC (a.k.a.
805
GMT). When 'seconds' is not passed in, convert the current time instead.
806
Handy replacement for time.gmtime() buggy on Windows and 32-bit platforms.
809
seconds = time.time()
810
return (datetime(1970, 1, 1) + timedelta(seconds=seconds)).timetuple()
775
813
def local_time_offset(t=None):
776
814
"""Return offset of local zone from GMT, either at present or at time t."""
779
817
offset = datetime.fromtimestamp(t) - datetime.utcfromtimestamp(t)
780
818
return offset.days * 86400 + offset.seconds
783
820
weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
784
821
_default_format_by_weekday_num = [wd + " %Y-%m-%d %H:%M:%S" for wd in weekdays]
797
834
:param show_offset: Whether to append the timezone.
799
836
(date_fmt, tt, offset_str) = \
800
_format_date(t, offset, timezone, date_fmt, show_offset)
837
_format_date(t, offset, timezone, date_fmt, show_offset)
801
838
date_fmt = date_fmt.replace('%a', weekdays[tt[6]])
802
839
date_str = time.strftime(date_fmt, tt)
803
840
return date_str + offset_str
841
878
:param show_offset: Whether to append the timezone.
843
880
(date_fmt, tt, offset_str) = \
844
_format_date(t, offset, timezone, date_fmt, show_offset)
881
_format_date(t, offset, timezone, date_fmt, show_offset)
845
882
date_str = time.strftime(date_fmt, tt)
846
if not isinstance(date_str, str):
883
if not isinstance(date_str, text_type):
847
884
date_str = date_str.decode(get_user_encoding(), 'replace')
848
885
return date_str + offset_str
851
888
def _format_date(t, offset, timezone, date_fmt, show_offset):
852
889
if timezone == 'utc':
855
892
elif timezone == 'original':
856
893
if offset is None:
858
tt = time.gmtime(t + offset)
895
tt = gmtime(t + offset)
859
896
elif timezone == 'local':
860
897
tt = time.localtime(t)
861
898
offset = local_time_offset(t)
863
raise UnsupportedTimezoneFormat(timezone)
900
raise errors.UnsupportedTimezoneFormat(timezone)
864
901
if date_fmt is None:
865
902
date_fmt = "%a %Y-%m-%d %H:%M:%S"
902
939
plural_seconds = ''
904
941
plural_seconds = 's'
905
if minutes < 90: # print minutes, seconds up to 90 minutes
942
if minutes < 90: # print minutes, seconds up to 90 minutes
907
944
return '%d minute, %d second%s %s' % (
908
minutes, seconds, plural_seconds, direction)
945
minutes, seconds, plural_seconds, direction)
910
947
return '%d minutes, %d second%s %s' % (
911
minutes, seconds, plural_seconds, direction)
948
minutes, seconds, plural_seconds, direction)
913
950
hours = int(minutes / 60)
914
951
minutes -= 60 * hours
923
960
return '%d hours, %d minute%s %s' % (hours, minutes,
924
961
plural_minutes, direction)
928
964
"""Return size of given open file."""
929
965
return os.fstat(f.fileno())[stat.ST_SIZE]
932
# Alias os.urandom to support platforms (which?) without /dev/urandom and
968
# Alias os.urandom to support platforms (which?) without /dev/urandom and
933
969
# override if it doesn't work. Avoid checking on windows where there is
934
970
# significant initialisation cost that can be avoided for some bzr calls.
962
996
for raw_byte in rand_bytes(num):
963
s += ALNUM[raw_byte % 36]
997
s += ALNUM[ord(raw_byte) % 36]
967
# TODO: We could later have path objects that remember their list
968
# decomposition (might be too tricksy though.)
1001
## TODO: We could later have path objects that remember their list
1002
## decomposition (might be too tricksy though.)
970
1004
def splitpath(p):
971
1005
"""Turn string into list of parts."""
972
use_bytes = isinstance(p, bytes)
973
if os.path.sep == '\\':
974
# split on either delimiter because people might use either on
977
ps = re.split(b'[\\\\/]', p)
979
ps = re.split(r'[\\/]', p)
988
current_empty_dir = (b'.', b'')
991
current_empty_dir = ('.', '')
1006
# split on either delimiter because people might use either on
1008
ps = re.split(r'[\\/]', p)
996
1013
raise errors.BzrError(gettext("sorry, %r not allowed in path") % f)
997
elif f in current_empty_dir:
1014
elif (f == '.') or (f == ''):
1054
1071
def report_extension_load_failures():
1055
1072
if not _extension_load_failures:
1057
if config.GlobalConfig().suppress_warning('missing_extensions'):
1074
if config.GlobalStack().get('ignore_missing_extensions'):
1059
1076
# the warnings framework should by default show this only once
1060
1077
from .trace import warning
1062
1079
"brz: warning: some compiled extensions could not be loaded; "
1063
"see ``brz help missing-extensions``")
1080
"see <https://answers.launchpad.net/bzr/+faq/703>")
1064
1081
# we no longer show the specific missing extensions here, because it makes
1065
1082
# the message too long and scary - see
1066
1083
# https://bugs.launchpad.net/bzr/+bug/430529
1077
1094
"""Split s into lines, but without removing the newline characters."""
1078
1095
# Trivially convert a fulltext into a 'chunked' representation, and let
1079
1096
# chunks_to_lines do the heavy lifting.
1080
if isinstance(s, bytes):
1097
if isinstance(s, str):
1081
1098
# chunks_to_lines only supports 8-bit strings
1082
1099
return chunks_to_lines([s])
1120
1136
Will delete even if readonly.
1123
_delete_file_or_dir(path)
1139
_delete_file_or_dir(path)
1124
1140
except (OSError, IOError) as e:
1125
1141
if e.errno in (errno.EPERM, errno.EACCES):
1126
1142
# make writable and try again
1139
1155
# - root can damage a solaris file system by using unlink,
1140
1156
# - unlink raises different exceptions on different OSes (linux: EISDIR, win32:
1141
1157
# EACCES, OSX: EPERM) when invoked on a directory.
1142
if isdir(path): # Takes care of symlinks
1158
if isdir(path): # Takes care of symlinks
1145
1161
os.unlink(path)
1188
1204
# 3) '\xa0' isn't unicode safe since it is >128.
1190
if isinstance(s, str):
1193
ws = (b' ', b'\t', b'\n', b'\r', b'\v', b'\f')
1206
# This should *not* be a unicode set of characters in case the source
1207
# string is not a Unicode string. We can auto-up-cast the characters since
1208
# they are ascii, but we don't want to auto-up-cast the string in case it
1210
for ch in ' \t\n\r\v\f':
1268
1284
abs_base = abspath(base)
1269
1285
current = abs_base
1286
_listdir = os.listdir
1271
1288
# use an explicit iterator so we can easily consume the rest on early exit.
1272
1289
bit_iter = iter(rel.split('/'))
1273
1290
for bit in bit_iter:
1274
1291
lbit = bit.lower()
1276
next_entries = scandir(current)
1277
except OSError: # enoent, eperm, etc
1293
next_entries = _listdir(current)
1294
except OSError: # enoent, eperm, etc
1278
1295
# We can't find this in the filesystem, so just append the
1279
1296
# remaining bits.
1280
1297
current = pathjoin(current, bit, *list(bit_iter))
1282
for entry in next_entries:
1283
if lbit == entry.name.lower():
1284
current = entry.path
1299
for look in next_entries:
1300
if lbit == look.lower():
1301
current = pathjoin(current, look)
1287
1304
# got to the end, nothing matched, so we just return the
1320
1335
Otherwise it is decoded from the the filesystem's encoding. If decoding
1321
1336
fails, a errors.BadFilenameEncoding exception is raised.
1323
if isinstance(filename, str):
1338
if isinstance(filename, text_type):
1324
1339
return filename
1326
1341
return filename.decode(_fs_enc)
1335
1350
Otherwise it is decoded from utf-8. If decoding fails, the exception is
1336
1351
wrapped in a BzrBadParameterNotUnicode exception.
1338
if isinstance(unicode_or_utf8_string, str):
1353
if isinstance(unicode_or_utf8_string, text_type):
1339
1354
return unicode_or_utf8_string
1341
1356
return unicode_or_utf8_string.decode('utf8')
1349
1364
If it is a str, it is returned.
1350
1365
If it is Unicode, it is encoded into a utf-8 string.
1352
if isinstance(unicode_or_utf8_string, bytes):
1367
if isinstance(unicode_or_utf8_string, str):
1353
1368
# TODO: jam 20070209 This is overkill, and probably has an impact on
1354
1369
# performance if we are dealing with lots of apis that want a
1355
1370
# utf-8 revision id
1362
1377
return unicode_or_utf8_string.encode('utf-8')
1380
def safe_revision_id(unicode_or_utf8_string):
1381
"""Revision ids should now be utf8, but at one point they were unicode.
1383
:param unicode_or_utf8_string: A possibly Unicode revision_id. (can also be
1385
:return: None or a utf8 revision id.
1387
if (unicode_or_utf8_string is None
1388
or unicode_or_utf8_string.__class__ == str):
1389
return unicode_or_utf8_string
1390
raise TypeError('Unicode revision ids are no longer supported. '
1391
'Revision id generators should be creating utf8 revision '
1395
def safe_file_id(unicode_or_utf8_string):
1396
"""File ids should now be utf8, but at one point they were unicode.
1398
This is the same as safe_utf8, except it uses the cached encode functions
1399
to save a little bit of performance.
1401
:param unicode_or_utf8_string: A possibly Unicode file_id. (can also be
1403
:return: None or a utf8 file id.
1405
if (unicode_or_utf8_string is None
1406
or unicode_or_utf8_string.__class__ == str):
1407
return unicode_or_utf8_string
1408
raise TypeError('Unicode file ids are no longer supported. '
1409
'File id generators should be creating utf8 file ids.')
1365
1412
_platform_normalizes_filenames = False
1366
1413
if sys.platform == 'darwin':
1367
1414
_platform_normalizes_filenames = True
1390
1437
can be accessed by that path.
1393
if isinstance(path, bytes):
1394
path = path.decode(sys.getfilesystemencoding())
1395
return unicodedata.normalize('NFC', path), True
1440
return unicodedata.normalize('NFC', unicode(path)), True
1398
1443
def _inaccessible_normalized_filename(path):
1399
1444
__doc__ = _accessible_normalized_filename.__doc__
1401
if isinstance(path, bytes):
1402
path = path.decode(sys.getfilesystemencoding())
1403
normalized = unicodedata.normalize('NFC', path)
1446
normalized = unicodedata.normalize('NFC', unicode(path))
1404
1447
return normalized, normalized == path
1549
1591
def _win32_terminal_size(width, height):
1550
width, height = win32utils.get_console_size(
1551
defaultx=width, defaulty=height)
1592
width, height = win32utils.get_console_size(defaultx=width, defaulty=height)
1552
1593
return width, height
1555
1596
def _ioctl_terminal_size(width, height):
1598
import struct, fcntl, termios
1560
1599
s = struct.pack('HHHH', 0, 0, 0, 0)
1561
1600
x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
1562
1601
height, width = struct.unpack('HHHH', x)[0:2]
1580
1618
_terminal_size = _ioctl_terminal_size
1583
def supports_executable(path):
1584
"""Return if filesystem at path supports executable bit.
1586
:param path: Path for which to check the file system
1587
:return: boolean indicating whether executable bit can be stored/relied upon
1589
if sys.platform == 'win32':
1592
fs_type = get_fs_type(path)
1593
except errors.DependencyNotPresent as e:
1594
trace.mutter('Unable to get fs type for %r: %s', path, e)
1596
if fs_type in ('vfat', 'ntfs'):
1597
# filesystems known to not support executable bit
1602
def supports_symlinks(path):
1603
"""Return if the filesystem at path supports the creation of symbolic links.
1606
if not has_symlinks():
1609
fs_type = get_fs_type(path)
1610
except errors.DependencyNotPresent as e:
1611
trace.mutter('Unable to get fs type for %r: %s', path, e)
1613
if fs_type in ('vfat', 'ntfs'):
1614
# filesystems known to not support symlinks
1621
def supports_executable():
1622
return sys.platform != "win32"
1619
1625
def supports_posix_readonly():
1660
1668
raise errors.IllegalPath(path)
1663
_WIN32_ERROR_DIRECTORY = 267 # Similar to errno.ENOTDIR
1667
scandir = os.scandir
1668
except AttributeError: # Python < 3
1669
lazy_import(globals(), """\
1670
from scandir import scandir
1671
_WIN32_ERROR_DIRECTORY = 267 # Similar to errno.ENOTDIR
1674
1673
def _is_error_enotdir(e):
1675
1674
"""Check if this exception represents ENOTDIR.
1687
1686
:return: True if this represents an ENOTDIR error. False otherwise.
1689
1688
en = getattr(e, 'errno', None)
1690
if (en == errno.ENOTDIR or
1691
(sys.platform == 'win32' and
1692
(en == _WIN32_ERROR_DIRECTORY or
1694
and getattr(e, 'winerror', None) == _WIN32_ERROR_DIRECTORY)
1689
if (en == errno.ENOTDIR
1690
or (sys.platform == 'win32'
1691
and (en == _WIN32_ERROR_DIRECTORY
1692
or (en == errno.EINVAL
1693
and getattr(e, 'winerror', None) == _WIN32_ERROR_DIRECTORY)
1725
1724
rooted higher up.
1726
1725
:return: an iterator over the dirs.
1728
# TODO there is a bit of a smell where the results of the directory-
1727
#TODO there is a bit of a smell where the results of the directory-
1729
1728
# summary in this, and the path from the root, may not agree
1730
1729
# depending on top and prefix - i.e. ./foo and foo as a pair leads to
1731
1730
# potentially confusing output. We should make this more robust - but
1732
1731
# not at a speed cost. RBC 20060731
1733
1733
_directory = _directory_kind
1734
_listdir = os.listdir
1735
_kind_from_mode = file_kind_from_stat_mode
1734
1736
pending = [(safe_unicode(prefix), "", _directory, None, safe_unicode(top))]
1736
1738
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1742
1744
top_slash = top + u'/'
1747
append = dirblock.append
1746
for entry in scandir(top):
1747
name = decode_filename(entry.name)
1748
statvalue = entry.stat(follow_symlinks=False)
1749
kind = file_kind_from_stat_mode(statvalue.st_mode)
1750
dirblock.append((relprefix + name, name, kind, statvalue, entry.path))
1749
names = sorted(map(decode_filename, _listdir(top)))
1751
1750
except OSError as e:
1752
1751
if not _is_error_enotdir(e):
1754
except UnicodeDecodeError as e:
1755
raise errors.BadFilenameEncoding(e.object, _fs_enc)
1755
abspath = top_slash + name
1756
statvalue = _lstat(abspath)
1757
kind = _kind_from_mode(statvalue.st_mode)
1758
append((relprefix + name, name, kind, statvalue, abspath))
1757
1759
yield (relroot, top), dirblock
1759
1761
# push the user specified dirs from dirblock
1865
1867
See DirReader.read_dir for details.
1867
1869
_utf8_encode = self._utf8_encode
1869
def _fs_decode(s): return s.decode(_fs_enc)
1871
def _fs_encode(s): return s.encode(_fs_enc)
1871
_listdir = os.listdir
1872
_kind_from_mode = file_kind_from_stat_mode
1874
relprefix = prefix + b'/'
1875
relprefix = prefix + '/'
1877
top_slash = top + '/'
1878
top_slash = top + u'/'
1880
1881
append = dirblock.append
1881
for entry in scandir(safe_utf8(top)):
1882
for name in sorted(_listdir(top)):
1883
name = _fs_decode(entry.name)
1884
name_utf8 = _utf8_encode(name)[0]
1884
1885
except UnicodeDecodeError:
1885
1886
raise errors.BadFilenameEncoding(
1886
relprefix + entry.name, _fs_enc)
1887
_utf8_encode(relprefix)[0] + name, _fs_enc)
1887
1888
abspath = top_slash + name
1888
name_utf8 = _utf8_encode(name)[0]
1889
statvalue = entry.stat(follow_symlinks=False)
1890
kind = file_kind_from_stat_mode(statvalue.st_mode)
1889
statvalue = _lstat(abspath)
1890
kind = _kind_from_mode(statvalue.st_mode)
1891
1891
append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
1892
return sorted(dirblock)
1895
1895
def copy_tree(from_path, to_path, handlers={}):
1920
1920
link_to = os.readlink(source)
1921
1921
os.symlink(link_to, dest)
1923
real_handlers = {'file': shutil.copy2,
1924
'symlink': copy_link,
1925
'directory': copy_dir,
1923
real_handlers = {'file':shutil.copy2,
1924
'symlink':copy_link,
1925
'directory':copy_dir,
1927
1927
real_handlers.update(handlers)
1929
1929
if not os.path.exists(to_path):
1965
1965
This can be used to sort paths in the same way that walkdirs does.
1967
return (dirname(path), path)
1967
return (dirname(path) , path)
1970
1970
def compare_paths_prefix_order(path_a, path_b):
1971
1971
"""Compare path_a and path_b to generate the same order walkdirs uses."""
1972
1972
key_a = path_prefix_key(path_a)
1973
1973
key_b = path_prefix_key(path_b)
1974
return (key_a > key_b) - (key_a < key_b)
1974
return cmp(key_a, key_b)
1977
1977
_cached_user_encoding = None
2058
2058
def read_bytes_from_socket(sock, report_activity=None,
2059
max_read_size=MAX_SOCKET_CHUNK):
2059
max_read_size=MAX_SOCKET_CHUNK):
2060
2060
"""Read up to max_read_size of bytes from sock and notify of progress.
2062
2062
Translates "Connection reset by peer" into file-like EOF (return an
2068
data = sock.recv(max_read_size)
2068
bytes = sock.recv(max_read_size)
2069
2069
except socket.error as e:
2070
2070
eno = e.args[0]
2071
2071
if eno in _end_of_stream_errors:
2072
2072
# The connection was closed by the other side. Callers expect
2073
2073
# an empty string to signal end-of-stream.
2075
2075
elif eno == errno.EINTR:
2076
2076
# Retry the interrupted recv.
2080
2080
if report_activity is not None:
2081
report_activity(len(data), 'read')
2081
report_activity(len(bytes), 'read')
2085
2085
def recv_all(socket, count):
2119
2119
view = memoryview(bytes)
2120
2120
while sent_total < byte_count:
2122
sent = sock.send(view[sent_total:sent_total + MAX_SOCKET_CHUNK])
2122
sent = sock.send(view[sent_total:sent_total+MAX_SOCKET_CHUNK])
2123
2123
except (socket.error, IOError) as e:
2124
2124
if e.args[0] in _end_of_stream_errors:
2125
2125
raise errors.ConnectionReset(
2203
2202
base = dirname(breezy.__file__)
2204
2203
if getattr(sys, 'frozen', None): # bzr.exe
2205
2204
base = abspath(pathjoin(base, '..', '..'))
2206
with open(pathjoin(base, resource_relpath), "rt") as f:
2205
f = file(pathjoin(base, resource_relpath), "rU")
2207
2207
return f.read()
2210
2211
def file_kind_from_stat_mode_thunk(mode):
2211
2212
global file_kind_from_stat_mode
2214
2215
from ._readdir_pyx import UTF8DirReader
2215
2216
file_kind_from_stat_mode = UTF8DirReader().kind_from_mode
2217
except ImportError as e:
2217
2218
# This is one time where we won't warn that an extension failed to
2218
2219
# load. The extension is never available on Windows anyway.
2219
2220
from ._readdir_py import (
2220
2221
_kind_from_mode as file_kind_from_stat_mode
2222
2223
return file_kind_from_stat_mode(mode)
2225
2224
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
2228
2226
def file_stat(f, _lstat=os.lstat):
2234
2232
raise errors.NoSuchFile(f)
2238
2235
def file_kind(f, _lstat=os.lstat):
2239
2236
stat_value = file_stat(f, _lstat)
2240
2237
return file_kind_from_stat_mode(stat_value.st_mode)
2243
2239
def until_no_eintr(f, *a, **kw):
2244
2240
"""Run f(*a, **kw), retrying if an EINTR error occurs.
2296
2292
stdout=subprocess.PIPE).communicate()[0]
2297
2293
elif sys.platform == 'sunos5':
2298
2294
def _local_concurrency():
2299
return subprocess.Popen(['psrinfo', '-p', ],
2295
return subprocess.Popen(['psrinfo', '-p',],
2300
2296
stdout=subprocess.PIPE).communicate()[0]
2301
2297
elif sys.platform == "win32":
2302
2298
def _local_concurrency():
2325
2320
concurrency = os.environ.get('BRZ_CONCURRENCY', None)
2326
2321
if concurrency is None:
2327
import multiprocessing
2323
import multiprocessing
2329
2324
concurrency = multiprocessing.cpu_count()
2330
except NotImplementedError:
2331
# multiprocessing.cpu_count() isn't implemented on all platforms
2325
except (ImportError, NotImplementedError):
2326
# multiprocessing is only available on Python >= 2.6
2327
# and multiprocessing.cpu_count() isn't implemented on all
2333
2330
concurrency = _local_concurrency()
2334
2331
except (OSError, IOError):
2437
2433
def find_executable_on_path(name):
2438
2434
"""Finds an executable on the PATH.
2440
2436
On Windows, this will try to append each extension in the PATHEXT
2441
2437
environment variable to the name, if it cannot be found with the name
2444
2440
:param name: The base name of the executable.
2445
2441
:return: The path to the executable found or None.
2484
2480
# exists, though not ours
2487
trace.mutter("os.kill(%d, 0) failed: %s" % (pid, e))
2483
mutter("os.kill(%d, 0) failed: %s" % (pid, e))
2488
2484
# Don't really know.
2491
2487
# Exists and our process: not dead.
2495
2490
if sys.platform == "win32":
2496
2491
is_local_pid_dead = win32utils.is_local_pid_dead
2537
2532
raise exception_class(path)
2540
def read_mtab(path):
2541
"""Read an fstab-style file and extract mountpoint+filesystem information.
2543
:param path: Path to read from
2544
:yield: Tuples with mountpoints (as bytestrings) and filesystem names
2546
with open(path, 'rb') as f:
2548
if line.startswith(b'#'):
2553
yield cols[1], cols[2].decode('ascii', 'replace')
2556
MTAB_PATH = '/etc/mtab'
2558
class FilesystemFinder(object):
2559
"""Find the filesystem for a particular path."""
2561
def __init__(self, mountpoints):
2564
self._mountpoints = sorted(mountpoints, key=key, reverse=True)
2568
"""Create a FilesystemFinder from an mtab-style file.
2570
Note that this will silenty ignore mtab if it doesn't exist or can not
2573
# TODO(jelmer): Use inotify to be notified when /etc/mtab changes and
2574
# we need to re-read it.
2576
return cls(read_mtab(MTAB_PATH))
2577
except EnvironmentError as e:
2578
trace.mutter('Unable to read mtab: %s', e)
2581
def find(self, path):
2582
"""Find the filesystem used by a particular path.
2584
:param path: Path to find (bytestring or text type)
2585
:return: Filesystem name (as text type) or None, if the filesystem is
2588
for mountpoint, filesystem in self._mountpoints:
2589
if is_inside(mountpoint, path):
2594
_FILESYSTEM_FINDER = None
2597
def get_fs_type(path):
2598
"""Return the filesystem type for the partition a path is in.
2600
:param path: Path to search filesystem type for
2601
:return: A FS type, as string. E.g. "ext2"
2603
global _FILESYSTEM_FINDER
2604
if _FILESYSTEM_FINDER is None:
2605
_FILESYSTEM_FINDER = FilesystemFinder.from_mtab()
2607
if not isinstance(path, bytes):
2608
path = path.encode(_fs_enc)
2610
return _FILESYSTEM_FINDER.find(path)
2613
perf_counter = time.perf_counter
2535
def is_environment_error(evalue):
2536
"""True if exception instance is due to a process environment issue
2538
This includes OSError and IOError, but also other errors that come from
2539
the operating system or core libraries but are not subclasses of those.
2541
if isinstance(evalue, (EnvironmentError, select.error)):
2543
if sys.platform == "win32" and win32utils._is_pywintypes_error(evalue):