17
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
19
from cStringIO import StringIO
23
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
24
S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
28
from bzrlib.lazy_import import lazy_import
29
lazy_import(globals(), """
21
31
from ntpath import (abspath as _nt_abspath,
24
34
realpath as _nt_realpath,
25
35
splitdrive as _nt_splitdrive,
28
from os import listdir
33
from shutil import copyfile
35
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
36
S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
45
from tempfile import (
45
from bzrlib.errors import (BzrError,
46
BzrBadParameterNotUnicode,
51
from bzrlib.symbol_versioning import (deprecated_function,
56
from bzrlib.symbol_versioning import (
53
60
from bzrlib.trace import mutter
122
129
return _mapper(_lstat(f).st_mode)
123
130
except OSError, e:
124
131
if getattr(e, 'errno', None) == errno.ENOENT:
125
raise bzrlib.errors.NoSuchFile(f)
132
raise errors.NoSuchFile(f)
144
151
elif kind == 'symlink':
147
raise BzrError('invalid file kind %r' % kind)
154
raise errors.BzrError('invalid file kind %r' % kind)
149
156
lexists = getattr(os.path, 'lexists', None)
150
157
if lexists is None:
153
if hasattr(os, 'lstat'):
160
if getattr(os, 'lstat') is not None:
159
166
if e.errno == errno.ENOENT:
162
raise BzrError("lstat/stat of (%r): %r" % (f, e))
169
raise errors.BzrError("lstat/stat of (%r): %r" % (f, e))
165
172
def fancy_rename(old, new, rename_func, unlink_func):
186
193
file_existed = False
188
195
rename_func(new, tmp_name)
189
except (NoSuchFile,), e:
196
except (errors.NoSuchFile,), e:
191
198
except IOError, e:
192
199
# RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
193
# function raises an IOError with errno == None when a rename fails.
200
# function raises an IOError with errno is None when a rename fails.
194
201
# This then gets caught here.
195
202
if e.errno not in (None, errno.ENOENT, errno.ENOTDIR):
197
204
except Exception, e:
198
if (not hasattr(e, 'errno')
205
if (getattr(e, 'errno', None) is None
199
206
or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
302
309
pathjoin = os.path.join
303
310
normpath = os.path.normpath
304
311
getcwd = os.getcwdu
305
mkdtemp = tempfile.mkdtemp
306
312
rename = os.rename
307
313
dirname = os.path.dirname
308
314
basename = os.path.basename
309
rmtree = shutil.rmtree
315
# These were already imported into local scope
316
# mkdtemp = tempfile.mkdtemp
317
# rmtree = shutil.rmtree
311
319
MIN_ABS_PATHLENGTH = 1
441
449
The empty string as a dir name is taken as top-of-tree and matches
444
>>> is_inside('src', pathjoin('src', 'foo.c'))
446
>>> is_inside('src', 'srccontrol')
448
>>> is_inside('src', pathjoin('src', 'a', 'a', 'a', 'foo.c'))
450
>>> is_inside('foo.c', 'foo.c')
452
>>> is_inside('foo.c', '')
454
>>> is_inside('', 'foo.c')
457
452
# XXX: Most callers of this can actually do something smarter by
458
453
# looking at the inventory
555
550
def local_time_offset(t=None):
556
551
"""Return offset of local zone from GMT, either at present or at time t."""
557
552
# python2.3 localtime() can't take None
561
556
if time.localtime(t).tm_isdst and time.daylight:
574
569
tt = time.gmtime(t)
576
571
elif timezone == 'original':
579
574
tt = time.gmtime(t + offset)
580
575
elif timezone == 'local':
581
576
tt = time.localtime(t)
582
577
offset = local_time_offset(t)
584
raise BzrError("unsupported timezone format %r" % timezone,
585
['options are "utc", "original", "local"'])
579
raise errors.BzrError("unsupported timezone format %r" % timezone,
580
['options are "utc", "original", "local"'])
586
581
if date_fmt is None:
587
582
date_fmt = "%a %Y-%m-%d %H:%M:%S"
596
591
return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
594
def format_delta(delta):
595
"""Get a nice looking string for a time delta.
597
:param delta: The time difference in seconds, can be positive or negative.
598
positive indicates time in the past, negative indicates time in the
599
future. (usually time.time() - stored_time)
600
:return: String formatted to show approximate resolution
606
direction = 'in the future'
610
if seconds < 90: # print seconds up to 90 seconds
612
return '%d second %s' % (seconds, direction,)
614
return '%d seconds %s' % (seconds, direction)
616
minutes = int(seconds / 60)
617
seconds -= 60 * minutes
622
if minutes < 90: # print minutes, seconds up to 90 minutes
624
return '%d minute, %d second%s %s' % (
625
minutes, seconds, plural_seconds, direction)
627
return '%d minutes, %d second%s %s' % (
628
minutes, seconds, plural_seconds, direction)
630
hours = int(minutes / 60)
631
minutes -= 60 * hours
638
return '%d hour, %d minute%s %s' % (hours, minutes,
639
plural_minutes, direction)
640
return '%d hours, %d minute%s %s' % (hours, minutes,
641
plural_minutes, direction)
601
644
"""Return size of given open file."""
611
654
except (NotImplementedError, AttributeError):
612
655
# If python doesn't have os.urandom, or it doesn't work,
613
656
# then try to first pull random data from /dev/urandom
614
if os.path.exists("/dev/urandom"):
615
658
rand_bytes = file('/dev/urandom', 'rb').read
616
659
# Otherwise, use this hack as a last resort
660
except (IOError, OSError):
618
661
# not well seeded, but better than nothing
619
662
def rand_bytes(n):
642
685
## decomposition (might be too tricksy though.)
644
687
def splitpath(p):
645
"""Turn string into list of parts.
651
>>> splitpath('a/./b')
653
>>> splitpath('a/.b')
655
>>> splitpath('a/../b')
656
Traceback (most recent call last):
658
BzrError: sorry, '..' not allowed in path
660
assert isinstance(p, types.StringTypes)
688
"""Turn string into list of parts."""
689
assert isinstance(p, basestring)
662
691
# split on either delimiter because people might use either on
677
706
assert isinstance(p, list)
679
if (f == '..') or (f == None) or (f == ''):
680
raise BzrError("sorry, %r not allowed in path" % f)
708
if (f == '..') or (f is None) or (f == ''):
709
raise errors.BzrError("sorry, %r not allowed in path" % f)
681
710
return pathjoin(*p)
705
734
def link_or_copy(src, dest):
706
735
"""Hardlink a file, or copy it if it can't be hardlinked."""
707
736
if not hardlinks_good():
737
shutil.copyfile(src, dest)
711
740
os.link(src, dest)
712
741
except (OSError, IOError), e:
713
742
if e.errno != errno.EXDEV:
744
shutil.copyfile(src, dest)
717
746
def delete_any(full_path):
718
747
"""Delete a file or directory."""
798
827
return unicode_or_utf8_string.decode('utf8')
799
828
except UnicodeDecodeError:
800
raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
829
raise errors.BzrBadParameterNotUnicode(unicode_or_utf8_string)
803
832
_platform_normalizes_filenames = False
870
900
def supports_executable():
871
901
return sys.platform != "win32"
904
def set_or_unset_env(env_variable, value):
905
"""Modify the environment, setting or removing the env_variable.
907
:param env_variable: The environment variable in question
908
:param value: The value to set the environment to. If None, then
909
the variable will be removed.
910
:return: The original value of the environment variable.
912
orig_val = os.environ.get(env_variable)
914
if orig_val is not None:
915
del os.environ[env_variable]
917
if isinstance(value, unicode):
918
value = value.encode(bzrlib.user_encoding)
919
os.environ[env_variable] = value
874
923
_validWin32PathRE = re.compile(r'^([A-Za-z]:[/\\])?[^:<>*"?\|]*$')
882
931
if sys.platform != "win32":
884
933
if _validWin32PathRE.match(path) is None:
885
raise IllegalPath(path)
934
raise errors.IllegalPath(path)
888
937
def walkdirs(top, prefix=""):
1032
1081
_cached_user_encoding = locale.getpreferredencoding()
1033
1082
except locale.Error, e:
1034
1083
sys.stderr.write('bzr: warning: %s\n'
1035
' Could not what text encoding to use.\n'
1084
' Could not determine what text encoding to use.\n'
1036
1085
' This error usually means your Python interpreter\n'
1037
1086
' doesn\'t support the locale set by $LANG (%s)\n'
1038
1087
" Continuing with ascii encoding.\n"