Patchwork py3: convert arbitrary exception object to byte string more reliably

login
register
mail settings
Submitter Yuya Nishihara
Date Aug. 3, 2017, 2:46 p.m.
Message ID <ddca9509675e5e132249.1501771593@mimosa>
Download mbox | patch
Permalink /patch/22659/
State Accepted
Headers show

Comments

Yuya Nishihara - Aug. 3, 2017, 2:46 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1501768952 -32400
#      Thu Aug 03 23:02:32 2017 +0900
# Node ID ddca9509675e5e132249356c17a1311c98d475b8
# Parent  6a620fd39e4cac88e1a46f18aa7b852639e3b729
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.
Augie Fackler - Aug. 4, 2017, 9:15 p.m.
On Thu, Aug 03, 2017 at 11:46:33PM +0900, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1501768952 -32400
> #      Thu Aug 03 23:02:32 2017 +0900
> # Node ID ddca9509675e5e132249356c17a1311c98d475b8
> # Parent  6a620fd39e4cac88e1a46f18aa7b852639e3b729
> py3: convert arbitrary exception object to byte string more reliably

queued, thanks

Patch

diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -1022,7 +1022,7 @@  class bundlepart(object):
             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)
diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -19,7 +19,6 @@  from .i18n import (
 from . import (
     cmdutil,
     configitems,
-    encoding,
     error,
     pycompat,
     util,
@@ -114,16 +113,11 @@  def _importext(name, path=None, reportfu
                 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 @@  def _runuisetup(name, ui):
             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 @@  def _runextsetup(name, ui):
                 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 @@  def loadall(ui, whitelist=None):
         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))
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -2268,6 +2268,15 @@  def escapestr(s):
 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('\\\\', '\\')