~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

 * bzr add now lists how many files were ignored per glob.  add --verbose
   lists the specific files.  (Aaron Bentley)

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
import sys
29
29
import time
30
30
import types
31
 
import tempfile
32
31
 
33
32
import bzrlib
34
 
from bzrlib.errors import (BzrError,
35
 
                           BzrBadParameterNotUnicode,
36
 
                           NoSuchFile,
37
 
                           PathNotChild,
38
 
                           )
 
33
from bzrlib.errors import BzrError, PathNotChild
39
34
from bzrlib.trace import mutter
40
35
 
41
36
 
102
97
        raise BzrError('invalid file kind %r' % kind)
103
98
 
104
99
def lexists(f):
105
 
    if hasattr(os.path, 'lexists'):
106
 
        return os.path.lexists(f)
107
100
    try:
108
101
        if hasattr(os, 'lstat'):
109
102
            os.lstat(f)
116
109
        else:
117
110
            raise BzrError("lstat/stat of (%r): %r" % (f, e))
118
111
 
119
 
def fancy_rename(old, new, rename_func, unlink_func):
120
 
    """A fancy rename, when you don't have atomic rename.
121
 
    
122
 
    :param old: The old path, to rename from
123
 
    :param new: The new path, to rename to
124
 
    :param rename_func: The potentially non-atomic rename function
125
 
    :param unlink_func: A way to delete the target file if the full rename succeeds
126
 
    """
127
 
 
128
 
    # sftp rename doesn't allow overwriting, so play tricks:
129
 
    import random
130
 
    base = os.path.basename(new)
131
 
    dirname = os.path.dirname(new)
132
 
    tmp_name = u'tmp.%s.%.9f.%d.%s' % (base, time.time(), os.getpid(), rand_chars(10))
133
 
    tmp_name = pathjoin(dirname, tmp_name)
134
 
 
135
 
    # Rename the file out of the way, but keep track if it didn't exist
136
 
    # We don't want to grab just any exception
137
 
    # something like EACCES should prevent us from continuing
138
 
    # The downside is that the rename_func has to throw an exception
139
 
    # with an errno = ENOENT, or NoSuchFile
140
 
    file_existed = False
141
 
    try:
142
 
        rename_func(new, tmp_name)
143
 
    except (NoSuchFile,), e:
144
 
        pass
145
 
    except IOError, e:
146
 
        # RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
147
 
        # function raises an IOError with errno == None when a rename fails.
148
 
        # This then gets caught here.
149
 
        if e.errno not in (None, errno.ENOENT, errno.ENOTDIR):
150
 
            raise
151
 
    except Exception, e:
152
 
        if (not hasattr(e, 'errno') 
153
 
            or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
154
 
            raise
155
 
    else:
156
 
        file_existed = True
157
 
 
158
 
    success = False
159
 
    try:
160
 
        # This may throw an exception, in which case success will
161
 
        # not be set.
162
 
        rename_func(old, new)
163
 
        success = True
164
 
    finally:
165
 
        if file_existed:
166
 
            # If the file used to exist, rename it back into place
167
 
            # otherwise just delete it from the tmp location
168
 
            if success:
169
 
                unlink_func(tmp_name)
170
 
            else:
171
 
                rename_func(tmp_name, new)
172
 
 
173
 
# Default is to just use the python builtins
174
 
abspath = os.path.abspath
175
 
realpath = os.path.realpath
176
 
pathjoin = os.path.join
177
 
normpath = os.path.normpath
178
 
getcwd = os.getcwdu
179
 
mkdtemp = tempfile.mkdtemp
180
 
rename = os.rename
181
 
dirname = os.path.dirname
182
 
basename = os.path.basename
 
112
def normalizepath(f):
 
113
    if hasattr(os.path, 'realpath'):
 
114
        F = os.path.realpath
 
115
    else:
 
116
        F = os.path.abspath
 
117
    [p,e] = os.path.split(f)
 
118
    if e == "" or e == "." or e == "..":
 
119
        return F(f)
 
120
    else:
 
121
        return os.path.join(F(p), e)
183
122
 
184
123
if os.name == "posix":
185
124
    # In Python 2.4.2 and older, os.path.abspath and os.path.realpath
189
128
    _fs_enc = sys.getfilesystemencoding()
190
129
    def abspath(path):
191
130
        return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
192
 
 
193
131
    def realpath(path):
194
132
        return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
195
 
 
196
 
if sys.platform == 'win32':
 
133
else:
197
134
    # We need to use the Unicode-aware os.path.abspath and
198
135
    # os.path.realpath on Windows systems.
199
 
    def abspath(path):
200
 
        return os.path.abspath(path).replace('\\', '/')
201
 
 
202
 
    def realpath(path):
203
 
        return os.path.realpath(path).replace('\\', '/')
204
 
 
205
 
    def pathjoin(*args):
206
 
        return os.path.join(*args).replace('\\', '/')
207
 
 
208
 
    def normpath(path):
209
 
        return os.path.normpath(path).replace('\\', '/')
210
 
 
211
 
    def getcwd():
212
 
        return os.getcwdu().replace('\\', '/')
213
 
 
214
 
    def mkdtemp(*args, **kwargs):
215
 
        return tempfile.mkdtemp(*args, **kwargs).replace('\\', '/')
216
 
 
217
 
    def rename(old, new):
218
 
        fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
219
 
 
220
 
 
221
 
def normalizepath(f):
222
 
    if hasattr(os.path, 'realpath'):
223
 
        F = realpath
224
 
    else:
225
 
        F = abspath
226
 
    [p,e] = os.path.split(f)
227
 
    if e == "" or e == "." or e == "..":
228
 
        return F(f)
229
 
    else:
230
 
        return pathjoin(F(p), e)
231
 
 
 
136
    abspath = os.path.abspath
 
137
    realpath = os.path.realpath
232
138
 
233
139
def backup_file(fn):
234
140
    """Copy a file to a backup.
257
163
    finally:
258
164
        outf.close()
259
165
 
 
166
if os.name == 'nt':
 
167
    import shutil
 
168
    rename = shutil.move
 
169
else:
 
170
    rename = os.rename
 
171
 
260
172
 
261
173
def isdir(f):
262
174
    """True if f is an accessible directory."""
283
195
def is_inside(dir, fname):
284
196
    """True if fname is inside dir.
285
197
    
286
 
    The parameters should typically be passed to osutils.normpath first, so
 
198
    The parameters should typically be passed to os.path.normpath first, so
287
199
    that . and .. and repeated slashes are eliminated, and the separators
288
200
    are canonical for the platform.
289
201
    
290
202
    The empty string as a dir name is taken as top-of-tree and matches 
291
203
    everything.
292
204
    
293
 
    >>> is_inside('src', pathjoin('src', 'foo.c'))
 
205
    >>> is_inside('src', os.path.join('src', 'foo.c'))
294
206
    True
295
207
    >>> is_inside('src', 'srccontrol')
296
208
    False
297
 
    >>> is_inside('src', pathjoin('src', 'a', 'a', 'a', 'foo.c'))
 
209
    >>> is_inside('src', os.path.join('src', 'a', 'a', 'a', 'foo.c'))
298
210
    True
299
211
    >>> is_inside('foo.c', 'foo.c')
300
212
    True
311
223
    if dir == '':
312
224
        return True
313
225
 
314
 
    if dir[-1] != '/':
315
 
        dir += '/'
 
226
    if dir[-1] != os.sep:
 
227
        dir += os.sep
316
228
 
317
229
    return fname.startswith(dir)
318
230
 
336
248
        tofile.write(b)
337
249
 
338
250
 
339
 
def file_iterator(input_file, readsize=32768):
340
 
    while True:
341
 
        b = input_file.read(readsize)
342
 
        if len(b) == 0:
343
 
            break
344
 
        yield b
345
 
 
346
 
 
347
251
def sha_file(f):
348
252
    if hasattr(f, 'tell'):
349
253
        assert f.tell() == 0
441
345
    """Return size of given open file."""
442
346
    return os.fstat(f.fileno())[ST_SIZE]
443
347
 
444
 
 
445
348
# Define rand_bytes based on platform.
446
349
try:
447
350
    # Python 2.4 and later have os.urandom,
464
367
                n -= 1
465
368
            return s
466
369
 
467
 
 
468
 
ALNUM = '0123456789abcdefghijklmnopqrstuvwxyz'
469
 
def rand_chars(num):
470
 
    """Return a random string of num alphanumeric characters
471
 
    
472
 
    The result only contains lowercase chars because it may be used on 
473
 
    case-insensitive filesystems.
474
 
    """
475
 
    s = ''
476
 
    for raw_byte in rand_bytes(num):
477
 
        s += ALNUM[ord(raw_byte) % 36]
478
 
    return s
479
 
 
480
 
 
481
370
## TODO: We could later have path objects that remember their list
482
371
## decomposition (might be too tricksy though.)
483
372
 
518
407
    for f in p:
519
408
        if (f == '..') or (f == None) or (f == ''):
520
409
            raise BzrError("sorry, %r not allowed in path" % f)
521
 
    return pathjoin(*p)
 
410
    return os.path.join(*p)
522
411
 
523
412
 
524
413
def appendpath(p1, p2):
525
414
    if p1 == '':
526
415
        return p2
527
416
    else:
528
 
        return pathjoin(p1, p2)
 
417
        return os.path.join(p1, p2)
529
418
    
530
419
 
531
420
def split_lines(s):
599
488
        # to branch anymore.
600
489
        raise PathNotChild(rp, base)
601
490
 
602
 
    if s:
603
 
        return pathjoin(*s)
604
 
    else:
605
 
        return ''
606
 
 
607
 
 
608
 
def safe_unicode(unicode_or_utf8_string):
609
 
    """Coerce unicode_or_utf8_string into unicode.
610
 
 
611
 
    If it is unicode, it is returned.
612
 
    Otherwise it is decoded from utf-8. If a decoding error
613
 
    occurs, it is wrapped as a If the decoding fails, the exception is wrapped 
614
 
    as a BzrBadParameter exception.
615
 
    """
616
 
    if isinstance(unicode_or_utf8_string, unicode):
617
 
        return unicode_or_utf8_string
618
 
    try:
619
 
        return unicode_or_utf8_string.decode('utf8')
620
 
    except UnicodeDecodeError:
621
 
        raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
 
491
    return os.sep.join(s)
 
492
 
622
493
 
623
494
 
624
495
def terminal_width():
633
504
        return int(os.environ['COLUMNS'])
634
505
    except (IndexError, KeyError, ValueError):
635
506
        return 80
636
 
 
637
 
def supports_executable():
638
 
    return sys.platform != "win32"