Skip to content

Commit 78f1bb3

Browse files
author
Pan
committed
Added generator reset on call to join, tests.
Updated changelog, docs
1 parent ad6a9b4 commit 78f1bb3

File tree

4 files changed

+51
-17
lines changed

4 files changed

+51
-17
lines changed

Changelog.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
Change Log
22
============
33

4+
1.5.2
5+
++++++
6+
7+
Changes
8+
--------
9+
10+
* Output generators automatically restarted on call to ``join`` so output can resume on any timeouts.
11+
412
1.5.1
513
++++++
614

doc/advanced.rst

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,8 @@ In some cases, such as when the remote command never terminates unless interrupt
186186
client.join(output, timeout=1)
187187
# Closing channel which has PTY has the effect of terminating
188188
# any running processes started on that channel.
189-
for host in client.hosts:
190-
client.host_clients[host].close_channel(output[host].channel)
189+
for host, host_out in output:
190+
client.host_clients[host].close_channel(host_out.channel)
191191
client.join(output)
192192
193193
Without a PTY, the ``join`` will complete but the remote process will be left running as per SSH protocol specifications.
@@ -196,16 +196,14 @@ Furthermore, once reading output has timed out, it is necessary to restart the o
196196

197197
.. code-block:: python
198198
199-
output = client.run_command(.., timeout=1)
199+
output = client.run_command(<..>, timeout=1)
200200
for host, host_out in output.items():
201201
try:
202202
stdout = list(host_out.stdout)
203203
except Timeout:
204-
stdout_buf = client.host_clients[host].read_output_buffer(
205-
client.host_clients[host].read_output(
206-
output[host].channel, timeout=1))
207-
# Reset generator to be able to gather new output
208-
host_out.stdout = stdout_buf
204+
client.reset_output_generators(host_out)
205+
206+
Generator reset shown above is also performed automatically by calls to ``join`` and does not need to be done manually ``join`` is used after output reading.
209207

210208
.. note::
211209

pssh/pssh2_client.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def run_command(self, command, sudo=False, user=None, stop_on_errors=True,
150150
raised otherwise
151151
:type host_args: tuple or list
152152
:param encoding: Encoding to use for output. Must be valid
153-
`Python codec <https://docs.python.org/2.7/library/codecs.html>`_
153+
`Python codec <https://docs.python.org/library/codecs.html>`_
154154
:type encoding: str
155155
:param timeout: (Optional) Timeout in seconds for reading from stdout
156156
or stderr. Defaults to no timeout. Reading from stdout/stderr will
@@ -218,14 +218,14 @@ def join(self, output, consume_output=False, timeout=None):
218218
continue
219219
client = self.host_clients[host]
220220
channel = host_out.channel
221+
stdout, stderr = self.reset_output_generators(
222+
host_out, client=client, channel=channel, timeout=timeout)
221223
try:
222224
client.wait_finished(channel, timeout=timeout)
223225
except Timeout:
224226
raise Timeout(
225227
"Timeout of %s sec(s) reached on host %s with command "
226228
"still running", timeout, host)
227-
stdout = host_out.stdout
228-
stderr = host_out.stderr
229229
if timeout:
230230
# Must consume buffers prior to EOF check
231231
self._consume_output(stdout, stderr)
@@ -237,6 +237,36 @@ def join(self, output, consume_output=False, timeout=None):
237237
self._consume_output(stdout, stderr)
238238
self.get_exit_codes(output)
239239

240+
def reset_output_generators(self, host_out, timeout=None,
241+
client=None, channel=None,
242+
encoding='utf-8'):
243+
"""Reset output generators for host output.
244+
245+
:param host_out: Host output
246+
:type host_out: :py:class:`pssh.output.HostOutput`
247+
:param client: (Optional) SSH client
248+
:type client: :py:class:`pssh.ssh2_client.SSHClient`
249+
:param channel: (Optional) SSH channel
250+
:type channel: :py:class:`ssh2.channel.Channel`
251+
:param timeout: (Optional) Timeout setting
252+
:type timeout: int
253+
:param encoding: (Optional) Encoding to use for output. Must be valid
254+
`Python codec <https://docs.python.org/library/codecs.html>`_
255+
:type encoding: str
256+
257+
:rtype: tuple(stdout, stderr)
258+
"""
259+
channel = host_out.channel if channel is None else channel
260+
client = self.host_clients[host_out.host] if client is None else client
261+
stdout = client.read_output_buffer(
262+
client.read_output(channel, timeout=timeout), encoding=encoding)
263+
stderr = client.read_output_buffer(
264+
client.read_stderr(channel, timeout=timeout),
265+
prefix='\t[err]', encoding=encoding)
266+
host_out.stdout = stdout
267+
host_out.stderr = stderr
268+
return stdout, stderr
269+
240270
def _consume_output(self, stdout, stderr):
241271
for line in stdout:
242272
pass

tests/test_pssh_ssh2_client.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,18 +1193,15 @@ def test_join_timeout_set_no_timeout(self):
11931193
def test_read_timeout(self):
11941194
client = ParallelSSHClient([self.host], port=self.port,
11951195
pkey=self.user_key)
1196-
output = client.run_command('sleep 2; echo me', timeout=1)
1196+
output = client.run_command('sleep 2; echo me; echo me; echo me', timeout=1)
11971197
for host, host_out in output.items():
11981198
self.assertRaises(Timeout, list, host_out.stdout)
11991199
self.assertFalse(output[self.host].channel.eof())
12001200
client.join(output)
12011201
for host, host_out in output.items():
1202-
stdout_buf = client.host_clients[self.host].read_output_buffer(
1203-
client.host_clients[self.host].read_output(
1204-
output[self.host].channel, timeout=1))
1205-
host_out.stdout = stdout_buf
12061202
stdout = list(output[self.host].stdout)
1207-
self.assertEqual(len(stdout), 1)
1203+
self.assertEqual(len(stdout), 3)
1204+
self.assertTrue(output[self.host].channel.eof())
12081205

12091206
def test_timeout_file_read(self):
12101207
dir_name = os.path.dirname(__file__)
@@ -1225,6 +1222,7 @@ def test_timeout_file_read(self):
12251222
pass
12261223
else:
12271224
raise Exception("Timeout should have been raised")
1225+
self.assertRaises(Timeout, self.client.join, output, timeout=1)
12281226
channel = output[self.host].channel
12291227
self.client.host_clients[self.host].close_channel(channel)
12301228
self.client.join(output)

0 commit comments

Comments
 (0)