[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-33]
add SFTPClient.put and SFTPClient.get, and make sftp file objects auto-close on del
This commit is contained in:
parent
1f88224239
commit
93f3cae64f
1
README
1
README
|
@ -238,7 +238,6 @@ v0.9 FEAROW
|
||||||
|
|
||||||
* ctr forms of ciphers are missing (blowfish-ctr, aes128-ctr, aes256-ctr)
|
* ctr forms of ciphers are missing (blowfish-ctr, aes128-ctr, aes256-ctr)
|
||||||
|
|
||||||
* would be nice to have an ftp-like interface to sftp (put, get, chdir...)
|
|
||||||
* cool sftp extension: retreive MD5 or SHA1 of section of a file
|
* cool sftp extension: retreive MD5 or SHA1 of section of a file
|
||||||
* SFTPClient.from_url('sftp://robey@arch.lag.net/folder/filename', 'r+')
|
* SFTPClient.from_url('sftp://robey@arch.lag.net/folder/filename', 'r+')
|
||||||
keep cache of opened sftp clients by (host, port, username)
|
keep cache of opened sftp clients by (host, port, username)
|
||||||
|
|
|
@ -56,6 +56,9 @@ class BufferedFile (object):
|
||||||
# (these may be different because we buffer for line reading)
|
# (these may be different because we buffer for line reading)
|
||||||
self._pos = self._realpos = 0
|
self._pos = self._realpos = 0
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close()
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
"""
|
"""
|
||||||
Returns an iterator that can be used to iterate over the lines in this
|
Returns an iterator that can be used to iterate over the lines in this
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
Client-mode SFTP support.
|
Client-mode SFTP support.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
from sftp import *
|
from sftp import *
|
||||||
from sftp_attr import SFTPAttributes
|
from sftp_attr import SFTPAttributes
|
||||||
from sftp_file import SFTPFile
|
from sftp_file import SFTPFile
|
||||||
|
@ -428,6 +429,62 @@ class SFTPClient (BaseSFTP):
|
||||||
@since: 1.4
|
@since: 1.4
|
||||||
"""
|
"""
|
||||||
return self._cwd
|
return self._cwd
|
||||||
|
|
||||||
|
def put(self, localpath, remotepath):
|
||||||
|
"""
|
||||||
|
Copy a local file (C{localpath}) to the SFTP server as C{remotepath}.
|
||||||
|
Any exception raised by operations will be passed through. This
|
||||||
|
method is primarily provided as a convenience.
|
||||||
|
|
||||||
|
@param localpath: the local file to copy
|
||||||
|
@type localpath: str
|
||||||
|
@param remotepath: the destination path on the SFTP server
|
||||||
|
@type remotepath: str
|
||||||
|
|
||||||
|
@since: 1.4
|
||||||
|
"""
|
||||||
|
fl = file(localpath, 'rb')
|
||||||
|
fr = self.file(remotepath, 'wb')
|
||||||
|
size = 0
|
||||||
|
while True:
|
||||||
|
data = fl.read(16384)
|
||||||
|
if len(data) == 0:
|
||||||
|
break
|
||||||
|
fr.write(data)
|
||||||
|
size += len(data)
|
||||||
|
fl.close()
|
||||||
|
fr.close()
|
||||||
|
s = self.stat(remotepath)
|
||||||
|
if s.st_size != size:
|
||||||
|
raise IOError('size mismatch in put! %d != %d' % (s.st_size, size))
|
||||||
|
|
||||||
|
def get(self, remotepath, localpath):
|
||||||
|
"""
|
||||||
|
Copy a remote file (C{remotepath}) from the SFTP server to the local
|
||||||
|
host as C{localpath}. Any exception raised by operations will be
|
||||||
|
passed through. This method is primarily provided as a convenience.
|
||||||
|
|
||||||
|
@param remotepath: the remote file to copy
|
||||||
|
@type remotepath: str
|
||||||
|
@param localpath: the destination path on the local host
|
||||||
|
@type localpath: str
|
||||||
|
|
||||||
|
@since: 1.4
|
||||||
|
"""
|
||||||
|
fr = self.file(remotepath, 'rb')
|
||||||
|
fl = file(localpath, 'wb')
|
||||||
|
size = 0
|
||||||
|
while True:
|
||||||
|
data = fr.read(16384)
|
||||||
|
if len(data) == 0:
|
||||||
|
break
|
||||||
|
fl.write(data)
|
||||||
|
size += len(data)
|
||||||
|
fl.close()
|
||||||
|
fr.close()
|
||||||
|
s = os.stat(localpath)
|
||||||
|
if s.st_size != size:
|
||||||
|
raise IOError('size mismatch in get! %d != %d' % (s.st_size, size))
|
||||||
|
|
||||||
|
|
||||||
### internals...
|
### internals...
|
||||||
|
|
|
@ -43,7 +43,14 @@ class SFTPFile (BufferedFile):
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
BufferedFile.close(self)
|
BufferedFile.close(self)
|
||||||
self.sftp._request(CMD_CLOSE, self.handle)
|
try:
|
||||||
|
self.sftp._request(CMD_CLOSE, self.handle)
|
||||||
|
except EOFError:
|
||||||
|
# may have outlived the Transport connection
|
||||||
|
pass
|
||||||
|
except IOError:
|
||||||
|
# may have outlived the Transport connection
|
||||||
|
pass
|
||||||
|
|
||||||
def _read(self, size):
|
def _read(self, size):
|
||||||
size = min(size, self.MAX_REQUEST_SIZE)
|
size = min(size, self.MAX_REQUEST_SIZE)
|
||||||
|
|
|
@ -540,3 +540,32 @@ class SFTPTest (unittest.TestCase):
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test_J_get_put(self):
|
||||||
|
"""
|
||||||
|
verify that get/put work.
|
||||||
|
"""
|
||||||
|
import os, warnings
|
||||||
|
warnings.filterwarnings('ignore', 'tempnam.*')
|
||||||
|
|
||||||
|
localname = os.tempnam()
|
||||||
|
text = 'All I wanted was a plastic bunny rabbit.\n'
|
||||||
|
f = open(localname, 'w')
|
||||||
|
f.write(text)
|
||||||
|
f.close()
|
||||||
|
sftp.put(localname, FOLDER + '/bunny.txt')
|
||||||
|
|
||||||
|
f = sftp.open(FOLDER + '/bunny.txt', 'r')
|
||||||
|
self.assertEquals(text, f.read(128))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
os.unlink(localname)
|
||||||
|
localname = os.tempnam()
|
||||||
|
sftp.get(FOLDER + '/bunny.txt', localname)
|
||||||
|
|
||||||
|
f = open(localname, 'r')
|
||||||
|
self.assertEquals(text, f.read(128))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
os.unlink(localname)
|
||||||
|
sftp.unlink(FOLDER + '/bunny.txt')
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue