Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-paramiko-expect for openSUSE:Factory checked in at 2022-04-08 00:28:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-paramiko-expect (Old) and /work/SRC/openSUSE:Factory/.python-paramiko-expect.new.1900 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-paramiko-expect" Fri Apr 8 00:28:25 2022 rev:3 rq:967527 version:0.3.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-paramiko-expect/python-paramiko-expect.changes 2020-04-21 13:10:26.216784721 +0200 +++ /work/SRC/openSUSE:Factory/.python-paramiko-expect.new.1900/python-paramiko-expect.changes 2022-04-08 00:29:03.893413972 +0200 @@ -1,0 +2,6 @@ +Thu Apr 7 11:55:05 UTC 2022 - pgaj...@suse.com + +- version update to 0.3.2 + * no upstream changelog file found + +------------------------------------------------------------------- Old: ---- paramiko-expect-0.2.8.tar.gz New: ---- paramiko-expect-0.3.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-paramiko-expect.spec ++++++ --- /var/tmp/diff_new_pack.4S0XTe/_old 2022-04-08 00:29:04.481407343 +0200 +++ /var/tmp/diff_new_pack.4S0XTe/_new 2022-04-08 00:29:04.489407252 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-paramiko-expect # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %bcond_without python2 Name: python-paramiko-expect -Version: 0.2.8 +Version: 0.3.2 Release: 0 Summary: An expect-like extension for the Paramiko SSH library License: MIT ++++++ paramiko-expect-0.2.8.tar.gz -> paramiko-expect-0.3.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/paramiko-expect-0.2.8/PKG-INFO new/paramiko-expect-0.3.2/PKG-INFO --- old/paramiko-expect-0.2.8/PKG-INFO 2017-05-15 23:02:20.000000000 +0200 +++ new/paramiko-expect-0.3.2/PKG-INFO 2022-01-13 13:07:35.973393200 +0100 @@ -1,6 +1,6 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: paramiko-expect -Version: 0.2.8 +Version: 0.3.2 Summary: An expect-like extension for the Paramiko SSH library Home-page: https://github.com/fgimian/paramiko-expect Author: Fotis Gimian @@ -12,16 +12,16 @@ .. image:: https://img.shields.io/pypi/l/paramiko-expect.svg :target: https://github.com/fgimian/paramiko-expect/blob/master/LICENSE - .. image:: https://coveralls.io/repos/github/fgimian/paramiko-expect/badge.svg?branch=master - :target: https://coveralls.io/github/fgimian/paramiko-expect?branch=master + .. image:: https://codecov.io/gh/fgimian/paramiko-expect/branch/master/graph/badge.svg + :target: https://codecov.io/gh/fgimian/paramiko-expect - .. image:: https://img.shields.io/travis/fgimian/paramiko-expect.svg + .. image:: https://img.shields.io/travis/fgimian/paramiko-expect.svg :target: https://travis-ci.org/fruch/paramiko-expect/ - .. image:: https://img.shields.io/pypi/v/paramiko-expect.svg + .. image:: https://img.shields.io/pypi/v/paramiko-expect.svg :target: https://pypi.python.org/pypi/paramiko-expect/ - .. image:: https://img.shields.io/pypi/pyversions/paramiko-expect.svg + .. image:: https://img.shields.io/pypi/pyversions/paramiko-expect.svg :target: https://pypi.python.org/pypi/paramiko-expect/ @@ -49,7 +49,7 @@ .. code:: bash # from pypi - pip insall paramiko-expect + pip install paramiko-expect # from source pip install git+https://github.com/fgimian/paramiko-expect.git @@ -183,7 +183,7 @@ .. code:: bash pip install -r requirements-test.txt - docker run -d -p 2222:22 -v `pwd`/examples:/examples -v `pwd`/test/id_rsa.pub:/root/.ssh/authorized_keys macropin/sshd + docker run -d -p 2222:22 -v `pwd`/examples:/examples -v `pwd`/test/id_rsa.pub:/root/.ssh/authorized_keys docker.io/panubo/sshd pytest -s --cov paramiko_expect --cov-report term-missing @@ -204,11 +204,10 @@ Platform: Posix Classifier: Development Status :: 4 - Beta Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Intended Audience :: Developers +Description-Content-Type: text/x-rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/paramiko-expect-0.2.8/README.rst new/paramiko-expect-0.3.2/README.rst --- old/paramiko-expect-0.2.8/README.rst 2017-05-15 23:01:24.000000000 +0200 +++ new/paramiko-expect-0.3.2/README.rst 2021-01-24 21:23:29.000000000 +0100 @@ -4,16 +4,16 @@ .. image:: https://img.shields.io/pypi/l/paramiko-expect.svg :target: https://github.com/fgimian/paramiko-expect/blob/master/LICENSE -.. image:: https://coveralls.io/repos/github/fgimian/paramiko-expect/badge.svg?branch=master - :target: https://coveralls.io/github/fgimian/paramiko-expect?branch=master +.. image:: https://codecov.io/gh/fgimian/paramiko-expect/branch/master/graph/badge.svg + :target: https://codecov.io/gh/fgimian/paramiko-expect -.. image:: https://img.shields.io/travis/fgimian/paramiko-expect.svg +.. image:: https://img.shields.io/travis/fgimian/paramiko-expect.svg :target: https://travis-ci.org/fruch/paramiko-expect/ -.. image:: https://img.shields.io/pypi/v/paramiko-expect.svg +.. image:: https://img.shields.io/pypi/v/paramiko-expect.svg :target: https://pypi.python.org/pypi/paramiko-expect/ -.. image:: https://img.shields.io/pypi/pyversions/paramiko-expect.svg +.. image:: https://img.shields.io/pypi/pyversions/paramiko-expect.svg :target: https://pypi.python.org/pypi/paramiko-expect/ @@ -41,7 +41,7 @@ .. code:: bash # from pypi - pip insall paramiko-expect + pip install paramiko-expect # from source pip install git+https://github.com/fgimian/paramiko-expect.git @@ -175,7 +175,7 @@ .. code:: bash pip install -r requirements-test.txt - docker run -d -p 2222:22 -v `pwd`/examples:/examples -v `pwd`/test/id_rsa.pub:/root/.ssh/authorized_keys macropin/sshd + docker run -d -p 2222:22 -v `pwd`/examples:/examples -v `pwd`/test/id_rsa.pub:/root/.ssh/authorized_keys docker.io/panubo/sshd pytest -s --cov paramiko_expect --cov-report term-missing diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/paramiko-expect-0.2.8/paramiko_expect.egg-info/PKG-INFO new/paramiko-expect-0.3.2/paramiko_expect.egg-info/PKG-INFO --- old/paramiko-expect-0.2.8/paramiko_expect.egg-info/PKG-INFO 2017-05-15 23:02:20.000000000 +0200 +++ new/paramiko-expect-0.3.2/paramiko_expect.egg-info/PKG-INFO 2022-01-13 13:07:35.000000000 +0100 @@ -1,6 +1,6 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: paramiko-expect -Version: 0.2.8 +Version: 0.3.2 Summary: An expect-like extension for the Paramiko SSH library Home-page: https://github.com/fgimian/paramiko-expect Author: Fotis Gimian @@ -12,16 +12,16 @@ .. image:: https://img.shields.io/pypi/l/paramiko-expect.svg :target: https://github.com/fgimian/paramiko-expect/blob/master/LICENSE - .. image:: https://coveralls.io/repos/github/fgimian/paramiko-expect/badge.svg?branch=master - :target: https://coveralls.io/github/fgimian/paramiko-expect?branch=master + .. image:: https://codecov.io/gh/fgimian/paramiko-expect/branch/master/graph/badge.svg + :target: https://codecov.io/gh/fgimian/paramiko-expect - .. image:: https://img.shields.io/travis/fgimian/paramiko-expect.svg + .. image:: https://img.shields.io/travis/fgimian/paramiko-expect.svg :target: https://travis-ci.org/fruch/paramiko-expect/ - .. image:: https://img.shields.io/pypi/v/paramiko-expect.svg + .. image:: https://img.shields.io/pypi/v/paramiko-expect.svg :target: https://pypi.python.org/pypi/paramiko-expect/ - .. image:: https://img.shields.io/pypi/pyversions/paramiko-expect.svg + .. image:: https://img.shields.io/pypi/pyversions/paramiko-expect.svg :target: https://pypi.python.org/pypi/paramiko-expect/ @@ -49,7 +49,7 @@ .. code:: bash # from pypi - pip insall paramiko-expect + pip install paramiko-expect # from source pip install git+https://github.com/fgimian/paramiko-expect.git @@ -183,7 +183,7 @@ .. code:: bash pip install -r requirements-test.txt - docker run -d -p 2222:22 -v `pwd`/examples:/examples -v `pwd`/test/id_rsa.pub:/root/.ssh/authorized_keys macropin/sshd + docker run -d -p 2222:22 -v `pwd`/examples:/examples -v `pwd`/test/id_rsa.pub:/root/.ssh/authorized_keys docker.io/panubo/sshd pytest -s --cov paramiko_expect --cov-report term-missing @@ -204,11 +204,10 @@ Platform: Posix Classifier: Development Status :: 4 - Beta Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Intended Audience :: Developers +Description-Content-Type: text/x-rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/paramiko-expect-0.2.8/paramiko_expect.py new/paramiko-expect-0.3.2/paramiko_expect.py --- old/paramiko-expect-0.2.8/paramiko_expect.py 2017-05-15 23:01:24.000000000 +0200 +++ new/paramiko-expect-0.3.2/paramiko_expect.py 2021-12-14 15:03:04.000000000 +0100 @@ -13,25 +13,31 @@ # from __future__ import unicode_literals +import codecs import sys import re import socket import struct +import time # Windows does not have termios try: import termios import tty has_termios = True -except ImportError: # pragma: no cover + MAX_TIMEOUT = 2 ** (struct.Struct(str('i')).size * 8 - 1) - 1 +except ImportError: # pragma: no cover import threading has_termios = False + MAX_TIMEOUT = threading.TIMEOUT_MAX import select + def strip_ansi_codes(s): return re.sub(r'\x1b\[([0-9,A-Z]{1,2}(;[0-9]{1,2})?(;[0-9]{3})?)?[m|K]?', '', s) + def default_output_func(msg): sys.stdout.write(msg) sys.stdout.flush() @@ -52,12 +58,14 @@ real-time as it is being performed (especially useful when debugging) :param encoding: The character encoding to use. + :param lines_to_check: The number of last few lines of the output to + look at, while matching regular expression(s) """ def __init__( self, client, timeout=60, newline='\r', buffer_size=1024, display=False, encoding='utf-8', output_callback=default_output_func, - tty_width=80, tty_height=24 + tty_width=80, tty_height=24, lines_to_check=1 ): self.channel = client.invoke_shell(width=tty_width, height=tty_height) self.timeout = timeout @@ -66,12 +74,17 @@ self.display = display self.encoding = encoding self.output_callback = output_callback + self.lines_to_check = lines_to_check self.current_output = '' self.current_output_clean = '' self.current_send_string = '' self.last_match = '' + # If the output is long, multi-byte encoded characters may be split + # across calls to recv, so decode incrementally. + self.decoder = codecs.getincrementaldecoder(self.encoding)() + def __del__(self): self.close() @@ -85,10 +98,13 @@ """Attempts to close the channel for clean completion.""" try: self.channel.close() - except: + except Exception: pass - def expect(self, re_strings='', timeout=None, output_callback=None, default_match_prefix='.*\n', strip_ansi=True): + def expect( + self, re_strings='', timeout=None, output_callback=None, default_match_prefix='.*\n', + strip_ansi=True, ignore_decode_error=True, lines_to_check=None + ): """ This function takes in a regular expression (or regular expressions) that represent the last line of output from the server. The function @@ -111,6 +127,10 @@ or the command has no output. :param strip_ansi: If True, will strip ansi control chars befores regex matching default to True. + :param ignore_decode_error: If True, will ignore decode errors if any. + default to True. + :param lines_to_check: The number of last few lines of the output to + look at, while matching regular expression(s) :return: An EOF returns -1, a regex metch returns 0 and a match in a list of regexes returns the index of the matched string in the list. @@ -122,15 +142,21 @@ timeout = timeout if timeout else self.timeout self.channel.settimeout(timeout) + lines_to_check = lines_to_check if lines_to_check else self.lines_to_check + + if ignore_decode_error: + self.decoder = codecs.getincrementaldecoder(self.encoding)('ignore') # Create an empty output buffer self.current_output = '' - + # saves the current buffer to check for re_strings pattern + current_buffer_output_decoded = '' # This function needs all regular expressions to be in the form of a # list, so if the user provided a string, let's convert it to a 1 # item list. if isinstance(re_strings, str) and len(re_strings) != 0: re_strings = [re_strings] - + # to avoid looping in recv_ready() + base_time = time.time() # Loop until one of the expressions is matched or loop forever if # nothing is expected (usually used for exit) while ( @@ -138,8 +164,15 @@ not [re_string for re_string in re_strings if re.match(default_match_prefix + re_string + '$', - self.current_output, re.DOTALL)] + current_buffer_output_decoded, re.DOTALL)] ): + current_buffer_output_decoded = '' + # avoids paramiko hang when recv is not ready yet + while not self.channel.recv_ready(): + time.sleep(.009) + if time.time() >= (base_time + timeout): + print('EXCESS TIME RECV_READY TIMEOUT, did you expect() before a send()') + return -1 # Read some of the output current_buffer = self.channel.recv(self.buffer_size) @@ -148,7 +181,7 @@ break # Convert the buffer to our chosen encoding - current_buffer_decoded = current_buffer.decode(self.encoding) + current_buffer_decoded = self.decoder.decode(current_buffer) # Strip all ugly \r (Ctrl-M making) characters from the current # read @@ -156,14 +189,18 @@ # Display the current buffer in realtime if requested to do so # (good for debugging purposes) - if self.display: - output_callback(current_buffer_decoded) - if strip_ansi: current_buffer_decoded = strip_ansi_codes(current_buffer_decoded) + if not current_buffer_decoded: + continue + + if self.display: + output_callback(current_buffer_decoded) + # Add the currently read buffer to the output self.current_output += current_buffer_decoded + current_buffer_output_decoded = '\n' + '\n'.join(self.current_output.splitlines()[-lines_to_check:]) # Grab the first pattern that was matched if len(re_strings) != 0: @@ -177,7 +214,7 @@ if len(self.current_send_string) != 0: self.current_output_clean = ( self.current_output_clean.replace( - self.current_send_string + '\n', '' + self.current_send_string + self.newline, '' ) ) @@ -200,12 +237,21 @@ # measure, let's send back a -1 return -1 - def send(self, send_string): + def send(self, send_string, newline=None): """Saves and sends the send string provided.""" self.current_send_string = send_string - self.channel.send(send_string + self.newline) - - def tail(self, line_prefix=None, callback=None, output_callback=None, stop_callback=lambda x: False, timeout=None): + # send_string, _ = codecs.getdecoder(self.encoding)(send_string) + newline = newline if newline is not None else self.newline + # don't send till send_ready + while not self.channel.send_ready(): + time.sleep(.009) + self.channel.send(send_string) + self.channel.send(newline) + + def tail( + self, line_prefix=None, callback=None, output_callback=None, stop_callback=lambda x: False, + timeout=None + ): """ This function takes control of an SSH channel and displays line by line of output as \n is recieved. This function is specifically @@ -225,18 +271,18 @@ :param output_callback: A function used to print ssh output. Printed to stdout by default. A user-defined logger may be passed like output_callback=lambda m: mylog.debug(m) - :param stop_callback: A function usesd to stop the tail, when function retruns + :param stop_callback: A function usesd to stop the tail, when function retruns True tail will stop, by default stop_callback=lambda x: False - :param timeout: how much time to wait for data, default to None which + :param timeout: how much time to wait for data, default to None which mean almost forever. """ output_callback = output_callback if output_callback else self.output_callback - # Set the channel timeout to the maximum integer the server allows, + # Set the channel timeout to the maximum allowed value, # setting this to None breaks the KeyboardInterrupt exception and - # won't allow us to Ctrl+C out of teh script - timeout = timeout if timeout else 2 ** (struct.Struct(str('i')).size * 8 - 1) - 1 + # won't allow us to Ctrl+C out of the script + timeout = timeout if timeout else MAX_TIMEOUT self.channel.settimeout(timeout) # Create an empty line buffer and a line counter @@ -260,7 +306,7 @@ # Display the last read line in realtime when we reach a \n # character if buffer == line_feed_byte: - current_line_decoded = current_line.decode(self.encoding) + current_line_decoded = self.decoder.decode(current_line) if line_counter: if callback: output_callback(callback(line_prefix, current_line_decoded)) @@ -307,7 +353,7 @@ buffer = self.channel.recv(self.buffer_size) if len(buffer) == 0: break - sys.stdout.write(buffer.decode(self.encoding)) + sys.stdout.write(self.decoder.decode(buffer)) sys.stdout.flush() except socket.timeout: pass @@ -327,7 +373,7 @@ buffer = sock.recv(self.buffer_size) if len(buffer) == 0: break - sys.stdout.write(buffer.decode(self.encoding)) + sys.stdout.write(self.decoder.decode(buffer)) sys.stdout.flush() writer = threading.Thread(target=writeall, args=(self.channel,)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/paramiko-expect-0.2.8/setup.cfg new/paramiko-expect-0.3.2/setup.cfg --- old/paramiko-expect-0.2.8/setup.cfg 2017-05-15 23:02:20.000000000 +0200 +++ new/paramiko-expect-0.3.2/setup.cfg 2022-01-13 13:07:35.973393200 +0100 @@ -4,5 +4,4 @@ [egg_info] tag_build = tag_date = 0 -tag_svn_revision = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/paramiko-expect-0.2.8/setup.py new/paramiko-expect-0.3.2/setup.py --- old/paramiko-expect-0.2.8/setup.py 2017-05-15 23:01:24.000000000 +0200 +++ new/paramiko-expect-0.3.2/setup.py 2022-01-13 13:07:25.000000000 +0100 @@ -8,13 +8,14 @@ setup( name='paramiko-expect', - version='0.2.8', + version='0.3.2', url='https://github.com/fgimian/paramiko-expect', license='MIT', author='Fotis Gimian', author_email='fgimiansoftw...@gmail.com', description='An expect-like extension for the Paramiko SSH library', long_description=long_description, + long_description_content_type='text/x-rst', platforms='Posix', py_modules=['paramiko_expect'], install_requires=[ @@ -23,9 +24,7 @@ classifiers=[ 'Development Status :: 4 - Beta', 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/paramiko-expect-0.2.8/test/test_paramiko_expect.py new/paramiko-expect-0.3.2/test/test_paramiko_expect.py --- old/paramiko-expect-0.2.8/test/test_paramiko_expect.py 2017-05-15 23:01:24.000000000 +0200 +++ new/paramiko-expect-0.3.2/test/test_paramiko_expect.py 2022-01-13 13:07:25.000000000 +0100 @@ -1,5 +1,5 @@ -import io -import sys +# -*- coding: utf-8 -*- + import socket import pytest @@ -13,15 +13,15 @@ from paramiko_expect import SSHClientInteraction try: - from contextlib import ExitStack, contextmanager + from contextlib import ExitStack except ImportError: - from contextlib2 import ExitStack, contextmanager + from contextlib2 import ExitStack + +prompt = ".*:~#.*" -prompt=".*:~#.*" @pytest.fixture(scope="module") def interact(request): - # Create a new SSH client object client = paramiko.SSHClient() # Set SSH key parameters to auto accept unknown hosts @@ -30,11 +30,11 @@ client.connect(hostname="localhost", username="root", port=2222, key_filename='./test/id_rsa') # Create a client interaction class which will interact with the host interact = SSHClientInteraction(client, timeout=10, display=True) - + def fin(): interact.send('exit') interact.expect() - + interact.close() request.addfinalizer(fin) @@ -42,23 +42,24 @@ def test_01_install_python(interact): - interact.send('apk update') interact.expect(prompt, timeout=120) - interact.send('apk add python') + interact.send('apk add python3') interact.expect(prompt, timeout=120) interact.send('apk add curl') interact.expect(prompt, timeout=120) + def test_02_test_other_commnads(interact): interact.send('ls -l /') interact.expect(prompt, timeout=5) + def test_03_test_demo_helper(interact): interact.expect(prompt) - interact.send('python /examples/paramiko_expect-demo-helper.py') + interact.send('python3 /examples/paramiko_expect-demo-helper.py') found_index = interact.expect([prompt, '.*Please enter your name:.*']) assert interact.last_match == '.*Please enter your name:.*' assert found_index == 1 @@ -68,38 +69,41 @@ def test_04_tail(interact): - interact.send('sleep 1; curl -v https://httpbin.org/stream/100') + def stop_callback(msg): - return "Connection #0 to host httpbin.org left intact" in msg + return "Connection #0 to host httpbin.org left intact" in msg interact.tail(stop_callback=stop_callback) -def test_04_tail_line_prefix(interact): +def test_04_tail_line_prefix(interact): interact.send('sleep 1; curl -v https://httpbin.org/stream/100') + def stop_callback(msg): - return "Connection #0 to host httpbin.org left intact" in msg + return "Connection #0 to host httpbin.org left intact" in msg interact.tail(line_prefix="test:", stop_callback=stop_callback) -def test_04_tail_callback(interact): +def test_04_tail_callback(interact): interact.send('sleep 1; curl -v https://httpbin.org/stream/100') + def stop_callback(msg): - return "Connection #0 to host httpbin.org left intact" in msg - interact.tail(line_prefix="test:", callback=lambda p, m: "" ,stop_callback=stop_callback) + return "Connection #0 to host httpbin.org left intact" in msg + interact.tail(line_prefix="test:", callback=lambda p, m: "", stop_callback=stop_callback) -def test_04_tail_empty_response(interact): +def test_04_tail_empty_response(interact): interact.send('sleep 1; curl -v https://httpbin.org/stream/100') + def stop_callback(msg): - return "Connection #0 to host httpbin.org left intact" in msg + return "Connection #0 to host httpbin.org left intact" in msg with mock.patch.object(interact, 'channel') as channel_mock: - channel_mock.recv.side_effect = [ b"" ] - interact.tail(line_prefix="test:", callback=lambda p, m: "" ,stop_callback=stop_callback) + channel_mock.recv.side_effect = [b""] + interact.tail(line_prefix="test:", callback=lambda p, m: "", stop_callback=stop_callback) -def test_05_context(): +def test_05_context(): client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect(hostname="localhost", username="root", port=2222, key_filename='./test/id_rsa') @@ -107,58 +111,71 @@ interact.send('ls -all /') interact.expect(prompt, timeout=120) -def test_06_take_control_01(interact): +def test_06_take_control_01(interact): with ExitStack() as stack: - mocks = [ mock.patch('termios.tcsetattr'), mock.patch('termios.tcgetattr'), - mock.patch('tty.setraw'), mock.patch('tty.setcbreak') ] - [ stack.enter_context(m) for m in mocks ] + mocks = [ + mock.patch('termios.tcsetattr'), mock.patch('termios.tcgetattr'), + mock.patch('tty.setraw'), mock.patch('tty.setcbreak') + ] + [stack.enter_context(m) for m in mocks] select_mock = stack.enter_context(mock.patch('select.select')) channel_mock = stack.enter_context(mock.patch.object(interact, 'channel')) stdin_mock = stack.enter_context(mock.patch('sys.stdin')) - channel_mock.recv.side_effect = [ b"test", b"test" ] + channel_mock.recv.side_effect = [b"test", b"test"] stdin_mock.read.side_effect = [b"ls -all\n", b""] - select_mock.side_effect = [ [[stdin_mock,], [], []], [[stdin_mock, channel_mock], [], []] ] + select_mock.side_effect = [ + [[stdin_mock], [], []], + [[stdin_mock, channel_mock], [], []] + ] interact.take_control() def test_06_take_control_02(interact): - with ExitStack() as stack: - mocks = [ mock.patch('termios.tcsetattr'), mock.patch('termios.tcgetattr'), - mock.patch('tty.setraw'), mock.patch('tty.setcbreak') ] - [ stack.enter_context(m) for m in mocks ] + mocks = [ + mock.patch('termios.tcsetattr'), mock.patch('termios.tcgetattr'), + mock.patch('tty.setraw'), mock.patch('tty.setcbreak') + ] + [stack.enter_context(m) for m in mocks] select_mock = stack.enter_context(mock.patch('select.select')) channel_mock = stack.enter_context(mock.patch.object(interact, 'channel')) stdin_mock = stack.enter_context(mock.patch('sys.stdin')) - channel_mock.recv.side_effect = [ socket.timeout() ] + channel_mock.recv.side_effect = [socket.timeout()] stdin_mock.read.side_effect = [b"ls -all\n", b""] - select_mock.side_effect = [ [[stdin_mock,], [], []], [[stdin_mock, channel_mock], [], []] ] + select_mock.side_effect = [ + [[stdin_mock], [], []], + [[stdin_mock, channel_mock], [], []] + ] interact.take_control() def test_06_take_control_03(interact): - with ExitStack() as stack: - mocks = [ mock.patch('termios.tcsetattr'), mock.patch('termios.tcgetattr'), - mock.patch('tty.setraw'), mock.patch('tty.setcbreak') ] - [ stack.enter_context(m) for m in mocks ] + mocks = [ + mock.patch('termios.tcsetattr'), mock.patch('termios.tcgetattr'), + mock.patch('tty.setraw'), mock.patch('tty.setcbreak') + ] + [stack.enter_context(m) for m in mocks] select_mock = stack.enter_context(mock.patch('select.select')) channel_mock = stack.enter_context(mock.patch.object(interact, 'channel')) stdin_mock = stack.enter_context(mock.patch('sys.stdin')) - channel_mock.recv.side_effect = [ "" ] + channel_mock.recv.side_effect = [""] stdin_mock.read.side_effect = [b"ls -all\n", b""] - select_mock.side_effect = [ [[stdin_mock,], [], []], [[stdin_mock, channel_mock], [], []] ] + select_mock.side_effect = [ + [[stdin_mock], [], []], + [[stdin_mock, channel_mock], [], []] + ] interact.take_control() -def test_06_take_control_no_termios_01(interact): +def test_06_take_control_no_termios_01(interact): paramiko_expect.has_termios = False import threading paramiko_expect.threading = threading @@ -167,7 +184,7 @@ channel_mock = stack.enter_context(mock.patch.object(interact, 'channel')) stdin_mock = stack.enter_context(mock.patch('sys.stdin')) - channel_mock.recv.side_effect = [ b"test" ] + channel_mock.recv.side_effect = [b"test"] stdin_mock.read.side_effect = [b"ls -all\n", b""] interact.take_control() @@ -182,12 +199,12 @@ channel_mock = stack.enter_context(mock.patch.object(interact, 'channel')) stdin_mock = stack.enter_context(mock.patch('sys.stdin')) - channel_mock.recv.side_effect = [ b"" ] + channel_mock.recv.side_effect = [b""] stdin_mock.read.side_effect = [b"ls -all\n", b""] interact.take_control() -def test_06_take_control_no_termios_03(interact): +def test_06_take_control_no_termios_03(interact): paramiko_expect.has_termios = False import threading paramiko_expect.threading = threading @@ -196,14 +213,33 @@ channel_mock = stack.enter_context(mock.patch.object(interact, 'channel')) stdin_mock = stack.enter_context(mock.patch('sys.stdin')) - channel_mock().recv.side_effect = [ b"test", b"test"] + channel_mock().recv.side_effect = [b"test", b"test"] stdin_mock.read.side_effect = [b"ls -all\n", EOFError()] interact.take_control() def test_07_close(interact): - with mock.patch.object(interact, 'channel') as channel_mock: - channel_mock.close.side_effect = [ socket.timeout ] + channel_mock.close.side_effect = [socket.timeout] interact.close() + +def test_08_issue_25_skip_newline(): + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + client.connect(hostname="localhost", username="root", port=2222, key_filename='./test/id_rsa') + with SSHClientInteraction(client, timeout=10, display=True) as interact: + interact.send('ls -all') + interact.expect(prompt, timeout=5) + + # Do not actually sleep, send a ctrl-c at the end + interact.send('sleep 1', newline=chr(3)) + interact.expect(prompt, timeout=5) + interact.send('sleep 1' + chr(3), newline='') + interact.expect(prompt, timeout=5) + + interact.send('ls -all') + interact.expect(prompt, timeout=5) + +def test_09_utf8(interact): + interact.send(u'Andr??')