[Python-modules-commits] [python-asyncssh] 02/08: Import python-asyncssh_1.5.6.orig.tar.gz
Vincent Bernat
bernat at moszumanska.debian.org
Sat Jul 2 12:15:54 UTC 2016
This is an automated email from the git hooks/post-receive script.
bernat pushed a commit to branch master
in repository python-asyncssh.
commit 1ed6ea46c8c27b3a572bb0638e3ab153c8d55720
Author: Vincent Bernat <bernat at debian.org>
Date: Sat Jul 2 13:53:39 2016 +0200
Import python-asyncssh_1.5.6.orig.tar.gz
---
.coveragerc | 2 +
.travis.yml | 70 ++
.travis/build-libsodium.sh | 11 +
MANIFEST.in | 3 +-
README.rst | 29 +-
asyncssh/connection.py | 133 ++-
asyncssh/misc.py | 60 +-
asyncssh/public_key.py | 6 +-
asyncssh/sftp.py | 365 +++---
asyncssh/version.py | 2 +-
docs/api.rst | 7 +
docs/changes.rst | 47 +
docs/index.rst | 20 +-
examples/check_exit_status.py | 15 +-
examples/chroot_sftp_server.py | 13 +-
examples/direct_client.py | 17 +-
examples/direct_server.py | 13 +-
examples/listening_client.py | 17 +-
examples/local_forwarding_client.py | 15 +-
examples/local_forwarding_client2.py | 15 +-
examples/local_forwarding_server.py | 13 +-
examples/math_client.py | 15 +-
examples/math_server.py | 13 +-
examples/remote_forwarding_client.py | 15 +-
examples/remote_forwarding_client2.py | 15 +-
examples/remote_forwarding_server.py | 13 +-
examples/sample_client.py | 17 +-
examples/set_environment.py | 19 +-
examples/set_terminal.py | 21 +-
examples/sftp_client.py | 15 +-
examples/show_environment.py | 16 +-
examples/show_terminal.py | 18 +-
examples/simple_cert_server.py | 13 +-
examples/simple_client.py | 15 +-
examples/simple_keyed_server.py | 11 +-
examples/simple_server.py | 11 +-
examples/simple_sftp_server.py | 13 +-
examples/stderr_client.py | 15 +-
examples/stream_direct_client.py | 15 +-
examples/stream_direct_server.py | 18 +-
examples/stream_listening_client.py | 22 +-
examples/stream_math_client.py | 15 +-
examples/stream_math_server.py | 18 +-
setup.py | 4 +-
tests/__init__.py | 5 +-
tests/server.py | 19 +-
tests/test_auth_keys.py | 2 +-
tests/test_channel.py | 2 +-
tests/test_connection.py | 146 +++
tests/test_forward.py | 10 +-
tests/test_sftp.py | 2069 ++++++++++++++++++++++++++++++++-
tests/util.py | 15 +-
tests_py35/test_async.py | 118 ++
tox.ini | 13 +
54 files changed, 3008 insertions(+), 611 deletions(-)
diff --git a/.coveragerc b/.coveragerc
index f2c3d0b..688d20e 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -5,3 +5,5 @@ branch = True
exclude_lines =
pragma: no cover
raise NotImplementedError
+omit =
+ .tox/*
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..2e472ce
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,70 @@
+language: python
+
+install:
+ - pip install tox py-bcrypt libnacl
+
+matrix:
+ allow_failures:
+ - python: nightly
+ fast_finish: true
+ include:
+ - os: linux
+ sudo: required
+ dist: trusty
+ python: 3.4
+ before_install:
+ - .travis/build-libsodium.sh
+ env:
+ - TOXENV=py34
+ - os: linux
+ sudo: required
+ dist: trusty
+ python: 3.5
+ before_install:
+ - .travis/build-libsodium.sh
+ env:
+ - TOXENV=py35
+ - os: linux
+ sudo: required
+ dist: trusty
+ python: nightly
+ before_install:
+ - .travis/build-libsodium.sh
+ env:
+ - TOXENV=py36
+ - os: osx
+ osx_image: xcode7.3
+ language: generic
+ env:
+ - CPPFLAGS=-I/usr/local/opt/openssl/include
+ - LDFLAGS=-L/usr/local/opt/openssl/lib -L/usr/local/opt/libffi/lib
+ - PATH=$HOME/.pyenv/bin:$PATH
+ - TOXENV=py34
+ before_install:
+ - brew update
+ - brew install libffi libsodium openssl pyenv pyenv-virtualenv
+ - eval "$(pyenv init -)"
+ - eval "$(pyenv virtualenv-init -)"
+ - pyenv install 3.4.4
+ - pyenv rehash
+ - pyenv virtualenv 3.4.4 venv
+ - pyenv activate venv
+ - os: osx
+ osx_image: xcode7.3
+ language: generic
+ env:
+ - CPPFLAGS=-I/usr/local/opt/openssl/include
+ - LDFLAGS=-L/usr/local/opt/openssl/lib -L/usr/local/opt/libffi/lib
+ - PATH=$HOME/.pyenv/bin:$PATH
+ - TOXENV=py35
+ before_install:
+ - brew update
+ - brew install libffi libsodium openssl pyenv pyenv-virtualenv
+ - eval "$(pyenv init -)"
+ - eval "$(pyenv virtualenv-init -)"
+ - pyenv install 3.5.1
+ - pyenv rehash
+ - pyenv virtualenv 3.5.1 venv
+ - pyenv activate venv
+
+script: tox
diff --git a/.travis/build-libsodium.sh b/.travis/build-libsodium.sh
new file mode 100755
index 0000000..4b72471
--- /dev/null
+++ b/.travis/build-libsodium.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+# Build and install libsodum 1.0.10
+#
+git clone git://github.com/jedisct1/libsodium.git
+cd libsodium
+git checkout tags/1.0.10
+./autogen.sh
+./configure
+make && sudo make install
+sudo ldconfig
diff --git a/MANIFEST.in b/MANIFEST.in
index e375cd5..9cd1216 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1 +1,2 @@
-include COPYRIGHT LICENSE README.rst examples/*.py tests/*.py
+include CONTRIBUTING.rst COPYRIGHT LICENSE README.rst pylintrc tox.ini
+include examples/*.py tests/*.py tests_py35/*.py
diff --git a/README.rst b/README.rst
index b5696da..30462ba 100644
--- a/README.rst
+++ b/README.rst
@@ -9,15 +9,14 @@ asyncio framework.
import asyncio, asyncssh, sys
- @asyncio.coroutine
- def run_client():
- with (yield from asyncssh.connect('localhost')) as conn:
- stdin, stdout, stderr = yield from conn.open_session('echo "Hello!"')
+ async def run_client():
+ async with asyncssh.connect('localhost') as conn:
+ stdin, stdout, stderr = await conn.open_session('echo "Hello!"')
- output = yield from stdout.read()
+ output = await stdout.read()
print(output, end='')
- yield from stdout.channel.wait_closed()
+ await stdout.channel.wait_closed()
status = stdout.channel.get_exit_status()
if status:
@@ -29,7 +28,7 @@ asyncio framework.
Check out the `examples`__ to get started!
-__ http://asyncssh.readthedocs.org/en/stable/#client-examples
+__ http://asyncssh.readthedocs.io/en/stable/#client-examples
Features
--------
@@ -63,14 +62,14 @@ Features
* Designed to be easy to extend to support new forms of key exchange,
authentication, encryption, and compression algorithms
-__ http://asyncssh.readthedocs.org/en/stable/api.html#key-exchange-algorithms
-__ http://asyncssh.readthedocs.org/en/stable/api.html#encryption-algorithms
-__ http://asyncssh.readthedocs.org/en/stable/api.html#mac-algorithms
-__ http://asyncssh.readthedocs.org/en/stable/api.html#compression-algorithms
-__ http://asyncssh.readthedocs.org/en/stable/api.html#public-key-support
-__ http://asyncssh.readthedocs.org/en/stable/api.html#ssh-agent-support
-__ http://asyncssh.readthedocs.org/en/stable/api.html#known-hosts
-__ http://asyncssh.readthedocs.org/en/stable/api.html#authorized-keys
+__ http://asyncssh.readthedocs.io/en/stable/api.html#key-exchange-algorithms
+__ http://asyncssh.readthedocs.io/en/stable/api.html#encryption-algorithms
+__ http://asyncssh.readthedocs.io/en/stable/api.html#mac-algorithms
+__ http://asyncssh.readthedocs.io/en/stable/api.html#compression-algorithms
+__ http://asyncssh.readthedocs.io/en/stable/api.html#public-key-support
+__ http://asyncssh.readthedocs.io/en/stable/api.html#ssh-agent-support
+__ http://asyncssh.readthedocs.io/en/stable/api.html#known-hosts
+__ http://asyncssh.readthedocs.io/en/stable/api.html#authorized-keys
License
-------
diff --git a/asyncssh/connection.py b/asyncssh/connection.py
index a724dff..079080d 100644
--- a/asyncssh/connection.py
+++ b/asyncssh/connection.py
@@ -75,7 +75,7 @@ from .logging import logger
from .mac import get_mac_algs, get_mac_params, get_mac
from .misc import ChannelOpenError, DisconnectError, PasswordChangeRequired
-from .misc import ip_address, map_handler_name
+from .misc import async_context_manager, ip_address, map_handler_name
from .packet import Boolean, Byte, NameList, String, UInt32, UInt64
from .packet import PacketDecodeError, SSHPacket, SSHPacketHandler
@@ -236,6 +236,7 @@ def _load_private_keypair_list(keylist, passphrase=None):
return result
+
def _load_public_key(key):
"""Load a public key
@@ -271,6 +272,7 @@ def _load_public_key_list(keylist):
else:
return [_load_public_key(key) for key in keylist]
+
def _load_authorized_keys(authorized_keys):
"""Load authorized keys list
@@ -286,6 +288,29 @@ def _load_authorized_keys(authorized_keys):
else:
return authorized_keys
+
+def _validate_version(version):
+ """Validate requested SSH version"""
+
+ if version is ():
+ from .version import __version__
+
+ version = b'AsyncSSH_' + __version__.encode('ascii')
+ else:
+ if isinstance(version, str):
+ version = version.encode('ascii')
+
+ # Version including 'SSH-2.0-' and CRLF must be 255 chars or less
+ if len(version) > 245:
+ raise ValueError('Version string is too long')
+
+ for b in version:
+ if b < 0x20 or b > 0x7e:
+ raise ValueError('Version string must be printable ASCII')
+
+ return version
+
+
def _select_algs(alg_type, algs, possible_algs, none_value=None):
"""Select a set of allowed algorithms"""
@@ -324,9 +349,9 @@ def _validate_algs(kex_algs, enc_algs, mac_algs, cmp_algs):
class SSHConnection(SSHPacketHandler):
"""Parent class for SSH connections"""
- def __init__(self, protocol_factory, loop, kex_algs, encryption_algs,
- mac_algs, compression_algs, rekey_bytes, rekey_seconds,
- server):
+ def __init__(self, protocol_factory, loop, version, kex_algs,
+ encryption_algs, mac_algs, compression_algs, rekey_bytes,
+ rekey_seconds, server):
self._protocol_factory = protocol_factory
self._loop = loop
self._tasks = set()
@@ -339,6 +364,7 @@ class SSHConnection(SSHPacketHandler):
self._packet = b''
self._pktlen = 0
+ self._version = version
self._client_version = b''
self._server_version = b''
self._client_kexinit = b''
@@ -438,6 +464,19 @@ class SSHConnection(SSHPacketHandler):
else:
raise
+ @asyncio.coroutine
+ def __aenter__(self):
+ """Allow SSHConnection to be used as an async context manager"""
+
+ return self
+
+ @asyncio.coroutine
+ def __aexit__(self, *exc_info):
+ """Wait for connection close when used as an async context manager"""
+
+ self.__exit__()
+ yield from self.wait_closed()
+
def _cleanup(self, exc):
"""Clean up this connection"""
@@ -665,9 +704,7 @@ class SSHConnection(SSHPacketHandler):
def _send_version(self):
"""Start the SSH handshake"""
- from .version import __version__
-
- version = b'SSH-2.0-AsyncSSH_' + __version__.encode('ascii')
+ version = b'SSH-2.0-' + self._version
if self.is_client():
self._client_version = version
@@ -1806,6 +1843,9 @@ class SSHConnection(SSHPacketHandler):
"""
try:
+ if dest_host == '':
+ dest_host = None
+
_, peer = yield from self._loop.create_connection(SSHForwarder,
dest_host,
dest_port)
@@ -1938,13 +1978,13 @@ class SSHClientConnection(SSHConnection):
"""
- def __init__(self, client_factory, loop, kex_algs, encryption_algs,
- mac_algs, compression_algs, rekey_bytes, rekey_seconds,
- host, port, known_hosts, username, password, client_keys,
- agent, agent_path, auth_waiter):
- super().__init__(client_factory, loop, kex_algs, encryption_algs,
- mac_algs, compression_algs, rekey_bytes,
- rekey_seconds, server=False)
+ def __init__(self, client_factory, loop, client_version, kex_algs,
+ encryption_algs, mac_algs, compression_algs, rekey_bytes,
+ rekey_seconds, host, port, known_hosts, username, password,
+ client_keys, agent, agent_path, auth_waiter):
+ super().__init__(client_factory, loop, client_version, kex_algs,
+ encryption_algs, mac_algs, compression_algs,
+ rekey_bytes, rekey_seconds, server=False)
self._host = host
self._port = port if port != _DEFAULT_PORT else None
@@ -2876,7 +2916,7 @@ class SSHClientConnection(SSHConnection):
return (yield from self.create_unix_server(session_factory,
listen_path))
- @asyncio.coroutine
+ @async_context_manager
def start_sftp_client(self, path_encoding='utf-8', path_errors='strict'):
"""Start an SFTP client
@@ -2949,14 +2989,15 @@ class SSHServerConnection(SSHConnection):
"""
- def __init__(self, server_factory, loop, kex_algs, encryption_algs,
- mac_algs, compression_algs, rekey_bytes, rekey_seconds,
- server_host_keys, authorized_client_keys, allow_pty,
- agent_forwarding, session_factory, session_encoding,
- sftp_factory, window, max_pktsize, login_timeout):
- super().__init__(server_factory, loop, kex_algs, encryption_algs,
- mac_algs, compression_algs, rekey_bytes,
- rekey_seconds, server=True)
+ def __init__(self, server_factory, loop, server_version, kex_algs,
+ encryption_algs, mac_algs, compression_algs, rekey_bytes,
+ rekey_seconds, server_host_keys, authorized_client_keys,
+ allow_pty, agent_forwarding, session_factory,
+ session_encoding, sftp_factory, window, max_pktsize,
+ login_timeout):
+ super().__init__(server_factory, loop, server_version, kex_algs,
+ encryption_algs, mac_algs, compression_algs,
+ rekey_bytes, rekey_seconds, server=True)
self._server_host_keys = server_host_keys
self._server_host_key_algs = server_host_keys.keys()
@@ -3860,8 +3901,9 @@ def create_connection(client_factory, host, port=_DEFAULT_PORT, *,
loop=None, tunnel=None, family=0, flags=0,
local_addr=None, known_hosts=(), username=None,
password=None, client_keys=(), passphrase=None,
- agent_path=(), agent_forwarding=False, kex_algs=(),
- encryption_algs=(), mac_algs=(), compression_algs=(),
+ agent_path=(), agent_forwarding=False,
+ client_version=(), kex_algs=(), encryption_algs=(),
+ mac_algs=(), compression_algs=(),
rekey_bytes=_DEFAULT_REKEY_BYTES,
rekey_seconds=_DEFAULT_REKEY_SECONDS):
"""Create an SSH client connection
@@ -3963,6 +4005,9 @@ def create_connection(client_factory, host, port=_DEFAULT_PORT, *,
Whether or not to allow forwarding of ssh-agent requests from
processes running on the server. By default, ssh-agent forwarding
requests from the server are not allowed.
+ :param str client_version: (optional)
+ An ASCII string to advertise to the SSH server as the version of
+ this client, defaulting to ``AsyncSSH`` and its version number.
:param kex_algs: (optional)
A list of allowed key exchange algorithms in the SSH handshake,
taken from :ref:`key exchange algorithms <KexAlgs>`
@@ -4002,11 +4047,12 @@ def create_connection(client_factory, host, port=_DEFAULT_PORT, *,
def conn_factory():
"""Return an SSH client connection handler"""
- return SSHClientConnection(client_factory, loop, kex_algs,
- encryption_algs, mac_algs, compression_algs,
- rekey_bytes, rekey_seconds, host, port,
- known_hosts, username, password,
- client_keys, agent, agent_path, auth_waiter)
+ return SSHClientConnection(client_factory, loop, client_version,
+ kex_algs, encryption_algs, mac_algs,
+ compression_algs, rekey_bytes,
+ rekey_seconds, host, port, known_hosts,
+ username, password, client_keys, agent,
+ agent_path, auth_waiter)
if not client_factory:
client_factory = SSHClient
@@ -4014,6 +4060,8 @@ def create_connection(client_factory, host, port=_DEFAULT_PORT, *,
if not loop:
loop = asyncio.get_event_loop()
+ client_version = _validate_version(client_version)
+
kex_algs, encryption_algs, mac_algs, compression_algs = \
_validate_algs(kex_algs, encryption_algs, mac_algs, compression_algs)
@@ -4077,7 +4125,7 @@ def create_connection(client_factory, host, port=_DEFAULT_PORT, *,
def create_server(server_factory, host=None, port=_DEFAULT_PORT, *,
loop=None, family=0, flags=socket.AI_PASSIVE, backlog=100,
reuse_address=None, server_host_keys, passphrase=None,
- authorized_client_keys=None, kex_algs=(),
+ authorized_client_keys=None, server_version=(), kex_algs=(),
encryption_algs=(), mac_algs=(), compression_algs=(),
allow_pty=True, agent_forwarding=True, session_factory=None,
session_encoding='utf-8', sftp_factory=None,
@@ -4128,6 +4176,9 @@ def create_server(server_factory, host=None, port=_DEFAULT_PORT, *,
:param authorized_client_keys: (optional)
A list of authorized user and CA public keys which should be
trusted for certifcate-based client public key authentication.
+ :param str server_version: (optional)
+ An ASCII string to advertise to SSH clients as the version of
+ this server, defaulting to ``AsyncSSH`` and its version number.
:param kex_algs: (optional)
A list of allowed key exchange algorithms in the SSH handshake,
taken from :ref:`key exchange algorithms <KexAlgs>`
@@ -4196,14 +4247,14 @@ def create_server(server_factory, host=None, port=_DEFAULT_PORT, *,
def conn_factory():
"""Return an SSH server connection handler"""
- return SSHServerConnection(server_factory, loop, kex_algs,
- encryption_algs, mac_algs, compression_algs,
- rekey_bytes, rekey_seconds,
- server_host_keys, authorized_client_keys,
- allow_pty, agent_forwarding,
- session_factory, session_encoding,
- sftp_factory, window, max_pktsize,
- login_timeout)
+ return SSHServerConnection(server_factory, loop, server_version,
+ kex_algs, encryption_algs, mac_algs,
+ compression_algs, rekey_bytes,
+ rekey_seconds, server_host_keys,
+ authorized_client_keys, allow_pty,
+ agent_forwarding, session_factory,
+ session_encoding, sftp_factory, window,
+ max_pktsize, login_timeout)
if not server_factory:
server_factory = SSHServer
@@ -4214,6 +4265,8 @@ def create_server(server_factory, host=None, port=_DEFAULT_PORT, *,
if not loop:
loop = asyncio.get_event_loop()
+ server_version = _validate_version(server_version)
+
kex_algs, encryption_algs, mac_algs, compression_algs = \
_validate_algs(kex_algs, encryption_algs, mac_algs, compression_algs)
@@ -4239,7 +4292,7 @@ def create_server(server_factory, host=None, port=_DEFAULT_PORT, *,
reuse_address=reuse_address))
- at asyncio.coroutine
+ at async_context_manager
def connect(host, port=_DEFAULT_PORT, **kwargs):
"""Make an SSH client connection
diff --git a/asyncssh/misc.py b/asyncssh/misc.py
index f41ca81..c918255 100644
--- a/asyncssh/misc.py
+++ b/asyncssh/misc.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2013-2015 by Ron Frederick <ronf at timeheart.net>.
+# Copyright (c) 2013-2016 by Ron Frederick <ronf at timeheart.net>.
# All rights reserved.
#
# This program and the accompanying materials are made available under
@@ -12,7 +12,10 @@
"""Miscellaneous utility classes and functions"""
+import asyncio
+import functools
import ipaddress
+import platform
import socket
from random import SystemRandom
@@ -20,6 +23,10 @@ from random import SystemRandom
from .constants import DEFAULT_LANG
+# Provide a global to test if we're on Python 3.5 or later
+python35 = platform.python_version_tuple() >= ('3', '5', '0')
+
+
# Define a version of randrange which is based on SystemRandom(), so that
# we get back numbers suitable for cryptographic use.
_random = SystemRandom()
@@ -81,6 +88,57 @@ def ip_network(addr):
return ipaddress.ip_network(_normalize_scoped_ip(addr) + mask)
+def async_context_manager(coro):
+ """Decorator for methods returning asynchronous context managers
+
+ This function can be used as a decorator for coroutines which
+ return objects intended to be used as Python 3.5 asynchronous
+ context managers. The object returned should implement __aenter__
+ and __aexit__ methods to run when the async context is entered
+ and exited.
+
+ This wrapper also allows non-async context managers to be defined
+ on the returned object, as well as the use of "await" or "yield
+ from" on the function being decorated for backward compatibility
+ with the API defined by older versions of AsyncSSH.
+
+ """
+
+ class AsyncContextManager:
+ """Async context manager wrapper for Python 3.5 and later"""
+
+ def __init__(self, coro):
+ self._coro = coro
+ self._result = None
+
+ def __iter__(self):
+ return (yield from self._coro)
+
+ def __await__(self):
+ return (yield from self._coro)
+
+ @asyncio.coroutine
+ def __aenter__(self):
+ self._result = yield from self._coro
+ return (yield from self._result.__aenter__())
+
+ @asyncio.coroutine
+ def __aexit__(self, *exc_info):
+ yield from self._result.__aexit__(*exc_info)
+ self._result = None
+
+ @functools.wraps(coro)
+ def coro_wrapper(*args, **kwargs):
+ """Return an async context manager wrapper for this coroutine"""
+
+ return AsyncContextManager(asyncio.coroutine(coro)(*args, **kwargs))
+
+ if python35:
+ return coro_wrapper
+ else:
+ return coro
+
+
class Error(Exception):
"""General SSH error"""
diff --git a/asyncssh/public_key.py b/asyncssh/public_key.py
index e25d614..c6a97a2 100644
--- a/asyncssh/public_key.py
+++ b/asyncssh/public_key.py
@@ -18,7 +18,7 @@ import time
try:
import bcrypt
- _bcrypt_available = True
+ _bcrypt_available = hasattr(bcrypt, 'kdf')
except ImportError: # pragma: no cover
_bcrypt_available = False
@@ -241,7 +241,7 @@ class SSHKey:
if not _bcrypt_available: # pragma: no cover
raise KeyExportError('OpenSSH private key encryption '
- 'requires bcrypt')
+ 'requires bcrypt with KDF support')
kdf = b'bcrypt'
salt = os.urandom(_OPENSSH_SALT_LEN)
@@ -699,7 +699,7 @@ def _decode_openssh_private(data, passphrase):
if not _bcrypt_available: # pragma: no cover
raise KeyEncryptionError('OpenSSH private key encryption '
- 'requires bcrypt')
+ 'requires bcrypt with KDF support')
packet = SSHPacket(kdf_data)
salt = packet.get_string()
diff --git a/asyncssh/sftp.py b/asyncssh/sftp.py
index ad431b6..c8c05fd 100644
--- a/asyncssh/sftp.py
+++ b/asyncssh/sftp.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015 by Ron Frederick <ronf at timeheart.net>.
+# Copyright (c) 2015-2016 by Ron Frederick <ronf at timeheart.net>.
# All rights reserved.
#
# This program and the accompanying materials are made available under
@@ -15,17 +15,15 @@
"""SFTP handlers"""
import asyncio
+from collections import OrderedDict
+import errno
+from fnmatch import fnmatch
import os
+from os import SEEK_SET, SEEK_CUR, SEEK_END
import posixpath
import stat
import time
-# pylint: disable=ungrouped-imports
-from asyncio import FIRST_COMPLETED
-from collections import OrderedDict
-from fnmatch import fnmatch
-from os import SEEK_SET, SEEK_CUR, SEEK_END
-
from .constants import DEFAULT_LANG
from .constants import FXP_INIT, FXP_VERSION, FXP_OPEN, FXP_CLOSE, FXP_READ
@@ -47,10 +45,9 @@ from .constants import FX_OK, FX_EOF, FX_NO_SUCH_FILE, FX_PERMISSION_DENIED
from .constants import FX_FAILURE, FX_BAD_MESSAGE, FX_NO_CONNECTION
from .constants import FX_CONNECTION_LOST, FX_OP_UNSUPPORTED
-from .misc import Error
-from .packet import Byte, String, UInt32, UInt64, PacketDecodeError, SSHPacket
+from .misc import Error, async_context_manager
-# pylint: enable=ungrouped-imports
+from .packet import Byte, String, UInt32, UInt64, PacketDecodeError, SSHPacket
_SFTP_VERSION = 3
_SFTP_BLOCK_SIZE = 16384
@@ -133,7 +130,7 @@ class _LocalFile:
def __enter__(self):
return self
- def __exit__(self, *excinfo):
+ def __exit__(self, *exc_info):
self._file.close()
@classmethod
@@ -144,10 +141,7 @@ class _LocalFile:
"""
- if isinstance(path, str):
- path = os.fsencode(path)
-
- return path
+ return os.fsencode(path)
@classmethod
def decode(cls, path, want_string=True):
@@ -204,69 +198,6 @@ class _LocalFile:
@classmethod
@asyncio.coroutine
- def truncate(cls, path):
- """Truncate a local file to the specified size"""
-
- os.truncate(path)
-
- @classmethod
- @asyncio.coroutine
- def chown(cls, path, uid, gid):
- """Change the owner user and group id of a local file or directory"""
-
- os.chown(path, uid, gid)
-
- @classmethod
- @asyncio.coroutine
- def chmod(cls, path, mode):
- """Change the file permissions of a local file or directory"""
-
- os.chmod(path, mode)
-
- @classmethod
- @asyncio.coroutine
- def utime(cls, path, times=None):
- """Change the access and modify times of a local file or directory"""
-
- os.utime(path, times)
-
- @classmethod
- @asyncio.coroutine
- def exists(cls, path):
- """Return if the local path exists and isn't a broken symbolic link"""
-
- return os.path.exists(path)
-
- @classmethod
- @asyncio.coroutine
- def lexists(cls, path):
- """Return if the local path exists, without following symbolic links"""
-
- return os.path.lexists(path)
-
- @classmethod
- @asyncio.coroutine
- def getatime(cls, path):
- """Return the last access time of a local file or directory"""
-
- return os.path.getatime(path)
-
- @classmethod
- @asyncio.coroutine
- def getmtime(cls, path):
- """Return the last modification time of a local file or directory"""
-
- return os.path.getmtime(path)
-
- @classmethod
- @asyncio.coroutine
- def getsize(cls, path):
- """Return the size of a local file or directory"""
-
- return os.path.getsize(path)
-
- @classmethod
- @asyncio.coroutine
def isdir(cls, path):
"""Return if the local path refers to a directory"""
@@ -274,53 +205,6 @@ class _LocalFile:
@classmethod
@asyncio.coroutine
- def isfile(cls, path):
- """Return if the local path refers to a regular file"""
-
- return os.path.isfile(path)
-
- @classmethod
- @asyncio.coroutine
- def islink(cls, path):
- """Return if the local path refers to a symbolic link"""
-
- return os.path.islink(path)
-
- @classmethod
- @asyncio.coroutine
- def remove(cls, path):
- """Remove a local file"""
-
- os.remove(path)
-
- @classmethod
- @asyncio.coroutine
- def unlink(cls, path):
- """Remove a local file (see :meth:`remove`)"""
-
- os.unlink(path)
-
- @classmethod
- @asyncio.coroutine
- def rename(cls, oldpath, newpath):
- """Rename a local file, directory, or link"""
-
- os.rename(oldpath, newpath)
-
- @classmethod
- @asyncio.coroutine
- def readdir(cls, path):
- """Read the contents of a local directory"""
-
- names = os.listdir(path)
-
- # This pylint error appears to be a false positive
- # pylint: disable=not-an-iterable
- return [SFTPName(filename=name, attrs=(yield from cls.stat(name)))
- for name in names]
-
- @classmethod
- @asyncio.coroutine
def listdir(cls, path):
"""Read the names of the files in a local directory"""
@@ -335,34 +219,6 @@ class _LocalFile:
@classmethod
@asyncio.coroutine
- def rmdir(cls, path):
- """Remove a local directory"""
-
- os.rmdir(path)
-
- @classmethod
- @asyncio.coroutine
- def realpath(cls, path):
- """Return the canonical version of a local path"""
-
- return os.path.realpath(path)
-
- @classmethod
- @asyncio.coroutine
- def getcwd(cls):
- """Return the local current working directory"""
-
- return os.getcwd()
-
- @classmethod
- @asyncio.coroutine
- def chdir(cls, path):
- """Change the local current working directory"""
-
- os.chdir(path)
-
- @classmethod
- @asyncio.coroutine
def readlink(cls, path):
"""Return the target of a local symbolic link"""
@@ -375,49 +231,20 @@ class _LocalFile:
os.symlink(oldpath, newpath)
- @classmethod
@asyncio.coroutine
- def link(cls, oldpath, newpath):
- """Create a local hard link"""
-
- os.link(oldpath, newpath)
-
- @asyncio.coroutine
- def read(self, size=-1, offset=None):
+ def read(self, size, offset):
"""Read data from the local file"""
- if offset is not None:
- self._file.seek(offset)
-
+ self._file.seek(offset)
return self._file.read(size)
@asyncio.coroutine
- def write(self, data, offset=None):
+ def write(self, data, offset):
"""Write data to the local file"""
- if offset is not None:
- self._file.seek(offset)
-
+ self._file.seek(offset)
return self._file.write(data)
- @asyncio.coroutine
- def seek(self, offset, from_what=SEEK_SET):
- """Seek to a new position in the local file"""
-
- return self._file.seek(offset, from_what)
-
- @asyncio.coroutine
- def tell(self):
- """Return the current position in the local file"""
-
- return self._file.tell()
-
- @asyncio.coroutine
- def close(self):
- """Close the local file"""
-
- self._file.close()
-
class _SFTPFileCopier:
"""SFTP file copier
@@ -438,11 +265,8 @@ class _SFTPFileCopier:
def _copy_block(self, offset, size):
"""Copy the next block of the file"""
- data = yield from self._src.read(size, offset=offset)
- if not data:
- return
-
- yield from self._dst.write(data, offset=offset)
+ data = yield from self._src.read(size, offset)
+ yield from self._dst.write(data, offset)
def _copy_blocks(self):
"""Create parallel requests to copy blocks from one file to another"""
@@ -465,15 +289,20 @@ class _SFTPFileCopier:
self._bytes_left = size
self._copy_blocks()
- try:
- while self._pending:
- _, self._pending = yield from asyncio.wait(
- self._pending, return_when=FIRST_COMPLETED)
+ while self._pending:
+ done, self._pending = yield from asyncio.wait(
+ self._pending, return_when=asyncio.FIRST_COMPLETED)
- self._copy_blocks()
- finally:
- for task in self._pending:
- task.cancel()
+ exceptions = [task.exception() for task in done
+ if task.exception()]
+
+ if exceptions:
+ for task in self._pending:
+ task.cancel()
+
+ raise exceptions[0]
+
+ self._copy_blocks()
class SFTPError(Error):
@@ -748,12 +577,13 @@ class SFTPHandler:
self._reader = reader
self._writer = writer
+ @asyncio.coroutine
def _cleanup(self, exc):
"""Clean up this SFTP session"""
# pylint: disable=unused-argument
- if self._writer:
+ if self._writer: # pragma: no branch
self._writer.close()
self._reader = None
self._writer = None
@@ -777,7 +607,7 @@ class SFTPHandler:
try:
self._writer.write(UInt32(len(payload)) + payload)
except ConnectionError as exc:
- raise SFTPError(FX_CONNECTION_LOST, str(exc))
+ raise SFTPError(FX_CONNECTION_LOST, str(exc)) from None
@asyncio.coroutine
def recv_packet(self):
@@ -789,8 +619,8 @@ class SFTPHandler:
packet = yield from self._reader.readexactly(pktlen)
packet = SSHPacket(packet)
- except (ConnectionError, EOFError) as exc:
- raise SFTPError(FX_CONNECTION_LOST, str(exc))
+ except EOFError:
+ raise SFTPError(FX_CONNECTION_LOST, 'Channel closed') from None
return packet
@@ -799,7 +629,7 @@ class SFTPHandler:
"""Receive and process SFTP packets"""
try:
- while self._reader:
+ while self._reader: # pragma: no branch
packet = yield from self.recv_packet()
... 4072 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-asyncssh.git
More information about the Python-modules-commits
mailing list