[elbe-devel] [PATCH v2 4/5] Python3: harmonize binary/unicode strings

Christian Teklenborg chris at linutronix.de
Fri Dec 13 12:45:18 CET 2019


Python 2 has binary strings as default whereas Python 3 has unicode strings.
This requires changing the string handling at some places to explicitly use one
or the other. This involves opening some files in binary mode and converting
Popen streams to unicode strings.

The binascii.b2a_base64 call returns binary strings on Python 3 even though it
could return unicode strings (base64 is ASCII). So we safely convert it via
decode('ascii').

Signed-off-by: Christian Teklenborg <chris at linutronix.de>
---
 elbepack/daemons/soap/esoap.py |  8 ++++----
 elbepack/kvm.py                | 10 ++++------
 elbepack/log.py                |  5 ++++-
 elbepack/shellhelper.py        |  6 ++++++
 elbepack/soapclient.py         |  4 ++--
 5 files changed, 20 insertions(+), 13 deletions(-)

diff --git a/elbepack/daemons/soap/esoap.py b/elbepack/daemons/soap/esoap.py
index 468df8a5..7d76d6a3 100644
--- a/elbepack/daemons/soap/esoap.py
+++ b/elbepack/daemons/soap/esoap.py
@@ -159,7 +159,7 @@ class ESoap (ServiceBase):
                 self.app.pm.set_current_project_xml(uid, fn)
             return -2
 
-        with open(fn, 'a') as fp:
+        with open(fn, 'ab') as fp:
             fp.write(binascii.a2b_base64(blob))
 
         return part + 1
@@ -264,7 +264,7 @@ class ESoap (ServiceBase):
         cdrom_fname = os.path.join(builddir, "uploaded_cdrom.iso")
 
         # Now append data to cdrom_file
-        fp = open(cdrom_fname, "a")
+        fp = open(cdrom_fname, "ab")
         fp.write(binascii.a2b_base64(data))
         fp.close()
 
@@ -297,7 +297,7 @@ class ESoap (ServiceBase):
         pdebuild_fname = os.path.join(builddir, "current_pdebuild.tar.gz")
 
         # Now write empty File
-        fp = open(pdebuild_fname, "a")
+        fp = open(pdebuild_fname, "ab")
         fp.write(binascii.a2b_base64(data))
         fp.close()
 
@@ -331,7 +331,7 @@ class ESoap (ServiceBase):
         orig_fname = os.path.join(builddir, self.app.pm.get_orig_fname(uid))
 
         # Now append to File
-        fp = open(orig_fname, "a")
+        fp = open(orig_fname, "ab")
         fp.write(binascii.a2b_base64(data))
         fp.close()
 
diff --git a/elbepack/kvm.py b/elbepack/kvm.py
index 62f2ddeb..8ea0e6dd 100644
--- a/elbepack/kvm.py
+++ b/elbepack/kvm.py
@@ -6,7 +6,8 @@
 # SPDX-License-Identifier: GPL-3.0-or-later
 
 import os
-import subprocess
+
+from elbepack.shellhelper import command_out
 
 kvm_exe_list = [
     '/usr/bin/kvm',
@@ -21,11 +22,8 @@ def find_kvm_exe():
     for fname in kvm_exe_list:
         if os.path.isfile(fname) and os.access(fname, os.X_OK):
             # determine kvm version
-            cmd = subprocess.Popen(
-                fname + ' --version',
-                shell=True,
-                stdout=subprocess.PIPE)
-            for line in cmd.stdout:
+            _, stdout = command_out(fname + ' --version')
+            for line in stdout.splitlines():
                 if "version" in line:
                     version = line.split()[3].split('(')[0].strip()
 
diff --git a/elbepack/log.py b/elbepack/log.py
index 43e04985..bffc6c1d 100644
--- a/elbepack/log.py
+++ b/elbepack/log.py
@@ -11,6 +11,7 @@ import select
 import threading
 from contextlib import contextmanager
 
+from io import TextIOWrapper, BytesIO
 
 root = logging.getLogger()
 root.setLevel(logging.DEBUG)
@@ -241,7 +242,7 @@ class AsyncLogging(object):
 
     def run(self):
         alive = True
-        rest = ""
+        rest = bytes()
         while alive:
             events = self.epoll.poll()
             for _, event in events:
@@ -256,6 +257,8 @@ class AsyncLogging(object):
             if not rest:
                 break
 
+        rest = TextIOWrapper(BytesIO(rest), encoding='utf-8', errors='replace').read()
+
         if self.lines:
             self.lines[-1] += rest
             self.block.info("\n".join(self.lines))
diff --git a/elbepack/shellhelper.py b/elbepack/shellhelper.py
index 0895c0b0..c73642c7 100644
--- a/elbepack/shellhelper.py
+++ b/elbepack/shellhelper.py
@@ -12,6 +12,7 @@ from subprocess import Popen, PIPE, STDOUT, call
 
 from elbepack.log import async_logging
 
+from io import TextIOWrapper, BytesIO
 
 log = logging.getLogger("log")
 soap = logging.getLogger("soap")
@@ -54,6 +55,8 @@ def command_out(cmd, stdin=None, output=PIPE, env_add=None):
                   stdout=output, stderr=STDOUT, stdin=PIPE, env=new_env)
         out, _ = p.communicate(input=stdin)
 
+    out = TextIOWrapper(BytesIO(out), encoding='utf-8', errors='replace').read()
+
     return p.returncode, out
 
 
@@ -81,6 +84,9 @@ def command_out_stderr(cmd, stdin=None, env_add=None):
                   stdout=PIPE, stderr=PIPE, stdin=PIPE, env=new_env)
         output, stderr = p.communicate(input=stdin)
 
+    output = TextIOWrapper(BytesIO(output), encoding='utf-8', errors='replace').read()
+    stderr = TextIOWrapper(BytesIO(stderr), encoding='utf-8', errors='replace').read()
+
     return p.returncode, output, stderr
 
 
diff --git a/elbepack/soapclient.py b/elbepack/soapclient.py
index c654e410..cb8dbd8f 100644
--- a/elbepack/soapclient.py
+++ b/elbepack/soapclient.py
@@ -87,7 +87,7 @@ class ElbeSoapClient(object):
         self.service.login(user, passwd)
 
     def download_file(self, builddir, filename, dst_fname):
-        fp = open(dst_fname, "w")
+        fp = open(dst_fname, "wb")
         part = 0
 
         # XXX the retry logic might get removed in the future, if the error
@@ -327,7 +327,7 @@ class SetXmlAction(ClientAction):
         part = 0
         with open(filename, "rb") as fp:
             while True:
-                xml_base64 = binascii.b2a_base64(fp.read(size))
+                xml_base64 = binascii.b2a_base64(fp.read(size)).decode('ascii')
                 # finish upload
                 if len(xml_base64) == 1:
                     part = client.service.upload_file(builddir,
-- 
2.20.1




More information about the elbe-devel mailing list