~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-06-16 14:33:42 UTC
  • mfrom: (1770.2.1 config)
  • Revision ID: pqm@pqm.ubuntu.com-20060616143342-8f7f4a4f77c1e4c8
Use create_signature for signing policy, deprecate check_signatures for this

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Bazaar -- distributed version control
 
1
# Bazaar-NG -- distributed version control
2
2
#
3
3
# Copyright (C) 2005 by Canonical Ltd
4
4
#
18
18
 
19
19
from cStringIO import StringIO
20
20
import errno
21
 
from ntpath import (abspath as _nt_abspath,
22
 
                    join as _nt_join,
23
 
                    normpath as _nt_normpath,
24
 
                    realpath as _nt_realpath,
25
 
                    splitdrive as _nt_splitdrive,
26
 
                    )
27
21
import os
28
22
from os import listdir
29
 
import posixpath
30
23
import re
31
24
import sha
32
25
import shutil
40
33
import types
41
34
import tempfile
42
35
import unicodedata
 
36
from ntpath import (abspath as _nt_abspath,
 
37
                    join as _nt_join,
 
38
                    normpath as _nt_normpath,
 
39
                    realpath as _nt_realpath,
 
40
                    )
43
41
 
44
42
import bzrlib
45
43
from bzrlib.errors import (BzrError,
48
46
                           PathNotChild,
49
47
                           IllegalPath,
50
48
                           )
51
 
from bzrlib.symbol_versioning import (deprecated_function, 
52
 
        zero_nine)
 
49
from bzrlib.symbol_versioning import *
53
50
from bzrlib.trace import mutter
54
 
 
55
 
 
56
 
# On win32, O_BINARY is used to indicate the file should
57
 
# be opened in binary mode, rather than text mode.
58
 
# On other platforms, O_BINARY doesn't exist, because
59
 
# they always open in binary mode, so it is okay to
60
 
# OR with 0 on those platforms
61
 
O_BINARY = getattr(os, 'O_BINARY', 0)
 
51
import bzrlib.win32console
62
52
 
63
53
 
64
54
def make_readonly(filename):
84
74
    Windows."""
85
75
    # TODO: I'm not really sure this is the best format either.x
86
76
    global _QUOTE_RE
87
 
    if _QUOTE_RE is None:
 
77
    if _QUOTE_RE == None:
88
78
        _QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/\\_~-])')
89
79
        
90
80
    if _QUOTE_RE.search(f):
126
116
        raise
127
117
 
128
118
 
129
 
def get_umask():
130
 
    """Return the current umask"""
131
 
    # Assume that people aren't messing with the umask while running
132
 
    # XXX: This is not thread safe, but there is no way to get the
133
 
    #      umask without setting it
134
 
    umask = os.umask(0)
135
 
    os.umask(umask)
136
 
    return umask
137
 
 
138
 
 
139
119
def kind_marker(kind):
140
120
    if kind == 'file':
141
121
        return ''
150
130
if lexists is None:
151
131
    def lexists(f):
152
132
        try:
153
 
            if getattr(os, 'lstat') is not None:
 
133
            if hasattr(os, 'lstat'):
154
134
                os.lstat(f)
155
135
            else:
156
136
                os.stat(f)
190
170
        pass
191
171
    except IOError, e:
192
172
        # RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
193
 
        # function raises an IOError with errno is None when a rename fails.
 
173
        # function raises an IOError with errno == None when a rename fails.
194
174
        # This then gets caught here.
195
175
        if e.errno not in (None, errno.ENOENT, errno.ENOTDIR):
196
176
            raise
197
177
    except Exception, e:
198
 
        if (getattr(e, 'errno', None) is None
 
178
        if (not hasattr(e, 'errno') 
199
179
            or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
200
180
            raise
201
181
    else:
223
203
# string.
224
204
_fs_enc = sys.getfilesystemencoding()
225
205
def _posix_abspath(path):
226
 
    # jam 20060426 rather than encoding to fsencoding
227
 
    # copy posixpath.abspath, but use os.getcwdu instead
228
 
    if not posixpath.isabs(path):
229
 
        path = posixpath.join(getcwd(), path)
230
 
    return posixpath.normpath(path)
 
206
    return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
 
207
    # jam 20060426 This is another possibility which mimics 
 
208
    # os.path.abspath, only uses unicode characters instead
 
209
    # if not os.path.isabs(path):
 
210
    #     return os.path.join(os.getcwdu(), path)
 
211
    # return path
231
212
 
232
213
 
233
214
def _posix_realpath(path):
234
 
    return posixpath.realpath(path.encode(_fs_enc)).decode(_fs_enc)
235
 
 
236
 
 
237
 
def _win32_fixdrive(path):
238
 
    """Force drive letters to be consistent.
239
 
 
240
 
    win32 is inconsistent whether it returns lower or upper case
241
 
    and even if it was consistent the user might type the other
242
 
    so we force it to uppercase
243
 
    running python.exe under cmd.exe return capital C:\\
244
 
    running win32 python inside a cygwin shell returns lowercase c:\\
245
 
    """
246
 
    drive, path = _nt_splitdrive(path)
247
 
    return drive.upper() + path
 
215
    return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
248
216
 
249
217
 
250
218
def _win32_abspath(path):
251
 
    # Real _nt_abspath doesn't have a problem with a unicode cwd
252
 
    return _win32_fixdrive(_nt_abspath(unicode(path)).replace('\\', '/'))
 
219
    return _nt_abspath(path.encode(_fs_enc)).decode(_fs_enc).replace('\\', '/')
253
220
 
254
221
 
255
222
def _win32_realpath(path):
256
 
    # Real _nt_realpath doesn't have a problem with a unicode cwd
257
 
    return _win32_fixdrive(_nt_realpath(unicode(path)).replace('\\', '/'))
 
223
    return _nt_realpath(path.encode(_fs_enc)).decode(_fs_enc).replace('\\', '/')
258
224
 
259
225
 
260
226
def _win32_pathjoin(*args):
262
228
 
263
229
 
264
230
def _win32_normpath(path):
265
 
    return _win32_fixdrive(_nt_normpath(unicode(path)).replace('\\', '/'))
 
231
    return _nt_normpath(path).replace('\\', '/')
266
232
 
267
233
 
268
234
def _win32_getcwd():
269
 
    return _win32_fixdrive(os.getcwdu().replace('\\', '/'))
 
235
    return os.getcwdu().replace('\\', '/')
270
236
 
271
237
 
272
238
def _win32_mkdtemp(*args, **kwargs):
273
 
    return _win32_fixdrive(tempfile.mkdtemp(*args, **kwargs).replace('\\', '/'))
 
239
    return tempfile.mkdtemp(*args, **kwargs).replace('\\', '/')
274
240
 
275
241
 
276
242
def _win32_rename(old, new):
277
 
    """We expect to be able to atomically replace 'new' with old.
278
 
 
279
 
    On win32, if new exists, it must be moved out of the way first,
280
 
    and then deleted. 
281
 
    """
282
 
    try:
283
 
        fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
284
 
    except OSError, e:
285
 
        if e.errno in (errno.EPERM, errno.EACCES, errno.EBUSY, errno.EINVAL):
286
 
            # If we try to rename a non-existant file onto cwd, we get 
287
 
            # EPERM or EACCES instead of ENOENT, this will raise ENOENT 
288
 
            # if the old path doesn't exist, sometimes we get EACCES
289
 
            # On Linux, we seem to get EBUSY, on Mac we get EINVAL
290
 
            os.lstat(old)
291
 
        raise
292
 
 
293
 
 
294
 
def _mac_getcwd():
295
 
    return unicodedata.normalize('NFKC', os.getcwdu())
 
243
    fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
296
244
 
297
245
 
298
246
# Default is to just use the python builtins, but these can be rebound on
338
286
    def rmtree(path, ignore_errors=False, onerror=_win32_delete_readonly):
339
287
        """Replacer for shutil.rmtree: could remove readonly dirs/files"""
340
288
        return shutil.rmtree(path, ignore_errors, onerror)
341
 
elif sys.platform == 'darwin':
342
 
    getcwd = _mac_getcwd
343
 
 
344
 
 
345
 
def get_terminal_encoding():
346
 
    """Find the best encoding for printing to the screen.
347
 
 
348
 
    This attempts to check both sys.stdout and sys.stdin to see
349
 
    what encoding they are in, and if that fails it falls back to
350
 
    bzrlib.user_encoding.
351
 
    The problem is that on Windows, locale.getpreferredencoding()
352
 
    is not the same encoding as that used by the console:
353
 
    http://mail.python.org/pipermail/python-list/2003-May/162357.html
354
 
 
355
 
    On my standard US Windows XP, the preferred encoding is
356
 
    cp1252, but the console is cp437
357
 
    """
358
 
    output_encoding = getattr(sys.stdout, 'encoding', None)
359
 
    if not output_encoding:
360
 
        input_encoding = getattr(sys.stdin, 'encoding', None)
361
 
        if not input_encoding:
362
 
            output_encoding = bzrlib.user_encoding
363
 
            mutter('encoding stdout as bzrlib.user_encoding %r', output_encoding)
364
 
        else:
365
 
            output_encoding = input_encoding
366
 
            mutter('encoding stdout as sys.stdin encoding %r', output_encoding)
367
 
    else:
368
 
        mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
369
 
    return output_encoding
370
289
 
371
290
 
372
291
def normalizepath(f):
373
 
    if getattr(os.path, 'realpath', None) is not None:
 
292
    if hasattr(os.path, 'realpath'):
374
293
        F = realpath
375
294
    else:
376
295
        F = abspath
505
424
 
506
425
 
507
426
def sha_file(f):
508
 
    if getattr(f, 'tell', None) is not None:
 
427
    if hasattr(f, 'tell'):
509
428
        assert f.tell() == 0
510
429
    s = sha.new()
511
430
    BUFSIZE = 128<<10
555
474
def local_time_offset(t=None):
556
475
    """Return offset of local zone from GMT, either at present or at time t."""
557
476
    # python2.3 localtime() can't take None
558
 
    if t is None:
 
477
    if t == None:
559
478
        t = time.time()
560
479
        
561
480
    if time.localtime(t).tm_isdst and time.daylight:
574
493
        tt = time.gmtime(t)
575
494
        offset = 0
576
495
    elif timezone == 'original':
577
 
        if offset is None:
 
496
        if offset == None:
578
497
            offset = 0
579
498
        tt = time.gmtime(t + offset)
580
499
    elif timezone == 'local':
676
595
def joinpath(p):
677
596
    assert isinstance(p, list)
678
597
    for f in p:
679
 
        if (f == '..') or (f is None) or (f == ''):
 
598
        if (f == '..') or (f == None) or (f == ''):
680
599
            raise BzrError("sorry, %r not allowed in path" % f)
681
600
    return pathjoin(*p)
682
601
 
726
645
 
727
646
 
728
647
def has_symlinks():
729
 
    if getattr(os, 'symlink', None) is not None:
 
648
    if hasattr(os, 'symlink'):
730
649
        return True
731
650
    else:
732
651
        return False
813
732
    return _platform_normalizes_filenames
814
733
 
815
734
 
816
 
def _accessible_normalized_filename(path):
817
 
    """Get the unicode normalized path, and if you can access the file.
818
 
 
819
 
    On platforms where the system normalizes filenames (Mac OSX),
820
 
    you can access a file by any path which will normalize correctly.
821
 
    On platforms where the system does not normalize filenames 
822
 
    (Windows, Linux), you have to access a file by its exact path.
823
 
 
824
 
    Internally, bzr only supports NFC/NFKC normalization, since that is 
825
 
    the standard for XML documents.
826
 
 
827
 
    So return the normalized path, and a flag indicating if the file
828
 
    can be accessed by that path.
829
 
    """
830
 
 
831
 
    return unicodedata.normalize('NFKC', unicode(path)), True
832
 
 
833
 
 
834
 
def _inaccessible_normalized_filename(path):
835
 
    __doc__ = _accessible_normalized_filename.__doc__
836
 
 
837
 
    normalized = unicodedata.normalize('NFKC', unicode(path))
838
 
    return normalized, normalized == path
839
 
 
840
 
 
841
735
if _platform_normalizes_filenames:
842
 
    normalized_filename = _accessible_normalized_filename
 
736
    def unicode_filename(path):
 
737
        """Make sure 'path' is a properly normalized filename.
 
738
 
 
739
        On platforms where the system normalizes filenames (Mac OSX),
 
740
        you can access a file by any path which will normalize
 
741
        correctly.
 
742
        Internally, bzr only supports NFC/NFKC normalization, since
 
743
        that is the standard for XML documents.
 
744
        So we return an normalized path, and indicate this has been
 
745
        properly normalized.
 
746
 
 
747
        :return: (path, is_normalized) Return a path which can
 
748
                access the file, and whether or not this path is
 
749
                normalized.
 
750
        """
 
751
        return unicodedata.normalize('NFKC', path), True
843
752
else:
844
 
    normalized_filename = _inaccessible_normalized_filename
 
753
    def unicode_filename(path):
 
754
        """Make sure 'path' is a properly normalized filename.
 
755
 
 
756
        On platforms where the system does not normalize filenames 
 
757
        (Windows, Linux), you have to access a file by its exact path.
 
758
        Internally, bzr only supports NFC/NFKC normalization, since
 
759
        that is the standard for XML documents.
 
760
        So we return the original path, and indicate if this is
 
761
        properly normalized.
 
762
 
 
763
        :return: (path, is_normalized) Return a path which can
 
764
                access the file, and whether or not this path is
 
765
                normalized.
 
766
        """
 
767
        return path, unicodedata.normalize('NFKC', path) == path
845
768
 
846
769
 
847
770
def terminal_width():
867
790
 
868
791
    return width
869
792
 
870
 
 
871
793
def supports_executable():
872
794
    return sys.platform != "win32"
873
795
 
874
796
 
875
 
def set_or_unset_env(env_variable, value):
876
 
    """Modify the environment, setting or removing the env_variable.
877
 
 
878
 
    :param env_variable: The environment variable in question
879
 
    :param value: The value to set the environment to. If None, then
880
 
        the variable will be removed.
881
 
    :return: The original value of the environment variable.
882
 
    """
883
 
    orig_val = os.environ.get(env_variable)
884
 
    if value is None:
885
 
        if orig_val is not None:
886
 
            del os.environ[env_variable]
887
 
    else:
888
 
        if isinstance(value, unicode):
889
 
            value = value.encode(bzrlib.user_encoding)
890
 
        os.environ[env_variable] = value
891
 
    return orig_val
892
 
 
893
 
 
894
797
_validWin32PathRE = re.compile(r'^([A-Za-z]:[/\\])?[^:<>*"?\|]*$')
895
798
 
896
799
 
913
816
    to exclude some directories, they are then not descended into.
914
817
    
915
818
    The data yielded is of the form:
916
 
    ((directory-relpath, directory-path-from-top),
917
 
    [(relpath, basename, kind, lstat), ...]),
918
 
     - directory-relpath is the relative path of the directory being returned
919
 
       with respect to top. prefix is prepended to this.
920
 
     - directory-path-from-root is the path including top for this directory. 
921
 
       It is suitable for use with os functions.
922
 
     - relpath is the relative path within the subtree being walked.
923
 
     - basename is the basename of the path
924
 
     - kind is the kind of the file now. If unknown then the file is not
925
 
       present within the tree - but it may be recorded as versioned. See
926
 
       versioned_kind.
927
 
     - lstat is the stat data *if* the file was statted.
928
 
     - planned, not implemented: 
929
 
       path_from_tree_root is the path from the root of the tree.
 
819
    [(relpath, basename, kind, lstat, path_from_top), ...]
930
820
 
931
821
    :param prefix: Prefix the relpaths that are yielded with 'prefix'. This 
932
822
        allows one to walk a subtree but get paths that are relative to a tree
933
823
        rooted higher up.
934
824
    :return: an iterator over the dirs.
935
825
    """
936
 
    #TODO there is a bit of a smell where the results of the directory-
937
 
    # summary in this, and the path from the root, may not agree 
938
 
    # depending on top and prefix - i.e. ./foo and foo as a pair leads to
939
 
    # potentially confusing output. We should make this more robust - but
940
 
    # not at a speed cost. RBC 20060731
941
826
    lstat = os.lstat
942
827
    pending = []
943
828
    _directory = _directory_kind
955
840
        for name in sorted(_listdir(top)):
956
841
            abspath = top + '/' + name
957
842
            statvalue = lstat(abspath)
958
 
            dirblock.append((relroot + name, name,
959
 
                file_kind_from_stat_mode(statvalue.st_mode),
960
 
                statvalue, abspath))
961
 
        yield (currentdir[0], top), dirblock
 
843
            dirblock.append ((relroot + name, name, file_kind_from_stat_mode(statvalue.st_mode), statvalue, abspath))
 
844
        yield dirblock
962
845
        # push the user specified dirs from dirblock
963
846
        for dir in reversed(dirblock):
964
847
            if dir[2] == _directory:
965
848
                pending.append(dir)
966
 
 
967
 
 
968
 
def copy_tree(from_path, to_path, handlers={}):
969
 
    """Copy all of the entries in from_path into to_path.
970
 
 
971
 
    :param from_path: The base directory to copy. 
972
 
    :param to_path: The target directory. If it does not exist, it will
973
 
        be created.
974
 
    :param handlers: A dictionary of functions, which takes a source and
975
 
        destinations for files, directories, etc.
976
 
        It is keyed on the file kind, such as 'directory', 'symlink', or 'file'
977
 
        'file', 'directory', and 'symlink' should always exist.
978
 
        If they are missing, they will be replaced with 'os.mkdir()',
979
 
        'os.readlink() + os.symlink()', and 'shutil.copy2()', respectively.
980
 
    """
981
 
    # Now, just copy the existing cached tree to the new location
982
 
    # We use a cheap trick here.
983
 
    # Absolute paths are prefixed with the first parameter
984
 
    # relative paths are prefixed with the second.
985
 
    # So we can get both the source and target returned
986
 
    # without any extra work.
987
 
 
988
 
    def copy_dir(source, dest):
989
 
        os.mkdir(dest)
990
 
 
991
 
    def copy_link(source, dest):
992
 
        """Copy the contents of a symlink"""
993
 
        link_to = os.readlink(source)
994
 
        os.symlink(link_to, dest)
995
 
 
996
 
    real_handlers = {'file':shutil.copy2,
997
 
                     'symlink':copy_link,
998
 
                     'directory':copy_dir,
999
 
                    }
1000
 
    real_handlers.update(handlers)
1001
 
 
1002
 
    if not os.path.exists(to_path):
1003
 
        real_handlers['directory'](from_path, to_path)
1004
 
 
1005
 
    for dir_info, entries in walkdirs(from_path, prefix=to_path):
1006
 
        for relpath, name, kind, st, abspath in entries:
1007
 
            real_handlers[kind](abspath, relpath)
1008
 
 
1009
 
 
1010
 
def path_prefix_key(path):
1011
 
    """Generate a prefix-order path key for path.
1012
 
 
1013
 
    This can be used to sort paths in the same way that walkdirs does.
1014
 
    """
1015
 
    return (dirname(path) , path)
1016
 
 
1017
 
 
1018
 
def compare_paths_prefix_order(path_a, path_b):
1019
 
    """Compare path_a and path_b to generate the same order walkdirs uses."""
1020
 
    key_a = path_prefix_key(path_a)
1021
 
    key_b = path_prefix_key(path_b)
1022
 
    return cmp(key_a, key_b)
1023
 
 
1024
 
 
1025
 
_cached_user_encoding = None
1026
 
 
1027
 
 
1028
 
def get_user_encoding():
1029
 
    """Find out what the preferred user encoding is.
1030
 
 
1031
 
    This is generally the encoding that is used for command line parameters
1032
 
    and file contents. This may be different from the terminal encoding
1033
 
    or the filesystem encoding.
1034
 
 
1035
 
    :return: A string defining the preferred user encoding
1036
 
    """
1037
 
    global _cached_user_encoding
1038
 
    if _cached_user_encoding is not None:
1039
 
        return _cached_user_encoding
1040
 
 
1041
 
    if sys.platform == 'darwin':
1042
 
        # work around egregious python 2.4 bug
1043
 
        sys.platform = 'posix'
1044
 
        try:
1045
 
            import locale
1046
 
        finally:
1047
 
            sys.platform = 'darwin'
1048
 
    else:
1049
 
        import locale
1050
 
 
1051
 
    try:
1052
 
        _cached_user_encoding = locale.getpreferredencoding()
1053
 
    except locale.Error, e:
1054
 
        sys.stderr.write('bzr: warning: %s\n'
1055
 
                         '  Could not determine what text encoding to use.\n'
1056
 
                         '  This error usually means your Python interpreter\n'
1057
 
                         '  doesn\'t support the locale set by $LANG (%s)\n'
1058
 
                         "  Continuing with ascii encoding.\n"
1059
 
                         % (e, os.environ.get('LANG')))
1060
 
 
1061
 
    if _cached_user_encoding is None:
1062
 
        _cached_user_encoding = 'ascii'
1063
 
    return _cached_user_encoding