555
638
def local_time_offset(t=None):
556
639
"""Return offset of local zone from GMT, either at present or at time t."""
557
# python2.3 localtime() can't take None
561
if time.localtime(t).tm_isdst and time.daylight:
564
return -time.timezone
642
offset = datetime.fromtimestamp(t) - datetime.utcfromtimestamp(t)
643
return offset.days * 86400 + offset.seconds
645
weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
567
def format_date(t, offset=0, timezone='original', date_fmt=None,
647
def format_date(t, offset=0, timezone='original', date_fmt=None,
568
648
show_offset=True):
569
## TODO: Perhaps a global option to use either universal or local time?
570
## Or perhaps just let people set $TZ?
571
assert isinstance(t, float)
649
"""Return a formatted date string.
651
:param t: Seconds since the epoch.
652
:param offset: Timezone offset in seconds east of utc.
653
:param timezone: How to display the time: 'utc', 'original' for the
654
timezone specified by offset, or 'local' for the process's current
656
:param show_offset: Whether to append the timezone.
657
:param date_fmt: strftime format.
573
659
if timezone == 'utc':
574
660
tt = time.gmtime(t)
596
683
return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
686
def format_delta(delta):
687
"""Get a nice looking string for a time delta.
689
:param delta: The time difference in seconds, can be positive or negative.
690
positive indicates time in the past, negative indicates time in the
691
future. (usually time.time() - stored_time)
692
:return: String formatted to show approximate resolution
698
direction = 'in the future'
702
if seconds < 90: # print seconds up to 90 seconds
704
return '%d second %s' % (seconds, direction,)
706
return '%d seconds %s' % (seconds, direction)
708
minutes = int(seconds / 60)
709
seconds -= 60 * minutes
714
if minutes < 90: # print minutes, seconds up to 90 minutes
716
return '%d minute, %d second%s %s' % (
717
minutes, seconds, plural_seconds, direction)
719
return '%d minutes, %d second%s %s' % (
720
minutes, seconds, plural_seconds, direction)
722
hours = int(minutes / 60)
723
minutes -= 60 * hours
730
return '%d hour, %d minute%s %s' % (hours, minutes,
731
plural_minutes, direction)
732
return '%d hours, %d minute%s %s' % (hours, minutes,
733
plural_minutes, direction)
601
736
"""Return size of given open file."""
798
935
return unicode_or_utf8_string.decode('utf8')
799
936
except UnicodeDecodeError:
800
raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
937
raise errors.BzrBadParameterNotUnicode(unicode_or_utf8_string)
940
def safe_utf8(unicode_or_utf8_string):
941
"""Coerce unicode_or_utf8_string to a utf8 string.
943
If it is a str, it is returned.
944
If it is Unicode, it is encoded into a utf-8 string.
946
if isinstance(unicode_or_utf8_string, str):
947
# TODO: jam 20070209 This is overkill, and probably has an impact on
948
# performance if we are dealing with lots of apis that want a
951
# Make sure it is a valid utf-8 string
952
unicode_or_utf8_string.decode('utf-8')
953
except UnicodeDecodeError:
954
raise errors.BzrBadParameterNotUnicode(unicode_or_utf8_string)
955
return unicode_or_utf8_string
956
return unicode_or_utf8_string.encode('utf-8')
959
_revision_id_warning = ('Unicode revision ids were deprecated in bzr 0.15.'
960
' Revision id generators should be creating utf8'
964
def safe_revision_id(unicode_or_utf8_string, warn=True):
965
"""Revision ids should now be utf8, but at one point they were unicode.
967
:param unicode_or_utf8_string: A possibly Unicode revision_id. (can also be
969
:param warn: Functions that are sanitizing user data can set warn=False
970
:return: None or a utf8 revision id.
972
if (unicode_or_utf8_string is None
973
or unicode_or_utf8_string.__class__ == str):
974
return unicode_or_utf8_string
976
symbol_versioning.warn(_revision_id_warning, DeprecationWarning,
978
return cache_utf8.encode(unicode_or_utf8_string)
981
_file_id_warning = ('Unicode file ids were deprecated in bzr 0.15. File id'
982
' generators should be creating utf8 file ids.')
985
def safe_file_id(unicode_or_utf8_string, warn=True):
986
"""File ids should now be utf8, but at one point they were unicode.
988
This is the same as safe_utf8, except it uses the cached encode functions
989
to save a little bit of performance.
991
:param unicode_or_utf8_string: A possibly Unicode file_id. (can also be
993
:param warn: Functions that are sanitizing user data can set warn=False
994
:return: None or a utf8 file id.
996
if (unicode_or_utf8_string is None
997
or unicode_or_utf8_string.__class__ == str):
998
return unicode_or_utf8_string
1000
symbol_versioning.warn(_file_id_warning, DeprecationWarning,
1002
return cache_utf8.encode(unicode_or_utf8_string)
803
1005
_platform_normalizes_filenames = False
902
1116
if sys.platform != "win32":
904
1118
if _validWin32PathRE.match(path) is None:
905
raise IllegalPath(path)
1119
raise errors.IllegalPath(path)
1122
_WIN32_ERROR_DIRECTORY = 267 # Similar to errno.ENOTDIR
1124
def _is_error_enotdir(e):
1125
"""Check if this exception represents ENOTDIR.
1127
Unfortunately, python is very inconsistent about the exception
1128
here. The cases are:
1129
1) Linux, Mac OSX all versions seem to set errno == ENOTDIR
1130
2) Windows, Python2.4, uses errno == ERROR_DIRECTORY (267)
1131
which is the windows error code.
1132
3) Windows, Python2.5 uses errno == EINVAL and
1133
winerror == ERROR_DIRECTORY
1135
:param e: An Exception object (expected to be OSError with an errno
1136
attribute, but we should be able to cope with anything)
1137
:return: True if this represents an ENOTDIR error. False otherwise.
1139
en = getattr(e, 'errno', None)
1140
if (en == errno.ENOTDIR
1141
or (sys.platform == 'win32'
1142
and (en == _WIN32_ERROR_DIRECTORY
1143
or (en == errno.EINVAL
1144
and getattr(e, 'winerror', None) == _WIN32_ERROR_DIRECTORY)
908
1150
def walkdirs(top, prefix=""):
938
1180
# depending on top and prefix - i.e. ./foo and foo as a pair leads to
939
1181
# potentially confusing output. We should make this more robust - but
940
1182
# not at a speed cost. RBC 20060731
943
1184
_directory = _directory_kind
945
pending = [(prefix, "", _directory, None, top)]
1185
_listdir = os.listdir
1186
_kind_from_mode = _formats.get
1187
pending = [(safe_unicode(prefix), "", _directory, None, safe_unicode(top))]
948
currentdir = pending.pop()
949
1189
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
952
relroot = currentdir[0] + '/'
955
for name in sorted(_listdir(top)):
956
abspath = top + '/' + name
957
statvalue = lstat(abspath)
958
dirblock.append((relroot + name, name,
959
file_kind_from_stat_mode(statvalue.st_mode),
961
yield (currentdir[0], top), dirblock
962
# push the user specified dirs from dirblock
963
for dir in reversed(dirblock):
964
if dir[2] == _directory:
1190
relroot, _, _, _, top = pending.pop()
1192
relprefix = relroot + u'/'
1195
top_slash = top + u'/'
1198
append = dirblock.append
1200
names = sorted(_listdir(top))
1202
if not _is_error_enotdir(e):
1206
abspath = top_slash + name
1207
statvalue = _lstat(abspath)
1208
kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1209
append((relprefix + name, name, kind, statvalue, abspath))
1210
yield (relroot, top), dirblock
1212
# push the user specified dirs from dirblock
1213
pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1216
_real_walkdirs_utf8 = None
1218
def _walkdirs_utf8(top, prefix=""):
1219
"""Yield data about all the directories in a tree.
1221
This yields the same information as walkdirs() only each entry is yielded
1222
in utf-8. On platforms which have a filesystem encoding of utf8 the paths
1223
are returned as exact byte-strings.
1225
:return: yields a tuple of (dir_info, [file_info])
1226
dir_info is (utf8_relpath, path-from-top)
1227
file_info is (utf8_relpath, utf8_name, kind, lstat, path-from-top)
1228
if top is an absolute path, path-from-top is also an absolute path.
1229
path-from-top might be unicode or utf8, but it is the correct path to
1230
pass to os functions to affect the file in question. (such as os.lstat)
1232
global _real_walkdirs_utf8
1233
if _real_walkdirs_utf8 is None:
1234
fs_encoding = _fs_enc.upper()
1235
if win32utils.winver == 'Windows NT':
1236
# Win98 doesn't have unicode apis like FindFirstFileW
1237
# TODO: We possibly could support Win98 by falling back to the
1238
# original FindFirstFile, and using TCHAR instead of WCHAR,
1239
# but that gets a bit tricky, and requires custom compiling
1242
from bzrlib._walkdirs_win32 import _walkdirs_utf8_win32_find_file
1244
_real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
1246
_real_walkdirs_utf8 = _walkdirs_utf8_win32_find_file
1247
elif fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968'):
1248
# ANSI_X3.4-1968 is a form of ASCII
1249
_real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
1251
_real_walkdirs_utf8 = _walkdirs_fs_utf8
1252
return _real_walkdirs_utf8(top, prefix=prefix)
1255
def _walkdirs_fs_utf8(top, prefix=""):
1256
"""See _walkdirs_utf8.
1258
This sub-function is called when we know the filesystem is already in utf8
1259
encoding. So we don't need to transcode filenames.
1262
_directory = _directory_kind
1263
_listdir = os.listdir
1264
_kind_from_mode = _formats.get
1266
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1267
# But we don't actually uses 1-3 in pending, so set them to None
1268
pending = [(safe_utf8(prefix), None, None, None, safe_utf8(top))]
1270
relroot, _, _, _, top = pending.pop()
1272
relprefix = relroot + '/'
1275
top_slash = top + '/'
1278
append = dirblock.append
1279
for name in sorted(_listdir(top)):
1280
abspath = top_slash + name
1281
statvalue = _lstat(abspath)
1282
kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1283
append((relprefix + name, name, kind, statvalue, abspath))
1284
yield (relroot, top), dirblock
1286
# push the user specified dirs from dirblock
1287
pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1290
def _walkdirs_unicode_to_utf8(top, prefix=""):
1291
"""See _walkdirs_utf8
1293
Because Win32 has a Unicode api, all of the 'path-from-top' entries will be
1295
This is currently the fallback code path when the filesystem encoding is
1296
not UTF-8. It may be better to implement an alternative so that we can
1297
safely handle paths that are not properly decodable in the current
1300
_utf8_encode = codecs.getencoder('utf8')
1302
_directory = _directory_kind
1303
_listdir = os.listdir
1304
_kind_from_mode = _formats.get
1306
pending = [(safe_utf8(prefix), None, None, None, safe_unicode(top))]
1308
relroot, _, _, _, top = pending.pop()
1310
relprefix = relroot + '/'
1313
top_slash = top + u'/'
1316
append = dirblock.append
1317
for name in sorted(_listdir(top)):
1318
name_utf8 = _utf8_encode(name)[0]
1319
abspath = top_slash + name
1320
statvalue = _lstat(abspath)
1321
kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1322
append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
1323
yield (relroot, top), dirblock
1325
# push the user specified dirs from dirblock
1326
pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
968
1329
def copy_tree(from_path, to_path, handlers={}):
1057
1422
' doesn\'t support the locale set by $LANG (%s)\n'
1058
1423
" Continuing with ascii encoding.\n"
1059
1424
% (e, os.environ.get('LANG')))
1061
if _cached_user_encoding is None:
1062
_cached_user_encoding = 'ascii'
1063
return _cached_user_encoding
1425
user_encoding = 'ascii'
1427
# Windows returns 'cp0' to indicate there is no code page. So we'll just
1428
# treat that as ASCII, and not support printing unicode characters to the
1431
# For python scripts run under vim, we get '', so also treat that as ASCII
1432
if user_encoding in (None, 'cp0', ''):
1433
user_encoding = 'ascii'
1437
codecs.lookup(user_encoding)
1439
sys.stderr.write('bzr: warning:'
1440
' unknown encoding %s.'
1441
' Continuing with ascii encoding.\n'
1444
user_encoding = 'ascii'
1447
_cached_user_encoding = user_encoding
1449
return user_encoding
1452
def recv_all(socket, bytes):
1453
"""Receive an exact number of bytes.
1455
Regular Socket.recv() may return less than the requested number of bytes,
1456
dependning on what's in the OS buffer. MSG_WAITALL is not available
1457
on all platforms, but this should work everywhere. This will return
1458
less than the requested amount if the remote end closes.
1460
This isn't optimized and is intended mostly for use in testing.
1463
while len(b) < bytes:
1464
new = socket.recv(bytes - len(b))
1471
def send_all(socket, bytes):
1472
"""Send all bytes on a socket.
1474
Regular socket.sendall() can give socket error 10053 on Windows. This
1475
implementation sends no more than 64k at a time, which avoids this problem.
1478
for pos in xrange(0, len(bytes), chunk_size):
1479
socket.sendall(bytes[pos:pos+chunk_size])
1482
def dereference_path(path):
1483
"""Determine the real path to a file.
1485
All parent elements are dereferenced. But the file itself is not
1487
:param path: The original path. May be absolute or relative.
1488
:return: the real path *to* the file
1490
parent, base = os.path.split(path)
1491
# The pathjoin for '.' is a workaround for Python bug #1213894.
1492
# (initial path components aren't dereferenced)
1493
return pathjoin(realpath(pathjoin('.', parent)), base)
1496
def supports_mapi():
1497
"""Return True if we can use MAPI to launch a mail client."""
1498
return sys.platform == "win32"
1501
def resource_string(package, resource_name):
1502
"""Load a resource from a package and return it as a string.
1504
Note: Only packages that start with bzrlib are currently supported.
1506
This is designed to be a lightweight implementation of resource
1507
loading in a way which is API compatible with the same API from
1509
http://peak.telecommunity.com/DevCenter/PkgResources#basic-resource-access.
1510
If and when pkg_resources becomes a standard library, this routine
1513
# Check package name is within bzrlib
1514
if package == "bzrlib":
1515
resource_relpath = resource_name
1516
elif package.startswith("bzrlib."):
1517
package = package[len("bzrlib."):].replace('.', os.sep)
1518
resource_relpath = pathjoin(package, resource_name)
1520
raise errors.BzrError('resource package %s not in bzrlib' % package)
1522
# Map the resource to a file and read its contents
1523
base = dirname(bzrlib.__file__)
1524
if getattr(sys, 'frozen', None): # bzr.exe
1525
base = abspath(pathjoin(base, '..', '..'))
1526
filename = pathjoin(base, resource_relpath)
1527
return open(filename, 'rU').read()