summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJeff Forcier <jeff@bitprophet.org>2016-04-23 18:57:15 -0700
committerJeff Forcier <jeff@bitprophet.org>2016-04-23 18:57:15 -0700
commitec7059d79770bb9890abd5c0514b67af7e9d2d52 (patch)
treeb483cfa25f95875ee05e891c00fe0ac2b925d52f
parentd6f61c9fe98a127adc291ac43ca7cdaf62d7b79f (diff)
parenta0bf23a25c39d5c4d61dda29b60b04c1a6fd1c41 (diff)
Merge branch '1.16'
-rw-r--r--paramiko/sftp_client.py36
-rw-r--r--sites/www/changelog.rst3
-rwxr-xr-xtests/test_sftp.py4
3 files changed, 23 insertions, 20 deletions
diff --git a/paramiko/sftp_client.py b/paramiko/sftp_client.py
index 57225558..daaae3ef 100644
--- a/paramiko/sftp_client.py
+++ b/paramiko/sftp_client.py
@@ -592,6 +592,18 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
# TODO: make class initialize with self._cwd set to self.normalize('.')
return self._cwd and u(self._cwd)
+ def _transfer_with_callback(self, reader, writer, file_size, callback):
+ size = 0
+ while True:
+ data = reader.read(32768)
+ writer.write(data)
+ size += len(data)
+ if len(data) == 0:
+ break
+ if callback is not None:
+ callback(size, file_size)
+ return size
+
def putfo(self, fl, remotepath, file_size=0, callback=None, confirm=True):
"""
Copy the contents of an open file object (``fl``) to the SFTP server as
@@ -621,15 +633,9 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
"""
with self.file(remotepath, 'wb') as fr:
fr.set_pipelined(True)
- size = 0
- while True:
- data = fl.read(32768)
- fr.write(data)
- size += len(data)
- if callback is not None:
- callback(size, file_size)
- if len(data) == 0:
- break
+ size = self._transfer_with_callback(
+ reader=fl, writer=fr, file_size=file_size, callback=callback
+ )
if confirm:
s = self.stat(remotepath)
if s.st_size != size:
@@ -689,16 +695,10 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
file_size = self.stat(remotepath).st_size
with self.open(remotepath, 'rb') as fr:
fr.prefetch(file_size)
+ return self._transfer_with_callback(
+ reader=fr, writer=fl, file_size=file_size, callback=callback
+ )
- size = 0
- while True:
- data = fr.read(32768)
- fl.write(data)
- size += len(data)
- if callback is not None:
- callback(size, file_size)
- if len(data) == 0:
- break
return size
def get(self, remotepath, localpath, callback=None):
diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst
index e9c4fa79..f1e0c471 100644
--- a/sites/www/changelog.rst
+++ b/sites/www/changelog.rst
@@ -2,6 +2,9 @@
Changelog
=========
+* :bug:`632` Fix logic bug in the SFTP client's callback-calling functionality;
+ previously there was a chance the given callback would fire twice at the end
+ of a transfer. Thanks to ``@ab9-er`` for catch & original patch.
* :support:`612` Identify & work around a race condition in the test for
handshake timeouts, which was causing frequent test failures for a subset of
contributors as well as Travis-CI (usually, but not always, limited to Python
diff --git a/tests/test_sftp.py b/tests/test_sftp.py
index 53b73ee0..e4c2c3a3 100755
--- a/tests/test_sftp.py
+++ b/tests/test_sftp.py
@@ -611,7 +611,7 @@ class SFTPTest (unittest.TestCase):
with sftp.open(FOLDER + '/bunny.txt', 'rb') as f:
self.assertEqual(text, f.read(128))
- self.assertEqual((41, 41), saved_progress[-1])
+ self.assertEqual([(41, 41)], saved_progress)
os.unlink(localname)
fd, localname = mkstemp()
@@ -621,7 +621,7 @@ class SFTPTest (unittest.TestCase):
with open(localname, 'rb') as f:
self.assertEqual(text, f.read(128))
- self.assertEqual((41, 41), saved_progress[-1])
+ self.assertEqual([(41, 41)], saved_progress)
os.unlink(localname)
sftp.unlink(FOLDER + '/bunny.txt')