~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Martin Pool
  • Date: 2006-11-02 10:20:19 UTC
  • mfrom: (2114 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2119.
  • Revision ID: mbp@sourcefrog.net-20061102102019-9a5a02f485dff6f6
merge bzr.dev and reconcile several changes, also some test fixes

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Bazaar -- distributed version control
2
 
#
3
 
# Copyright (C) 2005 by Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
4
2
#
5
3
# This program is free software; you can redistribute it and/or modify
6
4
# it under the terms of the GNU General Public License as published by
17
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
16
 
19
17
from cStringIO import StringIO
 
18
import os
 
19
import re
 
20
import stat
 
21
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
 
22
                  S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
 
23
import sys
 
24
import time
 
25
 
 
26
from bzrlib.lazy_import import lazy_import
 
27
lazy_import(globals(), """
20
28
import errno
21
29
from ntpath import (abspath as _nt_abspath,
22
30
                    join as _nt_join,
24
32
                    realpath as _nt_realpath,
25
33
                    splitdrive as _nt_splitdrive,
26
34
                    )
27
 
import os
28
 
from os import listdir
29
35
import posixpath
30
 
import re
31
36
import sha
32
37
import shutil
33
 
from shutil import copyfile
34
 
import stat
35
 
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
36
 
                  S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
 
38
from shutil import (
 
39
    rmtree,
 
40
    )
37
41
import string
38
 
import sys
39
 
import time
40
 
import types
41
42
import tempfile
 
43
from tempfile import (
 
44
    mkdtemp,
 
45
    )
42
46
import unicodedata
43
47
 
 
48
from bzrlib import (
 
49
    errors,
 
50
    )
 
51
""")
 
52
 
44
53
import bzrlib
45
 
from bzrlib.errors import (BzrError,
46
 
                           BzrBadParameterNotUnicode,
47
 
                           NoSuchFile,
48
 
                           PathNotChild,
49
 
                           IllegalPath,
50
 
                           )
51
 
from bzrlib.symbol_versioning import (deprecated_function, 
52
 
        zero_nine)
 
54
from bzrlib.symbol_versioning import (
 
55
    deprecated_function,
 
56
    zero_nine,
 
57
    )
53
58
from bzrlib.trace import mutter
54
59
 
55
60
 
122
127
        return _mapper(_lstat(f).st_mode)
123
128
    except OSError, e:
124
129
        if getattr(e, 'errno', None) == errno.ENOENT:
125
 
            raise bzrlib.errors.NoSuchFile(f)
 
130
            raise errors.NoSuchFile(f)
126
131
        raise
127
132
 
128
133
 
144
149
    elif kind == 'symlink':
145
150
        return '@'
146
151
    else:
147
 
        raise BzrError('invalid file kind %r' % kind)
 
152
        raise errors.BzrError('invalid file kind %r' % kind)
148
153
 
149
154
lexists = getattr(os.path, 'lexists', None)
150
155
if lexists is None:
159
164
            if e.errno == errno.ENOENT:
160
165
                return False;
161
166
            else:
162
 
                raise BzrError("lstat/stat of (%r): %r" % (f, e))
 
167
                raise errors.BzrError("lstat/stat of (%r): %r" % (f, e))
163
168
 
164
169
 
165
170
def fancy_rename(old, new, rename_func, unlink_func):
186
191
    file_existed = False
187
192
    try:
188
193
        rename_func(new, tmp_name)
189
 
    except (NoSuchFile,), e:
 
194
    except (errors.NoSuchFile,), e:
190
195
        pass
191
196
    except IOError, e:
192
197
        # RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
221
226
# choke on a Unicode string containing a relative path if
222
227
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
223
228
# string.
224
 
_fs_enc = sys.getfilesystemencoding()
 
229
_fs_enc = sys.getfilesystemencoding() or 'utf-8'
225
230
def _posix_abspath(path):
226
231
    # jam 20060426 rather than encoding to fsencoding
227
232
    # copy posixpath.abspath, but use os.getcwdu instead
302
307
pathjoin = os.path.join
303
308
normpath = os.path.normpath
304
309
getcwd = os.getcwdu
305
 
mkdtemp = tempfile.mkdtemp
306
310
rename = os.rename
307
311
dirname = os.path.dirname
308
312
basename = os.path.basename
309
 
rmtree = shutil.rmtree
 
313
# These were already imported into local scope
 
314
# mkdtemp = tempfile.mkdtemp
 
315
# rmtree = shutil.rmtree
310
316
 
311
317
MIN_ABS_PATHLENGTH = 1
312
318
 
330
336
        if function in (os.remove, os.rmdir) \
331
337
            and type_ == OSError \
332
338
            and value.errno == errno.EACCES:
333
 
            bzrlib.osutils.make_writable(path)
 
339
            make_writable(path)
334
340
            function(path)
335
341
        else:
336
342
            raise
440
446
    
441
447
    The empty string as a dir name is taken as top-of-tree and matches 
442
448
    everything.
443
 
    
444
 
    >>> is_inside('src', pathjoin('src', 'foo.c'))
445
 
    True
446
 
    >>> is_inside('src', 'srccontrol')
447
 
    False
448
 
    >>> is_inside('src', pathjoin('src', 'a', 'a', 'a', 'foo.c'))
449
 
    True
450
 
    >>> is_inside('foo.c', 'foo.c')
451
 
    True
452
 
    >>> is_inside('foo.c', '')
453
 
    False
454
 
    >>> is_inside('', 'foo.c')
455
 
    True
456
449
    """
457
450
    # XXX: Most callers of this can actually do something smarter by 
458
451
    # looking at the inventory
581
574
        tt = time.localtime(t)
582
575
        offset = local_time_offset(t)
583
576
    else:
584
 
        raise BzrError("unsupported timezone format %r" % timezone,
585
 
                       ['options are "utc", "original", "local"'])
 
577
        raise errors.BzrError("unsupported timezone format %r" % timezone,
 
578
                              ['options are "utc", "original", "local"'])
586
579
    if date_fmt is None:
587
580
        date_fmt = "%a %Y-%m-%d %H:%M:%S"
588
581
    if show_offset:
659
652
except (NotImplementedError, AttributeError):
660
653
    # If python doesn't have os.urandom, or it doesn't work,
661
654
    # then try to first pull random data from /dev/urandom
662
 
    if os.path.exists("/dev/urandom"):
 
655
    try:
663
656
        rand_bytes = file('/dev/urandom', 'rb').read
664
657
    # Otherwise, use this hack as a last resort
665
 
    else:
 
658
    except (IOError, OSError):
666
659
        # not well seeded, but better than nothing
667
660
        def rand_bytes(n):
668
661
            import random
690
683
## decomposition (might be too tricksy though.)
691
684
 
692
685
def splitpath(p):
693
 
    """Turn string into list of parts.
694
 
 
695
 
    >>> splitpath('a')
696
 
    ['a']
697
 
    >>> splitpath('a/b')
698
 
    ['a', 'b']
699
 
    >>> splitpath('a/./b')
700
 
    ['a', 'b']
701
 
    >>> splitpath('a/.b')
702
 
    ['a', '.b']
703
 
    >>> splitpath('a/../b')
704
 
    Traceback (most recent call last):
705
 
    ...
706
 
    BzrError: sorry, '..' not allowed in path
707
 
    """
708
 
    assert isinstance(p, types.StringTypes)
 
686
    """Turn string into list of parts."""
 
687
    assert isinstance(p, basestring)
709
688
 
710
689
    # split on either delimiter because people might use either on
711
690
    # Windows
714
693
    rps = []
715
694
    for f in ps:
716
695
        if f == '..':
717
 
            raise BzrError("sorry, %r not allowed in path" % f)
 
696
            raise errors.BzrError("sorry, %r not allowed in path" % f)
718
697
        elif (f == '.') or (f == ''):
719
698
            pass
720
699
        else:
725
704
    assert isinstance(p, list)
726
705
    for f in p:
727
706
        if (f == '..') or (f is None) or (f == ''):
728
 
            raise BzrError("sorry, %r not allowed in path" % f)
 
707
            raise errors.BzrError("sorry, %r not allowed in path" % f)
729
708
    return pathjoin(*p)
730
709
 
731
710
 
753
732
def link_or_copy(src, dest):
754
733
    """Hardlink a file, or copy it if it can't be hardlinked."""
755
734
    if not hardlinks_good():
756
 
        copyfile(src, dest)
 
735
        shutil.copyfile(src, dest)
757
736
        return
758
737
    try:
759
738
        os.link(src, dest)
760
739
    except (OSError, IOError), e:
761
740
        if e.errno != errno.EXDEV:
762
741
            raise
763
 
        copyfile(src, dest)
 
742
        shutil.copyfile(src, dest)
764
743
 
765
744
def delete_any(full_path):
766
745
    """Delete a file or directory."""
824
803
        if tail:
825
804
            s.insert(0, tail)
826
805
    else:
827
 
        raise PathNotChild(rp, base)
 
806
        raise errors.PathNotChild(rp, base)
828
807
 
829
808
    if s:
830
809
        return pathjoin(*s)
845
824
    try:
846
825
        return unicode_or_utf8_string.decode('utf8')
847
826
    except UnicodeDecodeError:
848
 
        raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
 
827
        raise errors.BzrBadParameterNotUnicode(unicode_or_utf8_string)
849
828
 
850
829
 
851
830
_platform_normalizes_filenames = False
950
929
    if sys.platform != "win32":
951
930
        return
952
931
    if _validWin32PathRE.match(path) is None:
953
 
        raise IllegalPath(path)
 
932
        raise errors.IllegalPath(path)
954
933
 
955
934
 
956
935
def walkdirs(top, prefix=""):
989
968
    lstat = os.lstat
990
969
    pending = []
991
970
    _directory = _directory_kind
992
 
    _listdir = listdir
 
971
    _listdir = os.listdir
993
972
    pending = [(prefix, "", _directory, None, top)]
994
973
    while pending:
995
974
        dirblock = []
1109
1088
    if _cached_user_encoding is None:
1110
1089
        _cached_user_encoding = 'ascii'
1111
1090
    return _cached_user_encoding
 
1091
 
 
1092
 
 
1093
def recv_all(socket, bytes):
 
1094
    """Receive an exact number of bytes.
 
1095
 
 
1096
    Regular Socket.recv() may return less than the requested number of bytes,
 
1097
    dependning on what's in the OS buffer.  MSG_WAITALL is not available
 
1098
    on all platforms, but this should work everywhere.  This will return
 
1099
    less than the requested amount if the remote end closes.
 
1100
 
 
1101
    This isn't optimized and is intended mostly for use in testing.
 
1102
    """
 
1103
    b = ''
 
1104
    while len(b) < bytes:
 
1105
        new = socket.recv(bytes - len(b))
 
1106
        if new == '':
 
1107
            break # eof
 
1108
        b += new
 
1109
    return b
 
1110
 
 
1111
def dereference_path(path):
 
1112
    """Determine the real path to a file.
 
1113
 
 
1114
    All parent elements are dereferenced.  But the file itself is not
 
1115
    dereferenced.
 
1116
    :param path: The original path.  May be absolute or relative.
 
1117
    :return: the real path *to* the file
 
1118
    """
 
1119
    parent, base = os.path.split(path)
 
1120
    # The pathjoin for '.' is a workaround for Python bug #1213894.
 
1121
    # (initial path components aren't dereferenced)
 
1122
    return pathjoin(realpath(pathjoin('.', parent)), base)