[Twisted-Python] RE: How do I upload files using FTPClient?

Daniel Burr ldanielburr at earthlink.net
Tue Jan 14 15:43:02 MST 2003


WARNING!  Long-winded, and perhaps clueless post ahead!

As discussed earlier in this thread, here is a rough sketch of how I think
a high-level interface to twisted.protocols.ftp.FTPClient should look.

Currently, twisted.protocols.ftp contains mostly FTP-related classes; there
is only one module-level method, "parsePWDResponse".  I am thinking that we
could just implement the high-level client interface as methods within the
current twisted.protocols.ftp module.

Ideally, I'd like to be able to use it like so:

---------------------------------------------------------------------------
import os

from twisted.protocols import ftp

def deploy(self, appRoot, filePaths):
        """
        Deploy code to an ftp server

        appRoot - string containing the root directory of some app
        filePaths - list or tuple containing absolute filepaths
        """

        try:
            ftp.connect(host='foo.bar.org',
                        port=21,
                        user='anonymous'
                        password='anonymous at anonymous.net')

        except ftp.ConnectionFailed:
            return False

        else:
            for filePath in filePaths:
                fileDirectory, fileName = os.path.split(filePath)
                uploadDirectory = '%s/%s' % (appRoot, fileDirectory)

                if ftp.pwd() != (uploadDirectory):
                    try:
                        ftp.cd(uploadDirectory)

                    except ftp.DirDoesNotExist:
                        makeDirectory(uploadDirectory)
                        ftp.cd(uploadDirectory)

                    ftp.binary()
                    ftp.put(fileName)

            ftp.quit()
            return True

def makeDirectory(self, path):
    """
    Recursively create directories on the FTP server
    """

    parent, base = os.path.split(path)

    try:
        ftp.cd(parent)
        ftp.mkdir(base)

    except ftp.DirDoesNotExist:
        makeDirectory(parent)
        ftp.cd(parent)
        ftp.mkdir(base)
---------------------------------------------------------------------------


This code is pretty naive, to be sure, and not at all Twisted.  However, it
does hopefully illustrate the way I would like to interact with an FTP
client.  The "deploy" function could be refactored to use Deferreds pretty
easily; the "makeDirectory" function would be similarly simple.


As for the interface to FTPClient, I've outlined my thoughts below.  Note
that my immediate itch only calls for "cd", "pwd", "mkdir", and "put", of
which only "mkdir" and "put" need to be implemented.  It'd be cool, down
the road, to implement "mget" and friends, so that my sample code above
could be reduced even further, by removing the need to loop through the
list of file paths and transfer files one at a time.

My overall, grand ambition is to tie all of this together into a handy
code deployment tool that can pull versioned code from multiple source
control solutions, and deploy it using a variety of protocols.


---------------------------------------------------------------------------
# a module level variable to which we will bind an instance of FTPClient
theClient = None

def append(localFile, remoteFile=localFile)
    """
    Append the contents of a file on the client to a file on the server

    localFile  - string containing the name of the file on the client
    remoteFile - string containing the name of the file on the server;
                 defaults to the same name as the file on the client
    """

def ascii():
    """
    Set the transfer type to ascii (TYPE A)
    """

def binary():
    """
    Set the transfer type to binary (TYPE I)
    """

def bye():
    """
    Close the connection and exit the client
    """

def cd(remoteDirectory):
    """
    Change directory on the server (CWD)

    remoteDirectory - string containing the directory name
    """

def close():
    """
    Close the connection, but do not exit the client
    """

def connect(host, port, userID='anonymous', password='foo at bar.org'):
    """
    Connect to an FTP server

    This function will create an instance of FTPClient and bind it to
    "theClient", a module-level variable which we will treat like a
    singleton, so all these functions have an instance to work with
    """

def delete(remoteFile):
    """
    Delete a file from the server

    remoteFile - string containing the name of the file on the server
    """

def debug():
    """
    Toggle debugging mode on/off

    If debugging mode is on, debug info will be logged to some default file
    """

def disconnect():
    """
    Close the connection, but do not exit the client
    """

def get(remoteFile, localFile=remoteFile):
    """
    Transfer a file from the server to the client
    
    remoteFile - string containing the name of the file on the server
    localFile  - string containing the name of the file on the client;
                 defaults to the same name as the file on the server
    """

def literal(command):
    """
    Send arbitrary FTP command

    command - string containing the line to be sent as a command
    """

def mdelete(remoteFiles):
    """
    Delete multiple files from the server

    remoteFiles - list or tuple of filepaths on the server
    """

def mget(remoteFiles):
    """
    Transfer multiple files from the server to the client

    remoteFiles - list or tuple of filepaths on the server
    """

def mkdir(directory):
    """
    Create a directory on the server

    directory - string containing an absolute or relative path
    """

def mput(localFiles):
    """
    Transfer multiple file from the client to the server

    localFiles - list or tuple of filepaths on the client
    """

def put(localFile, remoteFile=localFile):
    """
    Transfer a file from the client to the server

    localFile  - string containing the name of the file on the client
    remoteFile - string containing the name of the file on the server;
                 defaults to the same name as the file on the client
    """

def quit():
    """
    Close the connection and exit the client
    """

def rmdir(directory):
    """
    Delete a directory on the server

    directory - string containing an absolute or relative path
    """
---------------------------------------------------------------------------


Well, that's my brain dump for now.  Be as harsh as you like in criticizing;
I have a thick hide, and I'm trying to learn, so don't pull any punches.
If anyone thinks it should be entirely different, that's fantastic, and I
want to hear about it.

L. Daniel Burr





More information about the Twisted-Python mailing list