[PATCH] knit pushing with ftp transport

Alexandre Saint stalst at gmail.com
Mon May 1 16:07:36 BST 2006


On Mon, May 01, 2006 at 07:51:48AM -0500, John Arbash Meinel wrote:
> Alexandre Saint wrote:
> > On Sun, Apr 30, 2006 at 03:34:02PM -0500, John Arbash Meinel wrote:
> >> Alexandre Saint wrote:
> >>> Hello,
> >>>
> >>> This patch adds support for knit format to the ftp transport.
> >>>
> >>> Added an append() method using FTP 'APPE' command.
> >>> Modified copy() to deal well with relative paths (and not only with
> >>> absolute ones).
> >>>
[...] 
> The interface for append() has to be a file-like object (as defined by
> Transport).
> 
> I was suggesting that append() should not be recursive, but should call
> something like _append_helper() which would be recursive, and would
> expect a string.
> 

I misunderstood you. But I agree know.

> We decided on a policy of "no magic interfaces", so we don't want to use
> isinstance() switches if we can help it.
> 
> Oh, and the correct way to determine it is "isinstance(x, str)". If you
> want to compare for both regular strings and unicode strings, you need
> to use "isinstance(x, basestring)", but in this case you don't want to
> accept unicode strings.
> 
> I would do something more like:
> 
> def append(self, relpath, f, mode=None):
> 	if self.has(relpath):
> 		result = ftp.size(abspath)
> 	else:
> 		result = 0
> 	self._append_helper(self._abspath(relpath), f.read(),
> 		mode=mode, retries=0)
> 
> 	return result
> 
> def _append_helper(self, abspath, f, mode=None, retries=0):
> 	"""This is a recursive function that will ..."
> 
> 
> I'm not sure if you need to check the file size each time, or just once.
> But things that don't need to be repeated (like _abspath, and f.read())
> would go into 'append', and the repeating stuff would go into
> _append_helper().
> Or maybe it should be called _try_append()
> 
> John
> =:->
> 

In this patch, added _try_append(), which is recursive.

-- 
alex
-------------- next part --------------
=== modified file 'a/bzrlib/transport/ftp.py'
--- a/bzrlib/transport/ftp.py	
+++ b/bzrlib/transport/ftp.py	
@@ -303,11 +303,64 @@
                 raise TransportError(msg="Cannot remove directory at %s" % \
                         self._abspath(rel_path), extra=str(e))
 
-    def append(self, relpath, f):
+    def append(self, relpath, f, mode=None):
         """Append the text in the file-like object into the final
         location.
         """
-        raise TransportNotPossible('ftp does not support append()')
+        if self.has(relpath):
+	    ftp = self._get_FTP()
+            result = ftp.size(self._abspath(relpath))
+        else:
+            result = 0
+
+	self._try_append(relpath, f.read(), mode)
+
+	return result
+
+    def _try_append(self, relpath, text, mode=None, retries=0):
+        """This is a recursive function that will try to append the text
+	given as argument to the final location until the number of retries
+	exceeds the limit.
+	"""
+        try:
+            abspath = self._abspath(relpath)
+            mutter("FTP appe to %s" % abspath)
+            ftp = self._get_FTP()
+            ftp.voidcmd("TYPE I")
+            cmd = "APPE %s" % abspath
+            conn = ftp.transfercmd(cmd)
+            conn.sendall(text)
+            conn.close()
+            if mode is not None:
+                self._setmode(relpath, mode)
+            ftp.getresp()
+        except ftplib.error_perm, e:
+            FtpTransportError("Error appending data to %s" % abspath,
+                    orig_error=e)
+        except ftplib.error_temp, e:
+            if retries > _number_of_retries:
+                raise TransportError("FTP temporary error during APPEND %s." \
+                        "Aborting." % abspath, orig_error=e)
+            else:
+                warning("FTP temporary error: %s. Retrying." % str(e))
+                self._FTP_instance = None
+                self._try_append(relpath, text, mode, retries+1)
+
+
+    def _setmode(self, relpath, mode):
+        """Set permissions on a path.
+
+        Only set permissions if the FTP server support the 'SITE CHMOD'
+        extension.
+        """
+        try:
+            ftp = self._get_FTP()
+            cmd = "SITE CHMOD %s %s" % (self._abspath(relpath), str(mode))
+            ftp.sendcmd(cmd)
+        except ftplib.error_perm, e:
+            # Command probably not available on this server
+            pass
+
 
     def copy(self, rel_from, rel_to):
         """Copy the item at rel_from to the location at rel_to"""
@@ -347,11 +400,12 @@
             mutter("FTP nlst: %s" % self._abspath(relpath))
             f = self._get_FTP()
             basepath = self._abspath(relpath)
-            # FTP.nlst returns paths prefixed by relpath, strip 'em
-            the_list = f.nlst(basepath)
-            stripped = [path[len(basepath)+1:] for path in the_list]
+            paths = f.nlst(basepath)
+            # If FTP.nlst returns paths prefixed by relpath, strip 'em
+            if paths[0].startswith(basepath):
+                paths = [path[len(basepath)+1:] for path in paths]
             # Remove . and .. if present, and return
-            return [path for path in stripped if path not in (".", "..")]
+            return [path for path in paths if path not in (".", "..")]
         except ftplib.error_perm, e:
             raise TransportError(orig_error=e)
 

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 191 bytes
Desc: not available
Url : https://lists.ubuntu.com/archives/bazaar/attachments/20060501/4e297d2e/attachment.pgp 


More information about the bazaar mailing list