~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

[merge] jam-integration 1490

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
31
32
 
32
33
import bzrlib
33
 
from bzrlib.errors import BzrError, PathNotChild
 
34
from bzrlib.errors import BzrError, PathNotChild, NoSuchFile
34
35
from bzrlib.trace import mutter
35
36
 
36
37
 
97
98
        raise BzrError('invalid file kind %r' % kind)
98
99
 
99
100
def lexists(f):
 
101
    if hasattr(os.path, 'lexists'):
 
102
        return os.path.lexists(f)
100
103
    try:
101
104
        if hasattr(os, 'lstat'):
102
105
            os.lstat(f)
109
112
        else:
110
113
            raise BzrError("lstat/stat of (%r): %r" % (f, e))
111
114
 
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)
 
115
def fancy_rename(old, new, rename_func, unlink_func):
 
116
    """A fancy rename, when you don't have atomic rename.
 
117
    
 
118
    :param old: The old path, to rename from
 
119
    :param new: The new path, to rename to
 
120
    :param rename_func: The potentially non-atomic rename function
 
121
    :param unlink_func: A way to delete the target file if the full rename succeeds
 
122
    """
 
123
 
 
124
    # sftp rename doesn't allow overwriting, so play tricks:
 
125
    import random
 
126
    base = os.path.basename(new)
 
127
    dirname = os.path.dirname(new)
 
128
    tmp_name = u'tmp.%s.%.9f.%d.%d' % (base, time.time(), os.getpid(), random.randint(0, 0x7FFFFFFF))
 
129
    tmp_name = pathjoin(dirname, tmp_name)
 
130
 
 
131
    # Rename the file out of the way, but keep track if it didn't exist
 
132
    # We don't want to grab just any exception
 
133
    # something like EACCES should prevent us from continuing
 
134
    # The downside is that the rename_func has to throw an exception
 
135
    # with an errno = ENOENT, or NoSuchFile
 
136
    file_existed = False
 
137
    try:
 
138
        rename_func(new, tmp_name)
 
139
    except (NoSuchFile,), e:
 
140
        pass
 
141
    except Exception, e:
 
142
        if (not hasattr(e, 'errno') 
 
143
            or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
 
144
            raise
 
145
    else:
 
146
        file_existed = True
 
147
 
 
148
    success = False
 
149
    try:
 
150
        # This may throw an exception, in which case success will
 
151
        # not be set.
 
152
        rename_func(old, new)
 
153
        success = True
 
154
    finally:
 
155
        if file_existed:
 
156
            # If the file used to exist, rename it back into place
 
157
            # otherwise just delete it from the tmp location
 
158
            if success:
 
159
                unlink_func(tmp_name)
 
160
            else:
 
161
                rename_func(tmp_name, new)
 
162
 
 
163
# Default is to just use the python builtins
 
164
abspath = os.path.abspath
 
165
realpath = os.path.realpath
 
166
pathjoin = os.path.join
 
167
normpath = os.path.normpath
 
168
getcwd = os.getcwdu
 
169
mkdtemp = tempfile.mkdtemp
 
170
rename = os.rename
 
171
dirname = os.path.dirname
 
172
basename = os.path.basename
122
173
 
123
174
if os.name == "posix":
124
175
    # In Python 2.4.2 and older, os.path.abspath and os.path.realpath
128
179
    _fs_enc = sys.getfilesystemencoding()
129
180
    def abspath(path):
130
181
        return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
 
182
 
131
183
    def realpath(path):
132
184
        return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
133
 
else:
 
185
 
 
186
if sys.platform == 'win32':
134
187
    # We need to use the Unicode-aware os.path.abspath and
135
188
    # os.path.realpath on Windows systems.
136
 
    abspath = os.path.abspath
137
 
    realpath = os.path.realpath
 
189
    def abspath(path):
 
190
        return os.path.abspath(path).replace('\\', '/')
 
191
 
 
192
    def realpath(path):
 
193
        return os.path.realpath(path).replace('\\', '/')
 
194
 
 
195
    def pathjoin(*args):
 
196
        return os.path.join(*args).replace('\\', '/')
 
197
 
 
198
    def normpath(path):
 
199
        return os.path.normpath(path).replace('\\', '/')
 
200
 
 
201
    def getcwd():
 
202
        return os.getcwdu().replace('\\', '/')
 
203
 
 
204
    def mkdtemp(*args, **kwargs):
 
205
        return tempfile.mkdtemp(*args, **kwargs).replace('\\', '/')
 
206
 
 
207
    def rename(old, new):
 
208
        fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
 
209
 
 
210
def normalizepath(f):
 
211
    if hasattr(os.path, 'realpath'):
 
212
        F = realpath
 
213
    else:
 
214
        F = abspath
 
215
    [p,e] = os.path.split(f)
 
216
    if e == "" or e == "." or e == "..":
 
217
        return F(f)
 
218
    else:
 
219
        return pathjoin(F(p), e)
 
220
 
138
221
 
139
222
def backup_file(fn):
140
223
    """Copy a file to a backup.
163
246
    finally:
164
247
        outf.close()
165
248
 
166
 
if os.name == 'nt':
167
 
    import shutil
168
 
    rename = shutil.move
169
 
else:
170
 
    rename = os.rename
171
 
 
172
249
 
173
250
def isdir(f):
174
251
    """True if f is an accessible directory."""
195
272
def is_inside(dir, fname):
196
273
    """True if fname is inside dir.
197
274
    
198
 
    The parameters should typically be passed to os.path.normpath first, so
 
275
    The parameters should typically be passed to osutils.normpath first, so
199
276
    that . and .. and repeated slashes are eliminated, and the separators
200
277
    are canonical for the platform.
201
278
    
202
279
    The empty string as a dir name is taken as top-of-tree and matches 
203
280
    everything.
204
281
    
205
 
    >>> is_inside('src', os.path.join('src', 'foo.c'))
 
282
    >>> is_inside('src', pathjoin('src', 'foo.c'))
206
283
    True
207
284
    >>> is_inside('src', 'srccontrol')
208
285
    False
209
 
    >>> is_inside('src', os.path.join('src', 'a', 'a', 'a', 'foo.c'))
 
286
    >>> is_inside('src', pathjoin('src', 'a', 'a', 'a', 'foo.c'))
210
287
    True
211
288
    >>> is_inside('foo.c', 'foo.c')
212
289
    True
223
300
    if dir == '':
224
301
        return True
225
302
 
226
 
    if dir[-1] != os.sep:
227
 
        dir += os.sep
 
303
    if dir[-1] != '/':
 
304
        dir += '/'
228
305
 
229
306
    return fname.startswith(dir)
230
307
 
407
484
    for f in p:
408
485
        if (f == '..') or (f == None) or (f == ''):
409
486
            raise BzrError("sorry, %r not allowed in path" % f)
410
 
    return os.path.join(*p)
 
487
    return pathjoin(*p)
411
488
 
412
489
 
413
490
def appendpath(p1, p2):
414
491
    if p1 == '':
415
492
        return p2
416
493
    else:
417
 
        return os.path.join(p1, p2)
 
494
        return pathjoin(p1, p2)
418
495
    
419
496
 
420
497
def split_lines(s):
488
565
        # to branch anymore.
489
566
        raise PathNotChild(rp, base)
490
567
 
491
 
    return os.sep.join(s)
492
 
 
 
568
    if s:
 
569
        return pathjoin(*s)
 
570
    else:
 
571
        return ''
493
572
 
494
573
 
495
574
def terminal_width():