Merge branch '1.10' into 1.11

This commit is contained in:
Jeff Forcier 2014-02-11 09:48:00 -08:00
commit 36b937d436
6 changed files with 36 additions and 15 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ test.log
docs/ docs/
!sites/docs !sites/docs
_build _build
.coverage

View File

@ -10,7 +10,7 @@ install:
- pip install -r dev-requirements.txt - pip install -r dev-requirements.txt
script: script:
# Main tests, with coverage! # Main tests, with coverage!
- coverage run --source=paramiko test.py --verbose - invoke coverage
# Ensure documentation & invoke pipeline run OK. # Ensure documentation & invoke pipeline run OK.
# Run 'docs' first since its objects.inv is referred to by 'www'. # Run 'docs' first since its objects.inv is referred to by 'www'.
# Also force warnings to be errors since most of them tend to be actual # Also force warnings to be errors since most of them tend to be actual

View File

@ -736,7 +736,7 @@ class SFTPClient (BaseSFTP):
self._convert_status(msg) self._convert_status(msg)
return t, msg return t, msg
if fileobj is not type(None): if fileobj is not type(None):
fileobj._async_response(t, msg) fileobj._async_response(t, msg, num)
if waitfor is None: if waitfor is None:
# just doing a single check # just doing a single check
break break

View File

@ -20,6 +20,8 @@
L{SFTPFile} L{SFTPFile}
""" """
from __future__ import with_statement
from binascii import hexlify from binascii import hexlify
from collections import deque from collections import deque
import socket import socket
@ -53,7 +55,8 @@ class SFTPFile (BufferedFile):
self._prefetching = False self._prefetching = False
self._prefetch_done = False self._prefetch_done = False
self._prefetch_data = {} self._prefetch_data = {}
self._prefetch_reads = [] self._prefetch_extents = {}
self._prefetch_lock = threading.Lock()
self._saved_exception = None self._saved_exception = None
self._reqs = deque() self._reqs = deque()
@ -91,7 +94,7 @@ class SFTPFile (BufferedFile):
pass pass
def _data_in_prefetch_requests(self, offset, size): def _data_in_prefetch_requests(self, offset, size):
k = [i for i in self._prefetch_reads if i[0] <= offset] k = [x for x in self._prefetch_extents.values() if x[0] <= offset]
if len(k) == 0: if len(k) == 0:
return False return False
k.sort(lambda x, y: cmp(x[0], y[0])) k.sort(lambda x, y: cmp(x[0], y[0]))
@ -447,7 +450,6 @@ class SFTPFile (BufferedFile):
def _start_prefetch(self, chunks): def _start_prefetch(self, chunks):
self._prefetching = True self._prefetching = True
self._prefetch_done = False self._prefetch_done = False
self._prefetch_reads.extend(chunks)
t = threading.Thread(target=self._prefetch_thread, args=(chunks,)) t = threading.Thread(target=self._prefetch_thread, args=(chunks,))
t.setDaemon(True) t.setDaemon(True)
@ -457,9 +459,11 @@ class SFTPFile (BufferedFile):
# do these read requests in a temporary thread because there may be # do these read requests in a temporary thread because there may be
# a lot of them, so it may block. # a lot of them, so it may block.
for offset, length in chunks: for offset, length in chunks:
self.sftp._async_request(self, CMD_READ, self.handle, long(offset), int(length)) with self._prefetch_lock:
num = self.sftp._async_request(self, CMD_READ, self.handle, long(offset), int(length))
self._prefetch_extents[num] = (offset, length)
def _async_response(self, t, msg): def _async_response(self, t, msg, num):
if t == CMD_STATUS: if t == CMD_STATUS:
# save exception and re-raise it on next file operation # save exception and re-raise it on next file operation
try: try:
@ -470,10 +474,12 @@ class SFTPFile (BufferedFile):
if t != CMD_DATA: if t != CMD_DATA:
raise SFTPError('Expected data') raise SFTPError('Expected data')
data = msg.get_string() data = msg.get_string()
offset, length = self._prefetch_reads.pop(0) with self._prefetch_lock:
self._prefetch_data[offset] = data offset, length = self._prefetch_extents[num]
if len(self._prefetch_reads) == 0: self._prefetch_data[offset] = data
self._prefetch_done = True del self._prefetch_extents[num]
if len(self._prefetch_extents) == 0:
self._prefetch_done = True
def _check_exception(self): def _check_exception(self):
"if there's a saved exception, raise & clear it" "if there's a saved exception, raise & clear it"

View File

@ -2,7 +2,10 @@
Changelog Changelog
========= =========
* :bug:`193` (and its attentant PRs :issue:`230` & :issue:`253`): Fix SSH agent * :bug:`34` (PR :issue:`35`) Fix SFTP prefetching incompatibility with some
SFTP servers regarding request/response ordering. Thanks to Richard
Kettlewell for catch & patch.
* :bug:`193` (and its attentant PRs :issue:`230` & :issue:`253`) Fix SSH agent
problems present on Windows. Thanks to David Hobbs for initial report and to problems present on Windows. Thanks to David Hobbs for initial report and to
Aarni Koskela & Olle Lundberg for the patches. Aarni Koskela & Olle Lundberg for the patches.
* :release:`1.11.3 <2014-01-08>` * :release:`1.11.3 <2014-01-08>`

View File

@ -1,7 +1,7 @@
from os.path import join from os.path import join
from invoke import Collection from invoke import Collection, ctask as task
from invocations import docs as _docs, testing from invocations import docs as _docs
d = 'sites' d = 'sites'
@ -20,4 +20,15 @@ www = Collection.from_module(_docs, name='www', config={
'sphinx.target': join(path, '_build'), 'sphinx.target': join(path, '_build'),
}) })
ns = Collection(testing.test, docs=docs, www=www)
# Until we move to spec-based testing
@task
def test(ctx):
ctx.run("python test.py --verbose")
@task
def coverage(ctx):
ctx.run("coverage run --source=paramiko test.py --verbose")
ns = Collection(test, coverage, docs=docs, www=www)