Nevermind. I was missing &#39;self.dataReceived = self.interface_handler.dataReceived&#39; in the __init__ of SSHSession and self.dataReceived = self._client.dataReceived in the __init__ of SftpClient. Maybe I need to reduce the abstraction a bit here, hmm.<div>
<br></div><div>Well hopefully this helps someone sometime....</div><div><br><br><div class="gmail_quote">On 9 February 2011 10:00, Brad Milne <span dir="ltr">&lt;<a href="mailto:brad.milne@devx.runthered.com">brad.milne@devx.runthered.com</a>&gt;</span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">Hi all
<div><br></div><div>I&#39;ve looked around for SFTP client doco, but it&#39;s come up pretty thin on the ground. I&#39;ve seen <a href="http://twistedmatrix.com/trac/wiki/TwistedConch" target="_blank">http://twistedmatrix.com/trac/wiki/TwistedConch</a> and everything under it. With it I&#39;ve been able to connect via SSH to a server and run a command (<i>a la</i> the &#39;cat&#39; example from tutorial page and from <a href="http://twistedmatrix.com/documents/current/conch/examples/sshsimpleclient.py" shape="rect" target="_blank">sshsimpleclient.py</a>).</div>


<div><br></div><div>On the other hand, plenty of posts recommend checking out cftp.py in scripts/. That uses the FileTransferClient, and appears to be more along the lines of what is needed for a persistent SFTP connection and file transfers.</div>

<div><br></div><div>What seems to be happening with the below is that the connection is being created OK, but I haven&#39;t been able to utilise the _remoteGlob method (taken from cftp.py). It simply hangs.</div><div><br>

</div><div>My sandbox code is below.</div><div><br></div><div>Any help greatly appreciated.</div><div>Brad</div><div><br></div><div>&lt;code&gt;</div><div><div>import os</div><div>from twisted.python import failure</div>
</div>
<div><div>from twisted.internet import reactor, protocol, defer</div><div>from twisted.conch.ssh import transport, connection, userauth, channel, common, filetransfer</div><div>import logging</div><div>from twisted.python import log</div>

<div>import sys</div></div><div><br></div><div><div>class SimpleTransport(transport.SSHClientTransport):</div><div>    def verifyHostKey(self, hostKey, fingerprint):</div><div>        print &#39;host key fingerprint: %s&#39; % fingerprint</div>

<div>        return defer.succeed(1) </div><div><br></div><div>    def connectionSecure(self):</div><div>        &#39;&#39;&#39;</div><div>        called when the encryption is set up and other services can be run</div><div>

        &#39;&#39;&#39;</div><div>        self.requestService(SimpleUserAuth(USERNAME,SimpleConnection()))</div><div><br></div><div>class SimpleConnection(connection.SSHConnection):</div><div>    def serviceStarted(self):</div>

<div>        self.openChannel(SSHSession())</div><div>        </div><div>class SimpleUserAuth(userauth.SSHUserAuthClient):</div><div>    def getPassword(self):</div><div>        return defer.succeed(PASS)</div></div><div>

<br></div><div><div>class SSHSession(channel.SSHChannel):</div><div>    &#39;&#39;&#39;</div><div>    Things to do within the active SSH session.</div><div>    &#39;&#39;&#39;</div><div>    name = &#39;session&#39;</div>
<div>
    </div><div>    def __init__(self, interface_handler=None, *args, **kwargs):</div><div>        channel.SSHChannel.__init__(self, *args, **kwargs)</div><div>        self.interface_handler = interface_handler or SftpClient(self)</div>

<div>    </div><div>    def channelOpen(self, ignoredData):</div><div>        self.data = &#39;&#39;</div><div>        # Create an sftp connection (stays open)</div><div>        d = self.conn.sendRequest(self, &#39;subsystem&#39;, common.NS(&#39;sftp&#39;), wantReply=1)</div>

<div>        d.addCallback(self._cbSubsystem)</div><div>    </div><div>    def _cbSubsystem(self, result):</div><div>        self.interface_handler.getDirectoryContents(&#39;/tmp&#39;)</div><div>        </div><div>    def closeReceived(self):</div>

<div>        <a href="http://logging.info" target="_blank">logging.info</a>(&#39;remote side closed %s&#39; % self)</div><div>        self.conn.sendClose(self)</div><div>        reactor.stop()</div><div><br></div><div><div>
class SftpClient(object):</div>
<div>    </div><div>    def __init__(self, transport, *args, **kwargs):</div><div>        super(SftpClient, self).__init__(*args, **kwargs)</div><div>        self.transport = transport</div><div>        self._client = filetransfer.FileTransferClient()</div>

<div><br></div><div>    @property</div><div>    def client(self):</div><div>        if not self._client.connected:</div><div>            self._client.makeConnection(self.transport)</div><div>            logging.debug(&quot;Made &#39;connection&#39; with transport class&quot;)</div>

<div>        return self._client</div><div>    </div><div>    def getDirectoryContents(self, path):</div><div>        d = self._remoteGlob(path)</div><div>        </div><div>        def gotit(files):</div><div>            print &quot;Got %s: %s&quot; % (type(files), files)</div>

<div>        d.addCallback(gotit)</div><div>        return d</div><div>        </div><div>    # Accessory methods.</div><div>    # These are stolen from <a href="http://twisted.conch.scripts.cftp.py" target="_blank">twisted.conch.scripts.cftp.py</a>. We can&#39;t</div>

<div>    # import that module as it contains unix-dependent imports.</div><div>    def _remoteGlob(self, fullPath):</div><div>        logging.debug(&#39;looking up %s&#39; % fullPath)</div><div>        head, tail = os.path.split(fullPath)</div>

<div>        if &#39;*&#39; in tail or &#39;?&#39; in tail:</div><div>            glob = 1</div><div>        else:</div><div>            glob = 0</div><div>        if tail and not glob: # could be file or directory</div>
<div>
            # try directory first</div><div>            logging.debug(&quot;Opening dir&quot;)</div><div>            d = self.client.openDirectory(fullPath)</div><div>            d.addCallback(self._cbOpenList, &#39;&#39;)</div>

<div>            d.addErrback(self._ebNotADirectory, head, tail)</div><div>        else:</div><div>            d = self.client.openDirectory(head)</div><div>            d.addCallback(self._cbOpenList, tail)</div><div>        return d</div>

<div><br></div><div>    def _cbOpenList(self, directory, glob):</div><div>        logging.debug(&quot;Got dir&quot;)</div><div>        files = []</div><div>        d = directory.read()</div><div>        d.addBoth(self._cbReadFile, files, directory, glob)</div>

<div>        return d</div><div><br></div><div>    def _ebNotADirectory(self, reason, path, glob):</div><div>        logging.debug(&quot;Not a dir&quot;)</div><div>        d = self.client.openDirectory(path)</div><div>        d.addCallback(self._cbOpenList, glob)</div>

<div>        return d</div><div><br></div><div>    def _cbReadFile(self, files, l, directory, glob):</div><div>        logging.debug(&quot;Reading file&quot;)</div><div>        if not isinstance(files, failure.Failure):</div>

<div>            if glob:</div><div>                raise NotImplementedError(&quot;don&#39;t have fnmatch available to use on Windows so have commented this bit out&quot;)</div><div>#                l.extend([f for f in files if fnmatch.fnmatch(f[0], glob)])</div>

<div>            else:</div><div>                l.extend(files)</div><div>            d = directory.read()</div><div>            d.addBoth(self._cbReadFile, l, directory, glob)</div><div>            return d</div><div>        else:</div>

<div>            reason = files</div><div>            reason.trap(EOFError)</div><div>            directory.close()</div><div>            return l</div></div><div>        </div><div>if __name__==&#39;__main__&#39;:</div>
<div>
    protocol.ClientCreator(reactor, SimpleTransport).connectTCP(HOST, 22)</div><div>    log.startLogging(sys.stdout, setStdout=0)</div><div>    reactor.run()</div></div><div><br></div><div>&lt;/code&gt;</div>
</blockquote></div><br><br clear="all"><br>-- <br><span style="font-family:arial, sans-serif;font-size:13px;border-collapse:collapse;color:rgb(136, 136, 136)">Brad Milne | Run The Red | <span style="font-family:arial, sans-serif;font-size:13px;white-space:nowrap"><b><a href="mailto:brad.milne@devx.runthered.com" style="color:rgb(42, 93, 176)" target="_blank">brad.milne@devx.runthered.com</a></b></span></span><br>

</div>