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):
161
179
mutter("FTP has not: %s" % self._abspath(relpath))
164
def get(self, relpath, decode=False):
182
def get(self, relpath, decode=False, retries=0):
165
183
"""Get the file at the given relative path.
167
185
:param relpath: The relative path to the file
186
:param retries: Number of retries after temporary failures so far
169
189
We're meant to return a file-like object which bzr will
170
190
then read from. For now we do this via the magic of StringIO
192
# TODO: decode should be deprecated
173
194
mutter("FTP get: %s" % self._abspath(relpath))
174
195
f = self._get_FTP()
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):
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.
226
:param fp: File-like or string object.
227
:param retries: Number of retries after temporary failures so far
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))
188
234
if not hasattr(fp, 'read'):
189
235
fp = StringIO(fp)
191
237
mutter("FTP put: %s" % self._abspath(relpath))
192
238
f = self._get_FTP()
193
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"
194
251
except ftplib.error_perm, e:
195
raise TransportError(orig_error=e)
197
def mkdir(self, relpath):
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)
276
def mkdir(self, relpath, mode=None):
198
277
"""Create a directory at the given path."""
200
279
mutter("FTP mkd: %s" % self._abspath(relpath))