[Twisted-Python] need help for twisted FTPClient

Jean-Paul Calderone exarkun at divmod.com
Wed Feb 11 10:32:36 EST 2009


On Wed, 11 Feb 2009 11:43:07 +0800, 柳锴 <ioscas at gmail.com> wrote:
>hello, everyone:
>
>    I'm new to twisted and ftp protocol. for some purpose, i need a python
>ftp client to do list, get, put, remove operations upon a FTP server which
>is implemented with twisted.
>    here is my codes, testing for 'get' passed, but 'put' failed. i checked
>the api of storeFile, abd got these:
>
> [snip]
>
> but i don't know how to handle it, :)
> sorry for the rubbish codes, but i really need help from you guys, it's
>hard to find some useful info from twisted official doc,
> Any suggestion on how to write a nice ftp client with twisted is welcome.
> Thanks in advance.

The most obvious problem with the code is that it uses reactor.run and
reactor.stop too much.  You can start and stop the reactor exactly once.

>IOSCAS
>
>______________________________________________________________________________
>
>from twisted.protocols.ftp import FTPClient, FTPFileListProtocol
>from twisted.internet.protocol import Protocol, ClientCreator
>from twisted.python import usage
>from twisted.internet import reactor, defer
>
>class SaveFile(Protocol):
>    '''
>    save the ftp file to local filesystem
>    '''
>    def __init__(self, output):
>        self.fout = open(output, 'w')
>
>    def dataReceived(self, data):
>        self.fout.write(data)
>        self.fout.close()

This is also probably wrong.  dataReceived may be called multiple times.
You can't close the output file until you've received all the data.  I'm
not sure, but connectionLost is probably called when all data has been
received (or if there is an error receiving the data).

>class FTP:
>    '''
>    a simple ftp client
>    '''
>    def __init__(self, host, port):
>        '''
>        init
>        '''
>        self.__host = host
>        self.__port = port
>        self.__username = 'aaa'
>        self.__passwd = 'bbb'
>        self.__passive = 1
>
>    def __get(self, ftp_client, src, des):
>        '''
>        '''
>        save_file = SaveFile(des)
>        d = ftp_client.retrieveFile(src, save_file)
>        d = ftp_client.quit()
>        d.addCallback(lambda result: reactor.stop())
>        return d

Don't stop the reactor here.  Stopping the reactor means your program is
basically done and you don't want to do anything else with Twisted.

>    def get(self, src, des):
>        '''
>        get src file from ftp server, store it in des
>        '''
>        creator = ClientCreator(reactor, FTPClient, self.__username,
>self.__passwd, self.__passive)
>        defer = creator.connectTCP(self.__host,
>self.__port).addCallback(self.__get, src, des)
>        reactor.run()
>        return defer.result

Don't start the reactor here.  Start the reactor in the "main" part of your
program.  Also, don't access "defer.result".  Instead, just return "defer".

>
>    def __put(self, ftp_client, src, des):
>        '''
>        '''
>        source_file = os.path.basename(src)
>        target_dir = os.path.dirname(des)
>        ftp_client.changeDirectory(target_dir)
>        d = ftp_client.storeFile(src)
>        d = ftp_client.quit()
>        d.addCallback(lambda result: reactor.stop())
>        return d

Again, don't use stop here.

>   def put(self, src, des):
>        '''
>        put src file to ftp server, store it in des
>        '''
>        creator = ClientCreator(reactor, FTPClient, self.__username,
>self.__passwd, self.__passive)
>        defer = creator.connectTCP(self.__host,
>self.__port).addCallback(self.__put, src, des)
>        reactor.run()
>        return defer.result
>

And don't use run or .result here.

Jean-Paul




More information about the Twisted-Python mailing list