Hello community,
here is the log from the commit of package python-asyncssh for openSUSE:Factory checked in at 2019-04-02 09:22:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-asyncssh (Old)
and /work/SRC/openSUSE:Factory/.python-asyncssh.new.25356 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-asyncssh"
Tue Apr 2 09:22:46 2019 rev:5 rq:690377 version:1.16.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-asyncssh/python-asyncssh.changes 2019-03-22 15:00:58.473803190 +0100
+++ /work/SRC/openSUSE:Factory/.python-asyncssh.new.25356/python-asyncssh.changes 2019-04-02 09:22:51.184731969 +0200
@@ -1,0 +2,27 @@
+Mon Apr 1 13:23:08 UTC 2019 - Ondřej Súkup
+
+- update to 1.16.1
+- drop 194.patch
+* Added channel, connection, and env properties to SFTPServer instances,
+ so connection and channel information can be used to influence the
+ SFTP server's behavior. Previously, connection information was made
+ avaiable through the constructor, but channel and environment
+ information was not. Now, all of these are available as properties
+ on the SFTPServer instance without the need to explicitly store anything
+ in a custom constructor.
+* Optimized SFTP glob matching when the glob pattern contains directory
+ names without glob characters in them. Thanks go to Mikhail Terekhov
+ for contributing this improvement!
+* Added support for PurePath in a few places that were missed when this
+ support was originally added. Once again, thanks go to Mikhail Terehkov
+ for these fixes.
+* Fixed bug in SFTP parallel I/O file reader where it sometimes returned
+ EOF prematurely. Thanks go to David G for reporting this problem and
+ providing a reproducible test case.
+* Fixed test failures seen on Fedora Rawhide. Thanks go to Georg Sauthof
+ for reporting this issue and providing a test environment to help debug
+ it.
+* Updated Ed25519/448 and Curve25519/448 tests to only run when these
+ algorithms are available.
+
+-------------------------------------------------------------------
@@ -6 +32,0 @@
-
Old:
----
194.patch
asyncssh-1.16.0.tar.gz
New:
----
asyncssh-1.16.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-asyncssh.spec ++++++
--- /var/tmp/diff_new_pack.ttwLl2/_old 2019-04-02 09:22:51.704732318 +0200
+++ /var/tmp/diff_new_pack.ttwLl2/_new 2019-04-02 09:22:51.704732318 +0200
@@ -19,14 +19,13 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-asyncssh
-Version: 1.16.0
+Version: 1.16.1
Release: 0
Summary: Asynchronous SSHv2 client and server library
License: EPL-2.0 OR GPL-2.0-or-later
Group: Development/Languages/Python
Url: http://asyncssh.timeheart.net
Source: https://files.pythonhosted.org/packages/source/a/asyncssh/asyncssh-%{version}.tar.gz
-Patch0: 194.patch
BuildRequires: %{python_module bcrypt >= 3.1.3}
BuildRequires: %{python_module cryptography >= 2.6.1}
BuildRequires: %{python_module gssapi >= 1.2.0}
@@ -53,7 +52,6 @@
%prep
%setup -q -n asyncssh-%{version}
-%autopatch -p1
%build
%python_build
++++++ asyncssh-1.16.0.tar.gz -> asyncssh-1.16.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asyncssh-1.16.0/PKG-INFO new/asyncssh-1.16.1/PKG-INFO
--- old/asyncssh-1.16.0/PKG-INFO 2019-03-03 08:34:30.000000000 +0100
+++ new/asyncssh-1.16.1/PKG-INFO 2019-03-31 03:59:06.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: asyncssh
-Version: 1.16.0
+Version: 1.16.1
Summary: AsyncSSH: Asynchronous SSHv2 client and server library
Home-page: http://asyncssh.timeheart.net
Author: Ron Frederick
@@ -217,8 +217,8 @@
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Networking
-Provides-Extra: libnacl
-Provides-Extra: pyOpenSSL
Provides-Extra: gssapi
-Provides-Extra: bcrypt
+Provides-Extra: pyOpenSSL
Provides-Extra: pypiwin32
+Provides-Extra: libnacl
+Provides-Extra: bcrypt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asyncssh-1.16.0/asyncssh/connection.py new/asyncssh-1.16.1/asyncssh/connection.py
--- old/asyncssh-1.16.0/asyncssh/connection.py 2019-03-03 06:11:20.000000000 +0100
+++ new/asyncssh-1.16.1/asyncssh/connection.py 2019-03-30 22:01:08.000000000 +0100
@@ -3670,7 +3670,8 @@
raise
@async_context_manager
- def start_sftp_client(self, path_encoding='utf-8', path_errors='strict'):
+ def start_sftp_client(self, env=None, path_encoding='utf-8',
+ path_errors='strict'):
"""Start an SFTP client
This method is a coroutine which attempts to start a secure
@@ -3684,6 +3685,17 @@
will be left as bytes rather than being converted to & from
strings.
+ :param env: (optional)
+ The set of environment variables to set for this SFTP
+ session. Keys and values passed in here will be converted
+ to Unicode strings encoded as UTF-8 (ISO 10646) for
+ transmission.
+
+ .. note:: Many SSH servers restrict which environment
+ variables a client is allowed to set. The
+ server's configuration may need to be edited
+ before environment variables can be
+ successfully set in the remote environment.
:param path_encoding:
The Unicode encoding to apply when sending and receiving
remote pathnames
@@ -3699,6 +3711,7 @@
"""
writer, reader, _ = yield from self.open_session(subsystem='sftp',
+ env=env or {},
encoding=None)
return (yield from start_sftp_client(self, self._loop, reader, writer,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asyncssh-1.16.0/asyncssh/scp.py new/asyncssh-1.16.1/asyncssh/scp.py
--- old/asyncssh-1.16.0/asyncssh/scp.py 2018-11-29 06:11:31.000000000 +0100
+++ new/asyncssh-1.16.1/asyncssh/scp.py 2019-03-30 22:01:08.000000000 +0100
@@ -25,6 +25,7 @@
import argparse
import asyncio
import posixpath
+from pathlib import PurePath
import shlex
import stat
@@ -69,7 +70,7 @@
conn, path = path.split(':')
elif isinstance(path, bytes) and b':' in path:
conn, path = path.split(b':')
- elif isinstance(path, (str, bytes)):
+ elif isinstance(path, (str, bytes, PurePath)):
conn = None
else:
conn = path
@@ -91,6 +92,9 @@
def _start_remote(conn, source, must_be_dir, preserve, recurse, path):
"""Start remote SCP server"""
+ if isinstance(path, PurePath):
+ path = str(path)
+
if isinstance(path, str):
path = path.encode('utf-8')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asyncssh-1.16.0/asyncssh/sftp.py new/asyncssh-1.16.1/asyncssh/sftp.py
--- old/asyncssh-1.16.0/asyncssh/sftp.py 2019-03-03 06:11:20.000000000 +0100
+++ new/asyncssh-1.16.1/asyncssh/sftp.py 2019-03-30 22:01:08.000000000 +0100
@@ -147,44 +147,76 @@
os.utime(path, times=(attrs.atime, attrs.mtime))
+def _split_path_by_globs(pattern):
+ """Split path grouping parts without glob pattern"""
+
+ basedir, patlist, plain = None, [], []
+
+ for current in pattern.split(b'/'):
+ if any(c in current for c in b'*?[]'):
+ if plain:
+ if patlist:
+ patlist.append(plain)
+ else:
+ basedir = b'/'.join(plain) or b'/'
+
+ plain = []
+
+ patlist.append(current)
+ else:
+ plain.append(current)
+
+ if plain:
+ patlist.append(plain)
+
+ return basedir, patlist
+
+
@asyncio.coroutine
def _glob(fs, basedir, patlist, result):
"""Recursively match a glob pattern"""
pattern, newpatlist = patlist[0], patlist[1:]
- if not pattern and not newpatlist:
- result.append(basedir)
- return
+ names = yield from fs.listdir(basedir or b'.')
- if pattern == b'**':
- yield from _glob(fs, basedir, newpatlist, result)
+ if isinstance(pattern, list):
+ if len(pattern) == 1 and not pattern[0] and not newpatlist:
+ result.append(basedir)
+ return
- names = yield from fs.listdir(basedir or b'.')
+ for name in names:
+ if name == pattern[0]:
+ newbase = posixpath.join(basedir or b'', *pattern)
+ yield from fs.stat(newbase)
- for name in names:
- if pattern != name and name in (b'.', b'..'):
- continue
-
- if name[:1] == b'.' and not pattern[:1] == b'.':
- continue
-
- if fnmatch(name, pattern):
- if basedir:
- newbase = posixpath.join(basedir, name)
- else:
- newbase = name
+ if not newpatlist:
+ result.append(newbase)
+ else:
+ yield from _glob(fs, newbase, newpatlist, result)
+ break
+ else:
+ if pattern == b'**':
+ yield from _glob(fs, basedir, newpatlist, result)
- if not newpatlist:
- result.append(newbase)
- else:
- attrs = yield from fs.stat(newbase)
+ for name in names:
+ if name in (b'.', b'..'):
+ continue
+
+ if fnmatch(name, pattern):
+ newbase = posixpath.join(basedir or b'', name)
- if stat.S_ISDIR(attrs.permissions):
- if pattern == b'**':
- yield from _glob(fs, newbase, patlist, result)
- else:
- yield from _glob(fs, newbase, newpatlist, result)
+ if not newpatlist or (len(newpatlist) == 1 and
+ not newpatlist[0]):
+ result.append(newbase)
+ else:
+ attrs = yield from fs.stat(newbase)
+
+ if stat.S_ISDIR(attrs.permissions):
+ if pattern == b'**':
+ yield from _glob(fs, newbase, patlist, result)
+ else:
+ yield from _glob(fs, newbase, newpatlist, result)
@asyncio.coroutine
@@ -195,14 +227,7 @@
try:
if any(c in pattern for c in b'*?[]'):
- patlist = pattern.split(b'/')
-
- if not patlist[0]:
- basedir = b'/'
- patlist = patlist[1:]
- else:
- basedir = None
-
+ basedir, patlist = _split_path_by_globs(pattern)
yield from _glob(fs, basedir, patlist, names)
if not names:
@@ -427,7 +452,7 @@
for task in done:
exc = task.exception()
- if exc:
+ if exc and exc.code != FX_EOF:
exceptions.append(exc)
if exceptions:
@@ -2037,7 +2062,7 @@
if dstpath:
dstpath = dstfs.encode(dstpath)
- if isinstance(srcpaths, (str, bytes)):
+ if isinstance(srcpaths, (str, bytes, PurePath)):
srcpaths = [srcpaths]
elif not dst_isdir:
raise SFTPError(FX_FAILURE, '%s must be a directory' %
@@ -4004,24 +4029,67 @@
def __init__(self, conn, chroot=None):
# pylint: disable=unused-argument
- self._logger = None
-
if chroot:
self._chroot = _from_local_path(os.path.realpath(chroot))
else:
self._chroot = None
+ @classmethod
+ def new(cls, chan):
+ """Allocate a new SFTP server"""
+
+ # Make channel, connection, and env available as properties on
+ # this SFTP server even before __init__ is called, but continue
+ # to pass "conn" as an explicit argument for backward compatibility
+ # with older code which is expecting that.
+
+ sftp_server = cls.__new__(cls)
+ sftp_server.channel = chan
+
+ sftp_server.__init__(chan.get_connection())
+
+ return sftp_server
+
@property
- def logger(self):
- """A logger associated with this SFTP server"""
+ def channel(self):
+ """The channel associated with this SFTP server session"""
- return self._logger
+ return self._chan
+
+ @channel.setter
+ def channel(self, chan):
+ """Set the channel associated with this SFTP server session"""
- @logger.setter
- def logger(self, logger):
- """Set the logger associated with this SFTP server"""
+ # pylint: disable=attribute-defined-outside-init
- self._logger = logger
+ self._chan = chan
+
+ @property
+ def connection(self):
+ """The channel associated with this SFTP server session"""
+
+ return self._chan.get_connection()
+
+ @property
+ def env(self):
+ """The environment associated with this SFTP server session
+
+ This method returns the environment set by the client
+ when this SFTP session was opened.
+
+ :returns: A dictionary containing the environment variables
+ set by the client
+
+ """
+
+
+ return self._chan.get_environment()
+
+ @property
+ def logger(self):
+ """A logger associated with this SFTP server"""
+
+ return self._chan.logger
def format_user(self, uid):
"""Return the user name associated with a uid
@@ -4820,8 +4888,6 @@
def run_sftp_server(sftp_server, reader, writer):
"""Return a handler for an SFTP server session"""
- sftp_server.logger = reader.logger
-
handler = SFTPServerHandler(sftp_server, reader, writer)
handler.logger.info('Starting SFTP server')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asyncssh-1.16.0/asyncssh/stream.py new/asyncssh-1.16.1/asyncssh/stream.py
--- old/asyncssh-1.16.0/asyncssh/stream.py 2019-03-03 06:11:20.000000000 +0100
+++ new/asyncssh-1.16.1/asyncssh/stream.py 2019-03-30 22:01:08.000000000 +0100
@@ -628,13 +628,13 @@
self._chan.set_encoding(None)
self._encoding = None
- handler = run_sftp_server(self._sftp_factory(self._conn),
+ handler = run_sftp_server(self._sftp_factory.new(self._chan),
stdin, stdout)
elif self._allow_scp and command and command.startswith('scp '):
self._chan.set_encoding(None)
self._encoding = None
- handler = run_scp_server(self._sftp_factory(self._conn),
+ handler = run_scp_server(self._sftp_factory.new(self._chan),
command, stdin, stdout, stderr)
else:
handler = self._session_factory(stdin, stdout, stderr)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asyncssh-1.16.0/asyncssh/version.py new/asyncssh-1.16.1/asyncssh/version.py
--- old/asyncssh-1.16.0/asyncssh/version.py 2019-03-03 07:05:25.000000000 +0100
+++ new/asyncssh-1.16.1/asyncssh/version.py 2019-03-30 22:01:26.000000000 +0100
@@ -26,4 +26,4 @@
__url__ = 'http://asyncssh.timeheart.net'
-__version__ = '1.16.0'
+__version__ = '1.16.1'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asyncssh-1.16.0/asyncssh.egg-info/PKG-INFO new/asyncssh-1.16.1/asyncssh.egg-info/PKG-INFO
--- old/asyncssh-1.16.0/asyncssh.egg-info/PKG-INFO 2019-03-03 08:34:30.000000000 +0100
+++ new/asyncssh-1.16.1/asyncssh.egg-info/PKG-INFO 2019-03-31 03:59:06.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: asyncssh
-Version: 1.16.0
+Version: 1.16.1
Summary: AsyncSSH: Asynchronous SSHv2 client and server library
Home-page: http://asyncssh.timeheart.net
Author: Ron Frederick
@@ -217,8 +217,8 @@
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Networking
-Provides-Extra: libnacl
-Provides-Extra: pyOpenSSL
Provides-Extra: gssapi
-Provides-Extra: bcrypt
+Provides-Extra: pyOpenSSL
Provides-Extra: pypiwin32
+Provides-Extra: libnacl
+Provides-Extra: bcrypt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asyncssh-1.16.0/pylintrc new/asyncssh-1.16.1/pylintrc
--- old/asyncssh-1.16.0/pylintrc 2019-03-03 06:11:20.000000000 +0100
+++ new/asyncssh-1.16.1/pylintrc 2019-03-30 22:01:08.000000000 +0100
@@ -205,6 +205,12 @@
expected-line-ending-format=
+[REFACTORING]
+
+# Maximum number of nested blocks for function / method body
+max-nested-blocks=10
+
+
[LOGGING]
# Logging modules to check that the string format arguments are in logging
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asyncssh-1.16.0/tests/test_agent.py new/asyncssh-1.16.1/tests/test_agent.py
--- old/asyncssh-1.16.0/tests/test_agent.py 2018-09-08 23:52:10.000000000 +0200
+++ new/asyncssh-1.16.1/tests/test_agent.py 2019-03-30 22:01:08.000000000 +0100
@@ -30,9 +30,10 @@
import asyncssh
from asyncssh.agent import SSH_AGENT_SUCCESS, SSH_AGENT_FAILURE
+from asyncssh.crypto import ed25519_available
from asyncssh.packet import Byte, String
-from .util import AsyncTestCase, asynctest, libnacl_available, run
+from .util import AsyncTestCase, asynctest, run
def agent_test(func):
@@ -175,7 +176,7 @@
algs = ['ssh-dss', 'ssh-rsa', 'ecdsa-sha2-nistp256']
- if libnacl_available: # pragma: no branch
+ if ed25519_available: # pragma: no branch
algs.append('ssh-ed25519')
for alg_name in algs:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asyncssh-1.16.0/tests/test_kex.py new/asyncssh-1.16.1/tests/test_kex.py
--- old/asyncssh-1.16.0/tests/test_kex.py 2019-03-03 06:11:20.000000000 +0100
+++ new/asyncssh-1.16.1/tests/test_kex.py 2019-03-30 22:01:08.000000000 +0100
@@ -27,6 +27,7 @@
import asyncssh
+from asyncssh.crypto import curve25519_available, curve448_available
from asyncssh.kex_dh import MSG_KEXDH_INIT, MSG_KEXDH_REPLY
from asyncssh.kex_dh import MSG_KEX_DH_GEX_REQUEST, MSG_KEX_DH_GEX_GROUP
from asyncssh.kex_dh import MSG_KEX_DH_GEX_INIT, MSG_KEX_DH_GEX_REPLY, _KexDHGex
@@ -483,6 +484,7 @@
client_conn.close()
server_conn.close()
+ @unittest.skipUnless(curve25519_available, 'Curve25519 not available')
@asynctest
def test_curve25519dh_errors(self):
"""Unit test error conditions in Curve25519DH key exchange"""
@@ -514,6 +516,7 @@
client_conn.close()
server_conn.close()
+ @unittest.skipUnless(curve448_available, 'Curve448 not available')
@asynctest
def test_curve448dh_errors(self):
"""Unit test error conditions in Curve448DH key exchange"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asyncssh-1.16.0/tests/test_public_key.py new/asyncssh-1.16.1/tests/test_public_key.py
--- old/asyncssh-1.16.0/tests/test_public_key.py 2019-03-03 06:11:20.000000000 +0100
+++ new/asyncssh-1.16.1/tests/test_public_key.py 2019-03-30 22:01:08.000000000 +0100
@@ -996,26 +996,26 @@
('Invalid PEM PKCS#8 PBES2 PBKDF2 PRF',
b'-----BEGIN ENCRYPTED PRIVATE KEY-----\n' +
binascii.b2a_base64(der_encode(
- ((_ES2, ((_ES2_PBKDF2, (b'', 0, None)),
+ ((_ES2, ((_ES2_PBKDF2, (b'', 1, None)),
(_ES2_AES128, None))), b''))) +
b'-----END ENCRYPTED PRIVATE KEY-----'),
('Unknown PEM PKCS#8 PBES2 PBKDF2 PRF',
b'-----BEGIN ENCRYPTED PRIVATE KEY-----\n' +
binascii.b2a_base64(der_encode(
- ((_ES2, ((_ES2_PBKDF2, (b'', 0,
+ ((_ES2, ((_ES2_PBKDF2, (b'', 1,
(ObjectIdentifier('1.1'), None))),
(_ES2_AES128, None))), b''))) +
b'-----END ENCRYPTED PRIVATE KEY-----'),
('Invalid PEM PKCS#8 PBES2 encryption parameters',
b'-----BEGIN ENCRYPTED PRIVATE KEY-----\n' +
binascii.b2a_base64(der_encode(
- ((_ES2, ((_ES2_PBKDF2, (b'', 0)),
+ ((_ES2, ((_ES2_PBKDF2, (b'', 1)),
(_ES2_AES128, None))), b''))) +
b'-----END ENCRYPTED PRIVATE KEY-----'),
('Invalid length PEM PKCS#8 PBES2 IV',
b'-----BEGIN ENCRYPTED PRIVATE KEY-----\n' +
binascii.b2a_base64(der_encode(
- ((_ES2, ((_ES2_PBKDF2, (b'', 0)),
+ ((_ES2, ((_ES2_PBKDF2, (b'', 1)),
(_ES2_AES128, b''))), b''))) +
b'-----END ENCRYPTED PRIVATE KEY-----'),
('Invalid OpenSSH cipher',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asyncssh-1.16.0/tests/test_sftp.py new/asyncssh-1.16.1/tests/test_sftp.py
--- old/asyncssh-1.16.0/tests/test_sftp.py 2018-11-27 05:17:29.000000000 +0100
+++ new/asyncssh-1.16.1/tests/test_sftp.py 2019-03-30 22:01:08.000000000 +0100
@@ -145,6 +145,16 @@
yield from super()._process_packet(pkttype, pktid, packet)
+class _CheckPropSFTPServer(SFTPServer):
+ """Return an FTP server which checks channel properties"""
+
+ def listdir(self, path):
+ """List the contents of a directory"""
+
+ if self.channel.get_connection() == self.connection: # pragma: no branch
+ return [SFTPName(k.encode()) for k in self.env.keys()]
+
+
class _ChrootSFTPServer(SFTPServer):
"""Return an FTP server with a changed root"""
@@ -533,13 +543,14 @@
"""Test copying a file over SFTP"""
for method in ('get', 'put', 'copy'):
- with self.subTest(method=method):
- try:
- self._create_file('src')
- yield from getattr(sftp, method)('src', 'dst')
- self._check_file('src', 'dst')
- finally:
- remove('src dst')
+ for src in ('src', b'src', Path('src')):
+ with self.subTest(method=method, src=type(src)):
+ try:
+ self._create_file('src')
+ yield from getattr(sftp, method)(src, 'dst')
+ self._check_file('src', 'dst')
+ finally:
+ remove('src dst')
@sftp_test
def test_copy_progress(self, sftp):
@@ -766,30 +777,56 @@
def test_glob(self, sftp):
"""Test a glob pattern match over SFTP"""
+ # pylint: disable=bad-whitespace
+
+ glob_tests = (
+ ('file*', ['file1', 'filedir']),
+ ('./file*', ['./file1', './filedir']),
+ (b'file*', [b'file1', b'filedir']),
+ (['file*'], ['file1', 'filedir']),
+ (['', 'file*'], ['file1', 'filedir']),
+ (['file*/*2'], ['filedir/file2', 'filedir/filedir2']),
+ (['file*/*[3-9]'], ['filedir/file3']),
+ (['**/file[12]'], ['file1', 'filedir/file2']),
+ (['**/file*/'], ['filedir', 'filedir/filedir2']),
+ ('filedir/file2', ['filedir/file2']),
+ ('./filedir/file2', ['./filedir/file2']),
+ ('filedir/file*', ['filedir/file2', 'filedir/file3',
+ 'filedir/filedir2']),
+ ('./filedir/file*', ['./filedir/file2', './filedir/file3',
+ './filedir/filedir2']),
+ ('./filedir/filedir2/file*', ['./filedir/filedir2/file4',
+ './filedir/filedir2/file5']),
+ ('filedir/filedir2/file*', ['filedir/filedir2/file4',
+ 'filedir/filedir2/file5']),
+ ('./filedir/*/file4', ['./filedir/filedir2/file4']),
+ ('filedir/*/file4', ['filedir/filedir2/file4']),
+ ('./*/filedir2/file4', ['./filedir/filedir2/file4']),
+ ('*/filedir2/file4', ['filedir/filedir2/file4']),
+ ('*/filedir2/file*4', ['filedir/filedir2/file4']),
+ ('./filedir/filedir*/file*', ['./filedir/filedir2/file4',
+ './filedir/filedir2/file5']),
+ ('filedir/filedir*/file*', ['filedir/filedir2/file4',
+ 'filedir/filedir2/file5']),
+ ('./**/filedir2/file4', ['./filedir/filedir2/file4']),
+ ('**/filedir2/file4', ['filedir/filedir2/file4']))
+
+ # pylint: enable=bad-whitespace
+
try:
os.mkdir('filedir')
self._create_file('file1')
self._create_file('filedir/file2')
self._create_file('filedir/file3')
+ os.mkdir('filedir/filedir2')
+ self._create_file('filedir/filedir2/file4')
+ self._create_file('filedir/filedir2/file5')
+
+ for pattern, matches in glob_tests:
+ with self.subTest(pattern=pattern):
+ self.assertEqual(sorted((yield from sftp.glob(pattern))),
+ matches)
- self.assertEqual(sorted((yield from sftp.glob('file*'))),
- ['file1', 'filedir'])
- self.assertEqual(sorted((yield from sftp.glob('./file*'))),
- ['./file1', './filedir'])
- self.assertEqual(sorted((yield from sftp.glob(b'file*'))),
- [b'file1', b'filedir'])
- self.assertEqual(sorted((yield from sftp.glob(['file*']))),
- ['file1', 'filedir'])
- self.assertEqual(sorted((yield from sftp.glob(['', 'file*']))),
- ['file1', 'filedir'])
- self.assertEqual(sorted((yield from sftp.glob(['file*/*2']))),
- ['filedir/file2'])
- self.assertEqual(sorted((yield from sftp.glob(['file*/*[3-9]']))),
- ['filedir/file3'])
- self.assertEqual(sorted((yield from sftp.glob(['**/file[12]']))),
- ['file1', 'filedir/file2'])
- self.assertEqual(sorted((yield from sftp.glob(['**/file*/']))),
- ['filedir'])
self.assertEqual((yield from sftp.glob([b'fil*1', 'fil*dir'])),
[b'file1', 'filedir'])
finally:
@@ -1290,6 +1327,23 @@
remove('file')
+ @sftp_test
+ def test_open_read_parallel(self, sftp):
+ """Test reading data from a file using parallel I/O"""
+
+ f = None
+
+ try:
+ self._create_file('file', 40*1024*'\0')
+
+ f = yield from sftp.open('file')
+ self.assertEqual(len((yield from f.read(64*1024))), 40*1024)
+ finally:
+ if f: # pragma: no branch
+ yield from f.close()
+
+ remove('file')
+
def test_open_read_out_of_order(self):
"""Test parallel read with out-of-order responses"""
@@ -2247,6 +2301,31 @@
asyncssh.set_sftp_log_level('WARNING')
+class _TestSFTPServerProperties(_CheckSFTP):
+ """Unit test for checking SFTP server properties"""
+
+ @classmethod
+ @asyncio.coroutine
+ def start_server(cls):
+ """Start an SFTP server which checks channel properties"""
+
+ return (yield from cls.create_server(sftp_factory=_CheckPropSFTPServer))
+
+ @asynctest
+ def test_properties(self):
+ """Test SFTP server channel properties"""
+
+ with (yield from self.connect()) as conn:
+ with (yield from conn.start_sftp_client(env={'A': 1,
+ 'B': 2})) as sftp:
+ files = yield from sftp.listdir()
+ self.assertEqual(sorted(files), ['A', 'B'])
+
+ yield from sftp.wait_closed() # pragma: no branch
+
+ yield from conn.wait_closed()
+
+
class _TestSFTPChroot(_CheckSFTP):
"""Unit test for SFTP server with changed root"""
@@ -2681,12 +2760,15 @@
def test_get(self):
"""Test getting a file over SCP"""
- try:
- self._create_file('src')
- yield from scp((self._scp_server, 'src'), 'dst')
- self._check_file('src', 'dst')
- finally:
- remove('src dst')
+ for src in ('src', b'src', Path('src')):
+ for dst in ('dst', b'dst', Path('dst')):
+ with self.subTest(src=type(src), dst=type(dst)):
+ try:
+ self._create_file('src')
+ yield from scp((self._scp_server, src), dst)
+ self._check_file('src', 'dst')
+ finally:
+ remove('src dst')
@asynctest
def test_get_bytes_path(self):