~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Robert Collins
  • Date: 2006-01-02 22:37:32 UTC
  • mfrom: (1185.50.33 bzr-jam-integration)
  • Revision ID: robertc@robertcollins.net-20060102223732-d5221b37ff0f7888
Merge in John Meinels integration branch.

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 IOError, e:
 
142
        # RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
 
143
        # function raises an IOError with errno == None when a rename fails.
 
144
        # This then gets caught here.
 
145
        if e.errno is not None:
 
146
            raise
 
147
    except Exception, e:
 
148
        if (not hasattr(e, 'errno') 
 
149
            or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
 
150
            raise
 
151
    else:
 
152
        file_existed = True
 
153
 
 
154
    success = False
 
155
    try:
 
156
        # This may throw an exception, in which case success will
 
157
        # not be set.
 
158
        rename_func(old, new)
 
159
        success = True
 
160
    finally:
 
161
        if file_existed:
 
162
            # If the file used to exist, rename it back into place
 
163
            # otherwise just delete it from the tmp location
 
164
            if success:
 
165
                unlink_func(tmp_name)
 
166
            else:
 
167
                rename_func(tmp_name, new)
 
168
 
 
169
# Default is to just use the python builtins
 
170
abspath = os.path.abspath
 
171
realpath = os.path.realpath
 
172
pathjoin = os.path.join
 
173
normpath = os.path.normpath
 
174
getcwd = os.getcwdu
 
175
mkdtemp = tempfile.mkdtemp
 
176
rename = os.rename
 
177
dirname = os.path.dirname
 
178
basename = os.path.basename
122
179
 
123
180
if os.name == "posix":
124
181
    # In Python 2.4.2 and older, os.path.abspath and os.path.realpath
128
185
    _fs_enc = sys.getfilesystemencoding()
129
186
    def abspath(path):
130
187
        return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
 
188
 
131
189
    def realpath(path):
132
190
        return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
133
 
else:
 
191
 
 
192
if sys.platform == 'win32':
134
193
    # We need to use the Unicode-aware os.path.abspath and
135
194
    # os.path.realpath on Windows systems.
136
 
    abspath = os.path.abspath
137
 
    realpath = os.path.realpath
 
195
    def abspath(path):
 
196
        return os.path.abspath(path).replace('\\', '/')
 
197
 
 
198
    def realpath(path):
 
199
        return os.path.realpath(path).replace('\\', '/')
 
200
 
 
201
    def pathjoin(*args):
 
202
        return os.path.join(*args).replace('\\', '/')
 
203
 
 
204
    def normpath(path):
 
205
        return os.path.normpath(path).replace('\\', '/')
 
206
 
 
207
    def getcwd():
 
208
        return os.getcwdu().replace('\\', '/')
 
209
 
 
210
    def mkdtemp(*args, **kwargs):
 
211
        return tempfile.mkdtemp(*args, **kwargs).replace('\\', '/')
 
212
 
 
213
    def rename(old, new):
 
214
        fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
 
215
 
 
216
 
 
217
def normalizepath(f):
 
218
    if hasattr(os.path, 'realpath'):
 
219
        F = realpath
 
220
    else:
 
221
        F = abspath
 
222
    [p,e] = os.path.split(f)
 
223
    if e == "" or e == "." or e == "..":
 
224
        return F(f)
 
225
    else:
 
226
        return pathjoin(F(p), e)
 
227
 
138
228
 
139
229
def backup_file(fn):
140
230
    """Copy a file to a backup.
163
253
    finally:
164
254
        outf.close()
165
255
 
166
 
if os.name == 'nt':
167
 
    import shutil
168
 
    rename = shutil.move
169
 
else:
170
 
    rename = os.rename
171
 
 
172
256
 
173
257
def isdir(f):
174
258
    """True if f is an accessible directory."""
195
279
def is_inside(dir, fname):
196
280
    """True if fname is inside dir.
197
281
    
198
 
    The parameters should typically be passed to os.path.normpath first, so
 
282
    The parameters should typically be passed to osutils.normpath first, so
199
283
    that . and .. and repeated slashes are eliminated, and the separators
200
284
    are canonical for the platform.
201
285
    
202
286
    The empty string as a dir name is taken as top-of-tree and matches 
203
287
    everything.
204
288
    
205
 
    >>> is_inside('src', os.path.join('src', 'foo.c'))
 
289
    >>> is_inside('src', pathjoin('src', 'foo.c'))
206
290
    True
207
291
    >>> is_inside('src', 'srccontrol')
208
292
    False
209
 
    >>> is_inside('src', os.path.join('src', 'a', 'a', 'a', 'foo.c'))
 
293
    >>> is_inside('src', pathjoin('src', 'a', 'a', 'a', 'foo.c'))
210
294
    True
211
295
    >>> is_inside('foo.c', 'foo.c')
212
296
    True
223
307
    if dir == '':
224
308
        return True
225
309
 
226
 
    if dir[-1] != os.sep:
227
 
        dir += os.sep
 
310
    if dir[-1] != '/':
 
311
        dir += '/'
228
312
 
229
313
    return fname.startswith(dir)
230
314
 
407
491
    for f in p:
408
492
        if (f == '..') or (f == None) or (f == ''):
409
493
            raise BzrError("sorry, %r not allowed in path" % f)
410
 
    return os.path.join(*p)
 
494
    return pathjoin(*p)
411
495
 
412
496
 
413
497
def appendpath(p1, p2):
414
498
    if p1 == '':
415
499
        return p2
416
500
    else:
417
 
        return os.path.join(p1, p2)
 
501
        return pathjoin(p1, p2)
418
502
    
419
503
 
420
504
def split_lines(s):
488
572
        # to branch anymore.
489
573
        raise PathNotChild(rp, base)
490
574
 
491
 
    return os.sep.join(s)
492
 
 
 
575
    if s:
 
576
        return pathjoin(*s)
 
577
    else:
 
578
        return ''
493
579
 
494
580
 
495
581
def terminal_width():