24
24
active, in which case aftp:// will be your friend.
27
from bzrlib.transport import Transport
29
from bzrlib.errors import (TransportNotPossible, TransportError,
30
NoSuchFile, FileExists)
33
27
from cStringIO import StringIO
39
from bzrlib.branch import Branch
40
from bzrlib.trace import mutter
36
from warnings import warn
39
from bzrlib.transport import Transport
40
from bzrlib.errors import (TransportNotPossible, TransportError,
41
NoSuchFile, FileExists)
42
from bzrlib.trace import mutter, warning
46
def _find_FTP(hostname, username, password, is_active):
47
"""Find an ftplib.FTP instance attached to this triplet."""
48
key = "%s|%s|%s|%s" % (hostname, username, password, is_active)
49
if key not in _FTP_cache:
50
mutter("Constructing FTP instance against %r" % key)
51
_FTP_cache[key] = ftplib.FTP(hostname, username, password)
52
_FTP_cache[key].set_pasv(not is_active)
53
return _FTP_cache[key]
56
class FtpTransportError(TransportError):
43
60
class FtpStatResult(object):
179
200
except ftplib.error_perm, e:
180
raise NoSuchFile(self.abspath(relpath), extra=extra)
201
raise NoSuchFile(self.abspath(relpath), extra=str(e))
202
except ftplib.error_temp, e:
203
if retries > _number_of_retries:
204
raise TransportError(msg="FTP temporary error during GET %s. Aborting."
205
% self.abspath(relpath),
208
warning("FTP temporary error: %s. Retrying." % str(e))
209
self._FTP_instance = None
210
return self.get(relpath, decode, retries+1)
212
if retries > _number_of_retries:
213
raise TransportError("FTP control connection closed during GET %s."
214
% self.abspath(relpath),
217
warning("FTP control connection closed. Trying to reopen.")
218
time.sleep(_sleep_between_retries)
219
self._FTP_instance = None
220
return self.get(relpath, decode, retries+1)
182
def put(self, relpath, fp, mode=None):
222
def put(self, relpath, fp, mode=None, retries=0):
183
223
"""Copy the file-like or string object into the location.
185
225
:param relpath: Location to put the contents, relative to base.
186
:param f: File-like or string object.
187
TODO: jam 20051215 This should be an atomic put, not overwritting files in place
226
:param fp: File-like or string object.
227
:param retries: Number of retries after temporary failures so far
188
230
TODO: jam 20051215 ftp as a protocol seems to support chmod, but ftplib does not
232
tmp_abspath = '%s.tmp.%.9f.%d.%d' % (self._abspath(relpath), time.time(),
233
os.getpid(), random.randint(0,0x7FFFFFFF))
190
234
if not hasattr(fp, 'read'):
191
235
fp = StringIO(fp)
193
237
mutter("FTP put: %s" % self._abspath(relpath))
194
238
f = self._get_FTP()
195
f.storbinary('STOR '+self._abspath(relpath), fp, 8192)
240
f.storbinary('STOR '+tmp_abspath, fp)
241
f.rename(tmp_abspath, self._abspath(relpath))
242
except (ftplib.error_temp,EOFError), e:
243
warning("Failure during ftp PUT. Deleting temporary file.")
245
f.delete(tmp_abspath)
247
warning("Failed to delete temporary file on the server.\nFile: %s"
196
251
except ftplib.error_perm, e:
197
raise TransportError(orig_error=e)
252
if "no such file" in str(e).lower():
253
raise NoSuchFile("Error storing %s: %s"
254
% (self.abspath(relpath), str(e)), extra=e)
256
raise FtpTransportError(orig_error=e)
257
except ftplib.error_temp, e:
258
if retries > _number_of_retries:
259
raise TransportError("FTP temporary error during PUT %s. Aborting."
260
% self.abspath(relpath), orig_error=e)
262
warning("FTP temporary error: %s. Retrying." % str(e))
263
self._FTP_instance = None
264
self.put(relpath, fp, mode, retries+1)
266
if retries > _number_of_retries:
267
raise TransportError("FTP control connection closed during PUT %s."
268
% self.abspath(relpath), orig_error=e)
270
warning("FTP control connection closed. Trying to reopen.")
271
time.sleep(_sleep_between_retries)
272
self._FTP_instance = None
273
self.put(relpath, fp, mode, retries+1)
199
276
def mkdir(self, relpath, mode=None):
200
277
"""Create a directory at the given path."""