changeset 31820:45761ef1bc93

py3: have registrar process docstrings in bytes Mixing bytes and unicode creates a mess. Do things in bytes as possible. New sysbytes() helper only takes care of ASCII characters, but avoids raising nasty unicode exception. This is the same design principle as sysstr().
author Yuya Nishihara <yuya@tcha.org>
date Wed, 05 Apr 2017 00:34:58 +0900
parents 95a67508fd72
children 66a9faadbc83
files hgext/show.py mercurial/pycompat.py mercurial/registrar.py
diffstat 3 files changed, 17 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/show.py	Tue Apr 04 16:49:12 2017 +0200
+++ b/hgext/show.py	Wed Apr 05 00:34:58 2017 +0900
@@ -19,6 +19,7 @@
     cmdutil,
     commands,
     error,
+    pycompat,
     registrar,
 )
 
@@ -133,5 +134,5 @@
 # TODO make this more robust.
 longest = max(map(len, showview._table.keys()))
 for key in sorted(showview._table.keys()):
-    cmdtable['show'][0].__doc__ += ' %s   %s\n' % (
-        key.ljust(longest), showview._table[key]._origdoc)
+    cmdtable['show'][0].__doc__ += pycompat.sysstr(' %s   %s\n' % (
+        key.ljust(longest), showview._table[key]._origdoc))
--- a/mercurial/pycompat.py	Tue Apr 04 16:49:12 2017 +0200
+++ b/mercurial/pycompat.py	Wed Apr 05 00:34:58 2017 +0900
@@ -142,6 +142,14 @@
         """Iterate bytes as if it were a str object of Python 2"""
         return map(bytechr, s)
 
+    def sysbytes(s):
+        """Convert an internal str (e.g. keyword, __doc__) back to bytes
+
+        This never raises UnicodeEncodeError, but only ASCII characters
+        can be round-trip by sysstr(sysbytes(s)).
+        """
+        return s.encode(u'utf-8')
+
     def sysstr(s):
         """Return a keyword str to be passed to Python functions such as
         getattr() and str.encode()
@@ -210,6 +218,7 @@
     bytechr = chr
     bytestr = str
     iterbytestr = iter
+    sysbytes = identity
     sysstr = identity
 
     # Partial backport from os.py in Python 3, which only accepts bytes.
--- a/mercurial/registrar.py	Tue Apr 04 16:49:12 2017 +0200
+++ b/mercurial/registrar.py	Wed Apr 05 00:34:58 2017 +0900
@@ -56,9 +56,9 @@
             raise error.ProgrammingError(msg)
 
         if func.__doc__ and not util.safehasattr(func, '_origdoc'):
-            doc = func.__doc__.strip()
+            doc = pycompat.sysbytes(func.__doc__).strip()
             func._origdoc = doc
-            func.__doc__ = self._formatdoc(decl, doc)
+            func.__doc__ = pycompat.sysstr(self._formatdoc(decl, doc))
 
         self._table[name] = func
         self._extrasetup(name, func, *args, **kwargs)
@@ -127,7 +127,7 @@
     Otherwise, explicit 'revset.loadpredicate()' is needed.
     """
     _getname = _funcregistrarbase._parsefuncdecl
-    _docformat = pycompat.sysstr("``%s``\n    %s")
+    _docformat = "``%s``\n    %s"
 
     def _extrasetup(self, name, func, safe=False, takeorder=False):
         func._safe = safe
@@ -166,7 +166,7 @@
     Otherwise, explicit 'fileset.loadpredicate()' is needed.
     """
     _getname = _funcregistrarbase._parsefuncdecl
-    _docformat = pycompat.sysstr("``%s``\n    %s")
+    _docformat = "``%s``\n    %s"
 
     def _extrasetup(self, name, func, callstatus=False, callexisting=False):
         func._callstatus = callstatus
@@ -175,7 +175,7 @@
 class _templateregistrarbase(_funcregistrarbase):
     """Base of decorator to register functions as template specific one
     """
-    _docformat = pycompat.sysstr(":%s: %s")
+    _docformat = ":%s: %s"
 
 class templatekeyword(_templateregistrarbase):
     """Decorator to register template keyword