~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

Exclude more files from dumb-rsync upload

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, NotBranchError
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.%d' % (base, time.time(), os.getpid(), random.randint(0, 0x7FFFFFFF))
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
 
328
240
 
329
241
def pumpfile(fromfile, tofile):
330
242
    """Copy contents of one file to another."""
331
 
    BUFSIZE = 32768
332
 
    while True:
333
 
        b = fromfile.read(BUFSIZE)
334
 
        if not b:
335
 
            break
336
 
        tofile.write(b)
337
 
 
338
 
 
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
 
243
    tofile.write(fromfile.read())
345
244
 
346
245
 
347
246
def sha_file(f):
503
402
    for f in p:
504
403
        if (f == '..') or (f == None) or (f == ''):
505
404
            raise BzrError("sorry, %r not allowed in path" % f)
506
 
    return pathjoin(*p)
 
405
    return os.path.join(*p)
507
406
 
508
407
 
509
408
def appendpath(p1, p2):
510
409
    if p1 == '':
511
410
        return p2
512
411
    else:
513
 
        return pathjoin(p1, p2)
 
412
        return os.path.join(p1, p2)
514
413
    
515
414
 
516
415
def split_lines(s):
582
481
    else:
583
482
        # XXX This should raise a NotChildPath exception, as its not tied
584
483
        # to branch anymore.
585
 
        raise PathNotChild(rp, base)
586
 
 
587
 
    if s:
588
 
        return pathjoin(*s)
589
 
    else:
590
 
        return ''
591
 
 
592
 
 
593
 
def safe_unicode(unicode_or_utf8_string):
594
 
    """Coerce unicode_or_utf8_string into unicode.
595
 
 
596
 
    If it is unicode, it is returned.
597
 
    Otherwise it is decoded from utf-8. If a decoding error
598
 
    occurs, it is wrapped as a If the decoding fails, the exception is wrapped 
599
 
    as a BzrBadParameter exception.
600
 
    """
601
 
    if isinstance(unicode_or_utf8_string, unicode):
602
 
        return unicode_or_utf8_string
603
 
    try:
604
 
        return unicode_or_utf8_string.decode('utf8')
605
 
    except UnicodeDecodeError:
606
 
        raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
607
 
 
608
 
 
609
 
def terminal_width():
610
 
    """Return estimated terminal width."""
611
 
 
612
 
    # TODO: Do something smart on Windows?
613
 
 
614
 
    # TODO: Is there anything that gets a better update when the window
615
 
    # is resized while the program is running? We could use the Python termcap
616
 
    # library.
617
 
    try:
618
 
        return int(os.environ['COLUMNS'])
619
 
    except (IndexError, KeyError, ValueError):
620
 
        return 80
 
484
        raise NotBranchError("path %r is not within branch %r" % (rp, base))
 
485
 
 
486
    return os.sep.join(s)