py3: convert arbitrary exception object to byte string more reliably
authorYuya Nishihara <yuya@tcha.org>
Thu, 03 Aug 2017 23:02:32 +0900
changeset 33682 1d5e497c08b3
parent 33681 8626b44516c1
child 33683 7dcb517122f9
py3: convert arbitrary exception object to byte string more reliably Our exception types implement __bytes__(), which should be tried first. Do lossy encoding conversion as a last resort.
mercurial/bundle2.py
mercurial/extensions.py
mercurial/util.py
--- a/mercurial/bundle2.py	Thu Aug 03 20:08:31 2017 -0700
+++ b/mercurial/bundle2.py	Thu Aug 03 23:02:32 2017 +0900
@@ -1045,7 +1045,7 @@
             ui.debug('bundle2-generatorexit\n')
             raise
         except BaseException as exc:
-            bexc = pycompat.bytestr(exc)
+            bexc = util.forcebytestr(exc)
             # backup exception data for later
             ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
                      % bexc)
--- a/mercurial/extensions.py	Thu Aug 03 20:08:31 2017 -0700
+++ b/mercurial/extensions.py	Thu Aug 03 23:02:32 2017 +0900
@@ -19,7 +19,6 @@
 from . import (
     cmdutil,
     configitems,
-    encoding,
     error,
     pycompat,
     util,
@@ -114,16 +113,11 @@
                 mod = _importh(name)
     return mod
 
-def _forbytes(inst):
-    """Portably format an import error into a form suitable for
-    %-formatting into bytestrings."""
-    return encoding.strtolocal(str(inst))
-
 def _reportimporterror(ui, err, failed, next):
     # note: this ui.debug happens before --debug is processed,
     #       Use --config ui.debug=1 to see them.
     ui.debug('could not import %s (%s): trying %s\n'
-             % (failed, _forbytes(err), next))
+             % (failed, util.forcebytestr(err), next))
     if ui.debugflag:
         ui.traceback()
 
@@ -180,7 +174,7 @@
             uisetup(ui)
         except Exception as inst:
             ui.traceback()
-            msg = _forbytes(inst)
+            msg = util.forcebytestr(inst)
             ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg))
             return False
     return True
@@ -197,7 +191,7 @@
                 extsetup() # old extsetup with no ui argument
         except Exception as inst:
             ui.traceback()
-            msg = _forbytes(inst)
+            msg = util.forcebytestr(inst)
             ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg))
             return False
     return True
@@ -215,7 +209,7 @@
         try:
             load(ui, name, path)
         except Exception as inst:
-            msg = _forbytes(inst)
+            msg = util.forcebytestr(inst)
             if path:
                 ui.warn(_("*** failed to import extension %s from %s: %s\n")
                         % (name, path, msg))
--- a/mercurial/util.py	Thu Aug 03 20:08:31 2017 -0700
+++ b/mercurial/util.py	Thu Aug 03 23:02:32 2017 +0900
@@ -2268,6 +2268,15 @@
 def unescapestr(s):
     return codecs.escape_decode(s)[0]
 
+def forcebytestr(obj):
+    """Portably format an arbitrary object (e.g. exception) into a byte
+    string."""
+    try:
+        return pycompat.bytestr(obj)
+    except UnicodeEncodeError:
+        # non-ascii string, may be lossy
+        return pycompat.bytestr(encoding.strtolocal(str(obj)))
+
 def uirepr(s):
     # Avoid double backslash in Windows path repr()
     return repr(s).replace('\\\\', '\\')