[Python-modules-commits] [python-spur] 07/13: Add encoding argument

Ruben Undheim rubund-guest at moszumanska.debian.org
Sun Jan 3 10:38:51 UTC 2016


This is an automated email from the git hooks/post-receive script.

rubund-guest pushed a commit to branch master
in repository python-spur.

commit 3712bc82083ca8cec12113a38f1427e5fbb33360
Author: Michael Williamson <mike at zwobble.org>
Date:   Mon Dec 28 11:01:14 2015 +0000

    Add encoding argument
---
 README.rst                | 13 +++++++++----
 spur/io.py                | 26 +++++++++++++++++++++++---
 spur/local.py             |  9 +++++----
 spur/results.py           | 24 ++++++++++++++++--------
 spur/ssh.py               |  6 ++++--
 tests/process_test_set.py | 16 ++++++++++++++++
 6 files changed, 73 insertions(+), 21 deletions(-)

diff --git a/README.rst b/README.rst
index 2b784c6..d358d11 100644
--- a/README.rst
+++ b/README.rst
@@ -107,8 +107,8 @@ Optional arguments:
 Shell interface
 ---------------
 
-run(command, cwd, update\_env, store\_pid, allow\_error, stdout, stderr)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+run(command, cwd, update\_env, store\_pid, allow\_error, stdout, stderr, encoding)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Run a command and wait for it to complete. The command is expected to be
 a list of strings. Returns an instance of ``ExecutionResult``.
@@ -145,12 +145,17 @@ Optional arguments:
 * ``stderr`` -- if not ``None``, anything the command prints to
   standard error during its execution will also be written to
   ``stderr`` using ``stderr.write``.
+* ``encoding`` -- if set, this is used to decode any output.
+  By default, any output is treated as raw bytes.
+  If set, the raw bytes are decoded before writing to
+  the passed ``stdout`` and ``stderr`` arguments (if set)
+  and before setting the output attributes on the result.
 
 ``shell.run(*args, **kwargs)`` should behave similarly to
 ``shell.spawn(*args, **kwargs).wait_for_result()``
 
-spawn(command, cwd, update\_env, store\_pid, allow\_error, stdout, stderr)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+spawn(command, cwd, update\_env, store\_pid, allow\_error, stdout, stderr, encoding)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Behaves the same as ``run`` except that ``spawn`` immediately returns an
 object representing the running process.
diff --git a/spur/io.py b/spur/io.py
index 11f4e93..50e4ff7 100644
--- a/spur/io.py
+++ b/spur/io.py
@@ -3,8 +3,11 @@ import os
 
 
 class IoHandler(object):
-    def __init__(self, channels):
-        self._handlers = list(map(_output_handler, channels))
+    def __init__(self, channels, encoding):
+        self._handlers = [
+            _output_handler(channel, encoding)
+            for channel in channels
+        ]
         
     def wait(self):
         return [handler.wait() for handler in self._handlers]
@@ -17,7 +20,24 @@ class Channel(object):
         self.is_pty = is_pty
 
 
-def _output_handler(channel):
+def _output_handler(channel, encoding):
+    bytes_handler = _bytes_output_handler(channel)
+    if encoding is None:
+        return bytes_handler
+    else:
+        return _EncodedOutputHandler(bytes_handler, encoding)
+
+
+class _EncodedOutputHandler(object):
+    def __init__(self, bytes_handler, encoding):
+        self._bytes_handler = bytes_handler
+        self._encoding = encoding
+    
+    def wait(self):
+        return self._bytes_handler.wait().decode(self._encoding)
+
+
+def _bytes_output_handler(channel):
     if channel.file_out is None and not channel.is_pty:
         return _ReadOutputAtEnd(channel.file_in)
     else:
diff --git a/spur/local.py b/spur/local.py
index 77e840b..c1a7d07 100644
--- a/spur/local.py
+++ b/spur/local.py
@@ -44,6 +44,7 @@ class LocalShell(object):
         allow_error = kwargs.pop("allow_error", False)
         store_pid = kwargs.pop("store_pid", False)
         use_pty = kwargs.pop("use_pty", False)
+        encoding = kwargs.pop("encoding", None)
         if use_pty:
             if pty is None:
                 raise ValueError("use_pty is not supported when the pty module cannot be imported")
@@ -95,10 +96,10 @@ class LocalShell(object):
             process,
             allow_error=allow_error,
             process_stdin=process_stdin,
-            channels=[
+            io_handler=IoHandler([
                 Channel(process_stdout, stdout, is_pty=use_pty),
                 Channel(process_stderr, stderr, is_pty=use_pty),
-            ]
+            ], encoding=encoding)
         )
         if store_pid:
             spur_process.pid = process.pid
@@ -141,13 +142,13 @@ class LocalShell(object):
             
 
 class LocalProcess(object):
-    def __init__(self, subprocess, allow_error, process_stdin, channels):
+    def __init__(self, subprocess, allow_error, process_stdin, io_handler):
         self._subprocess = subprocess
         self._allow_error = allow_error
         self._process_stdin = process_stdin
         self._result = None
             
-        self._io = IoHandler(channels)
+        self._io = io_handler
         
     def is_running(self):
         return self._subprocess.poll() is None
diff --git a/spur/results.py b/spur/results.py
index e3cfd1f..1aaaee9 100644
--- a/spur/results.py
+++ b/spur/results.py
@@ -1,4 +1,6 @@
-import locale
+from __future__ import unicode_literals
+
+import sys
 
 
 def result(return_code, allow_error, output, stderr_output):
@@ -25,17 +27,23 @@ class ExecutionResult(object):
         
 class RunProcessError(RuntimeError):
     def __init__(self, return_code, output, stderr_output):
-        message = "return code: {0}\noutput: {1}\nstderr output: {2}".format(
-            return_code, _bytes_repr(output), _bytes_repr(stderr_output))
+        message = "return code: {0}\noutput:{1}\nstderr output:{2}".format(
+            return_code, _render_output(output), _render_output(stderr_output))
         super(type(self), self).__init__(message)
         self.return_code = return_code
         self.output = output
         self.stderr_output = stderr_output
 
 
-def _bytes_repr(raw_bytes):
-    result =  repr(raw_bytes)
-    if result.startswith("b"):
-        return result
+def _render_output(output):
+    if isinstance(output, unicode):
+        return "\n" + output
     else:
-        return "b" + result
+        result =  repr(output)
+        if result.startswith("b"):
+            return " " + result
+        else:
+            return " b" + result
+
+if sys.version_info[0] >= 3:
+    unicode = str
diff --git a/spur/ssh.py b/spur/ssh.py
index 5fb20f3..38319e5 100644
--- a/spur/ssh.py
+++ b/spur/ssh.py
@@ -163,6 +163,7 @@ class SshShell(object):
         allow_error = kwargs.pop("allow_error", False)
         store_pid = kwargs.pop("store_pid", False)
         use_pty = kwargs.pop("use_pty", False)
+        encoding = kwargs.pop("encoding", None)
         command_in_cwd = self._shell_type.generate_run_command(command, *args, store_pid=store_pid, **kwargs)
         try:
             channel = self._get_ssh_transport().open_session()
@@ -189,6 +190,7 @@ class SshShell(object):
             process_stdout=process_stdout,
             stdout=stdout,
             stderr=stderr,
+            encoding=encoding,
             shell=self,
         )
         if store_pid:
@@ -331,7 +333,7 @@ def escape_sh(value):
 
 
 class SshProcess(object):
-    def __init__(self, channel, allow_error, process_stdout, stdout, stderr, shell):
+    def __init__(self, channel, allow_error, process_stdout, stdout, stderr, encoding, shell):
         self._channel = channel
         self._allow_error = allow_error
         self._stdin = channel.makefile('wb')
@@ -343,7 +345,7 @@ class SshProcess(object):
         self._io = IoHandler([
             Channel(self._stdout, stdout),
             Channel(self._stderr, stderr),
-        ])
+        ], encoding=encoding)
         
     def is_running(self):
         return not self._channel.exit_status_ready()
diff --git a/tests/process_test_set.py b/tests/process_test_set.py
index 56bed8c..9cbb5b1 100644
--- a/tests/process_test_set.py
+++ b/tests/process_test_set.py
@@ -46,6 +46,11 @@ class ProcessTestSet(object):
         assert_equal(b"hello\n", result.stderr_output)
         
     @test
+    def output_bytes_are_decoded_if_encoding_is_set(shell):
+        result = shell.run(["bash", "-c", r'echo -e "\u2603"'], encoding="utf8")
+        assert_equal(_u("☃\n"), result.output)
+        
+    @test
     def cwd_of_run_can_be_set(shell):
         result = shell.run(["pwd"], cwd="/")
         assert_equal(b"/\n", result.output)
@@ -87,6 +92,17 @@ class ProcessTestSet(object):
             )
 
     @test
+    def exception_message_contains_output_as_string_if_encoding_is_set(shell):
+        try:
+            shell.run(["sh", "-c", "echo starting; echo failed! 1>&2; exit 1"], encoding="ascii")
+            assert_true(False)
+        except spur.RunProcessError as error:
+            assert_equal(
+                """return code: 1\noutput:\nstarting\n\nstderr output:\nfailed!\n""",
+                error.args[0]
+            )
+
+    @test
     def exception_message_shows_unicode_bytes(shell):
         try:
             shell.run(["sh", "-c", _u("echo ‽; exit 1")])

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-spur.git



More information about the Python-modules-commits mailing list