From 1368459085cfae7eaac603f58ab855dc8531a291 Mon Sep 17 00:00:00 2001 From: David Lai Date: Sat, 24 Oct 2020 00:41:28 +0800 Subject: [PATCH] better subprocess listening experience bleeding-rez and nerdvegas/rez has a bit different shell spawning setup. This commit tries to makes people in both world have the same subprocess experience when using Allzpark. In addition, since Allzpark has the duty to listen to subprocess and displaying logs continually, this commit adds an error handler in case UnicodeDecodeError happens. --- allzpark/allzparkconfig.py | 45 ++++++++++++++++++++++++++++++++++++++ allzpark/control.py | 9 ++++++++ 2 files changed, 54 insertions(+) diff --git a/allzpark/allzparkconfig.py b/allzpark/allzparkconfig.py index fc15a05..3c6ba82 100644 --- a/allzpark/allzparkconfig.py +++ b/allzpark/allzparkconfig.py @@ -101,3 +101,48 @@ def metadata_from_package(variant): "icon": data.get("icon", ""), "hidden": data.get("hidden", False), }) + + +def subprocess_encoding(): + """Codec that should be used to decode subprocess stdout/stderr + + See https://docs.python.org/3/library/codecs.html#standard-encodings + + Returns: + str: name of codec + + """ + # nerdvegas/rez sets `encoding='utf-8'` when `universal_newlines=True` and + # `encoding` is not in Popen kwarg. + return "utf-8" + + +def unicode_decode_error_handler(): + """Error handler for handling UnicodeDecodeError in subprocess + + See https://docs.python.org/3/library/codecs.html#error-handlers + + Returns: + str: name of registered error handler + + """ + import codecs + import locale + + def decode_with_preferred_encoding(exception): + encoding = locale.getpreferredencoding(do_setlocale=False) + invalid_bytes = exception.object[exception.start:] + + text = invalid_bytes.decode(encoding, + # second fallback + errors="backslashreplace") + + return text, len(exception.object) + + handler_name = "decode_with_preferred_encoding" + try: + codecs.lookup_error(handler_name) + except LookupError: + codecs.register_error(handler_name, decode_with_preferred_encoding) + + return handler_name diff --git a/allzpark/control.py b/allzpark/control.py index c9b33ba..7b73511 100644 --- a/allzpark/control.py +++ b/allzpark/control.py @@ -1,6 +1,7 @@ """Orchestrates view.py and model.py""" import os +import sys import time import errno import shutil @@ -1193,6 +1194,14 @@ def _execute(self): "parent_environ": None, "startupinfo": startupinfo } + if rez.project == "rez": + # bleeding-rez adds `universal_newlines=True` when spawning shell, + # nerdvegas/rez doesn't. + kwargs["universal_newlines"] = True + + if sys.version_info[:2] >= (3, 6): + kwargs["encoding"] = allzparkconfig.subprocess_encoding() + kwargs["errors"] = allzparkconfig.unicode_decode_error_handler() context = self.context