~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: John Arbash Meinel
  • Date: 2005-12-01 22:50:26 UTC
  • mto: (1185.50.19 bzr-jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1532.
  • Revision ID: john@arbash-meinel.com-20051201225026-142c9a5a04bb38ba
Added a fancy footwork rename to osutils, made SftpTransport use it.

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
import tempfile
32
32
 
33
33
import bzrlib
34
 
from bzrlib.errors import BzrError, PathNotChild
 
34
from bzrlib.errors import BzrError, PathNotChild, NoSuchFile
35
35
from bzrlib.trace import mutter
36
36
 
37
37
 
112
112
        else:
113
113
            raise BzrError("lstat/stat of (%r): %r" % (f, e))
114
114
 
 
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, final_path)
 
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
 
173
 
115
174
if os.name == "posix":
116
175
    # In Python 2.4.2 and older, os.path.abspath and os.path.realpath
117
176
    # choke on a Unicode string containing a relative path if
120
179
    _fs_enc = sys.getfilesystemencoding()
121
180
    def abspath(path):
122
181
        return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
 
182
 
123
183
    def realpath(path):
124
184
        return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
125
 
    pathjoin = os.path.join
126
 
    normpath = os.path.normpath
127
 
    getcwd = os.getcwdu
128
 
    mkdtemp = tempfile.mkdtemp
129
 
else:
 
185
 
 
186
if sys.platform == 'win32':
130
187
    # We need to use the Unicode-aware os.path.abspath and
131
188
    # os.path.realpath on Windows systems.
132
189
    def abspath(path):
133
190
        return os.path.abspath(path).replace('\\', '/')
 
191
 
134
192
    def realpath(path):
135
193
        return os.path.realpath(path).replace('\\', '/')
 
194
 
136
195
    def pathjoin(*args):
137
196
        return os.path.join(*args).replace('\\', '/')
 
197
 
138
198
    def normpath(path):
139
199
        return os.path.normpath(path).replace('\\', '/')
 
200
 
140
201
    def getcwd():
141
202
        return os.getcwdu().replace('\\', '/')
 
203
 
142
204
    def mkdtemp(*args, **kwargs):
143
205
        return tempfile.mkdtemp(*args, **kwargs).replace('\\', '/')
144
 
# Because these shrink the path, we can use the original
145
 
# versions on any platform
146
 
dirname = os.path.dirname
147
 
basename = os.path.basename
 
206
 
 
207
    def rename(old, new):
 
208
        fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
148
209
 
149
210
def normalizepath(f):
150
211
    if hasattr(os.path, 'realpath'):
185
246
    finally:
186
247
        outf.close()
187
248
 
188
 
if os.name == 'nt':
189
 
    import shutil
190
 
    rename = shutil.move
191
 
else:
192
 
    rename = os.rename
193
 
 
194
249
 
195
250
def isdir(f):
196
251
    """True if f is an accessible directory."""