Patchwork [2,of,3,RFC] global: u'' prefix some literals to appease module importing

login
register
mail settings
Submitter Gregory Szorc
Date May 16, 2016, 4:02 a.m.
Message ID <effc44c0ec7440fabb13.1463371372@ubuntu-vm-main>
Download mbox | patch
Permalink /patch/15146/
State RFC, archived
Headers show

Comments

Gregory Szorc - May 16, 2016, 4:02 a.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1463371145 25200
#      Sun May 15 20:59:05 2016 -0700
# Node ID effc44c0ec7440fabb134a076e974ca478d89e11
# Parent  7c5d1f8db9618f511f40bc4089145310671ca57b
global: u'' prefix some literals to appease module importing

This demonstrates how global mass rewriting of string literals
to b'' in Python 3 can be too aggressive and result in Python 3
complaining. All these changes (and more) are needed to preserve
literals in Python 3 as the str (not bytes) type.

With this change, running `hg` fails on Python due to encountering
an "iteritems," which of course does not exist.

This change also breaks Python 2, so it isn't fit for checkin.
timeless - May 16, 2016, 3:25 p.m.
I think I sent an environment bytes class for run tests, if we like it, we
could use it generally to address all environment hunks.

That wouldn't handle calls to encode, which might be handled by saying we
only use encoding.encode/decode
On May 16, 2016 12:03 AM, "Gregory Szorc" <gregory.szorc@gmail.com> wrote:

> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@gmail.com>
> # Date 1463371145 25200
> #      Sun May 15 20:59:05 2016 -0700
> # Node ID effc44c0ec7440fabb134a076e974ca478d89e11
> # Parent  7c5d1f8db9618f511f40bc4089145310671ca57b
> global: u'' prefix some literals to appease module importing
>
> This demonstrates how global mass rewriting of string literals
> to b'' in Python 3 can be too aggressive and result in Python 3
> complaining. All these changes (and more) are needed to preserve
> literals in Python 3 as the str (not bytes) type.
>
> With this change, running `hg` fails on Python due to encountering
> an "iteritems," which of course does not exist.
>
> This change also breaks Python 2, so it isn't fit for checkin.
>
> diff --git a/mercurial/demandimport.py b/mercurial/demandimport.py
> --- a/mercurial/demandimport.py
> +++ b/mercurial/demandimport.py
> @@ -262,17 +262,17 @@ ignore = [
>      'distutils.msvc9compiler'
>      ]
>
>  def isenabled():
>      return builtins.__import__ == _demandimport
>
>  def enable():
>      "enable global demand-loading of modules"
> -    if os.environ.get('HGDEMANDIMPORT') != 'disable':
> +    if os.environ.get(u'HGDEMANDIMPORT') != u'disable':
>          builtins.__import__ = _demandimport
>
>  def disable():
>      "disable global demand-loading of modules"
>      builtins.__import__ = _origimport
>
>  @contextmanager
>  def deactivated():
> diff --git a/mercurial/encoding.py b/mercurial/encoding.py
> --- a/mercurial/encoding.py
> +++ b/mercurial/encoding.py
> @@ -18,17 +18,17 @@ from . import (
>  )
>
>  if sys.version_info[0] >= 3:
>      unichr = chr
>
>  # These unicode characters are ignored by HFS+ (Apple Technote 1150,
>  # "Unicode Subtleties"), so we need to ignore them in some places for
>  # sanity.
> -_ignore = [unichr(int(x, 16)).encode("utf-8") for x in
> +_ignore = [unichr(int(x, 16)).encode(u"utf-8") for x in
>             "200c 200d 200e 200f 202a 202b 202c 202d 202e "
>             "206a 206b 206c 206d 206e 206f feff".split()]
>  # verify the next function will work
>  if sys.version_info[0] >= 3:
>      assert set(i[0] for i in _ignore) == set([ord(b'\xe2'), ord(b'\xef')])
>  else:
>      assert set(i[0] for i in _ignore) == set(["\xe2", "\xef"])
>
> @@ -71,23 +71,23 @@ def _getpreferredencoding():
>
>  _encodingfixers = {
>      '646': lambda: 'ascii',
>      'ANSI_X3.4-1968': lambda: 'ascii',
>      'mac-roman': _getpreferredencoding
>  }
>
>  try:
> -    encoding = os.environ.get("HGENCODING")
> +    encoding = os.environ.get(u"HGENCODING")
>      if not encoding:
>          encoding = locale.getpreferredencoding() or 'ascii'
>          encoding = _encodingfixers.get(encoding, lambda: encoding)()
>  except locale.Error:
>      encoding = 'ascii'
> -encodingmode = os.environ.get("HGENCODINGMODE", "strict")
> +encodingmode = os.environ.get(u"HGENCODINGMODE", u"strict")
>  fallbackencoding = 'ISO-8859-1'
>
>  class localstr(str):
>      '''This class allows strings that are unmodified to be
>      round-tripped to the local encoding and back'''
>      def __new__(cls, u, l):
>          s = str.__new__(cls, l)
>          s._utf8 = u
> @@ -175,17 +175,17 @@ def fromlocal(s):
>          return s.decode(encoding, encodingmode).encode("utf-8")
>      except UnicodeDecodeError as inst:
>          sub = s[max(0, inst.start - 10):inst.start + 10]
>          raise error.Abort("decoding near '%s': %s!" % (sub, inst))
>      except LookupError as k:
>          raise error.Abort(k, hint="please check your locale settings")
>
>  # How to treat ambiguous-width characters. Set to 'wide' to treat as wide.
> -wide = (os.environ.get("HGENCODINGAMBIGUOUS", "narrow") == "wide"
> +wide = (os.environ.get(u"HGENCODINGAMBIGUOUS", u"narrow") == u"wide"
>          and "WFA" or "WF")
>
>  def colwidth(s):
>      "Find the column width of a string for display in the local encoding"
>      return ucolwidth(s.decode(encoding, 'replace'))
>
>  def ucolwidth(d):
>      "Find the column width of a Unicode string for display"
> diff --git a/mercurial/i18n.py b/mercurial/i18n.py
> --- a/mercurial/i18n.py
> +++ b/mercurial/i18n.py
> @@ -10,17 +10,17 @@ from __future__ import absolute_import
>  import gettext as gettextmod
>  import locale
>  import os
>  import sys
>
>  from . import encoding
>
>  # modelled after templater.templatepath:
> -if getattr(sys, 'frozen', None) is not None:
> +if getattr(sys, u'frozen', None) is not None:
>      module = sys.executable
>  else:
>      module = __file__
>
>  try:
>      unicode
>  except NameError:
>      unicode = str
> @@ -41,17 +41,17 @@ if (os.name == 'nt'
>          _languages = [locale.windows_locale[langid]]
>      except (ImportError, AttributeError, KeyError):
>          # ctypes not found or unknown langid
>          pass
>
>  _ugettext = None
>
>  def setdatapath(datapath):
> -    localedir = os.path.join(datapath, 'locale')
> +    localedir = os.path.join(datapath, u'locale')
>      t = gettextmod.translation('hg', localedir, _languages, fallback=True)
>      global _ugettext
>      try:
>          _ugettext = t.ugettext
>      except AttributeError:
>          _ugettext = t.gettext
>
>  _msgcache = {}
> @@ -70,34 +70,34 @@ def gettext(message):
>      if message is None or not _ugettext:
>          return message
>
>      if message not in _msgcache:
>          if type(message) is unicode:
>              # goofy unicode docstrings in test
>              paragraphs = message.split(u'\n\n')
>          else:
> -            paragraphs = [p.decode("ascii") for p in
> message.split('\n\n')]
> +            paragraphs = [p.decode(u"ascii") for p in
> message.split('\n\n')]
>          # Be careful not to translate the empty string -- it holds the
>          # meta data of the .po file.
>          u = u'\n\n'.join([p and _ugettext(p) or '' for p in paragraphs])
>          try:
>              # encoding.tolocal cannot be used since it will first try to
>              # decode the Unicode string. Calling u.decode(enc) really
>              # means u.encode(sys.getdefaultencoding()).decode(enc). Since
>              # the Python encoding defaults to 'ascii', this fails if the
>              # translated string use non-ASCII characters.
> -            _msgcache[message] = u.encode(encoding.encoding, "replace")
> +            _msgcache[message] = u.encode(encoding.encoding, u"replace")
>          except LookupError:
>              # An unknown encoding results in a LookupError.
>              _msgcache[message] = message
>      return _msgcache[message]
>
>  def _plain():
> -    if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
> +    if u'HGPLAIN' not in os.environ and u'HGPLAINEXCEPT' not in
> os.environ:
>          return False
> -    exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
> -    return 'i18n' not in exceptions
> +    exceptions = os.environ.get(u'HGPLAINEXCEPT', u'').strip().split(u',')
> +    return u'i18n' not in exceptions
>
>  if _plain():
>      _ = lambda message: message
>  else:
>      _ = gettext
> diff --git a/mercurial/mdiff.py b/mercurial/mdiff.py
> --- a/mercurial/mdiff.py
> +++ b/mercurial/mdiff.py
> @@ -40,27 +40,27 @@ class diffopts(object):
>      noprefix disables the 'a/' and 'b/' prefixes (ignored in plain mode)
>      ignorews ignores all whitespace changes in the diff
>      ignorewsamount ignores changes in the amount of whitespace
>      ignoreblanklines ignores changes whose lines are all blank
>      upgrade generates git diffs to avoid data loss
>      '''
>
>      defaults = {
> -        'context': 3,
> -        'text': False,
> -        'showfunc': False,
> -        'git': False,
> -        'nodates': False,
> -        'nobinary': False,
> -        'noprefix': False,
> -        'ignorews': False,
> -        'ignorewsamount': False,
> -        'ignoreblanklines': False,
> -        'upgrade': False,
> +        u'context': 3,
> +        u'text': False,
> +        u'showfunc': False,
> +        u'git': False,
> +        u'nodates': False,
> +        u'nobinary': False,
> +        u'noprefix': False,
> +        u'ignorews': False,
> +        u'ignorewsamount': False,
> +        u'ignoreblanklines': False,
> +        u'upgrade': False,
>          }
>
>      __slots__ = defaults.keys()
>
>      def __init__(self, **opts):
>          for k in self.__slots__:
>              v = opts.get(k)
>              if v is None:
> diff --git a/mercurial/pure/osutil.py b/mercurial/pure/osutil.py
> --- a/mercurial/pure/osutil.py
> +++ b/mercurial/pure/osutil.py
> @@ -75,43 +75,43 @@ if os.name != 'nt':
>          _msg_iovlen_t = ctypes.c_size_t
>      else:
>          _cmsg_len_t = _socklen_t
>          _msg_controllen_t = _socklen_t
>          _msg_iovlen_t = ctypes.c_int
>
>      class _iovec(ctypes.Structure):
>          _fields_ = [
> -            ('iov_base', ctypes.c_void_p),
> -            ('iov_len', ctypes.c_size_t),
> +            (u'iov_base', ctypes.c_void_p),
> +            (u'iov_len', ctypes.c_size_t),
>          ]
>
>      class _msghdr(ctypes.Structure):
>          _fields_ = [
> -            ('msg_name', ctypes.c_void_p),
> -            ('msg_namelen', _socklen_t),
> -            ('msg_iov', ctypes.POINTER(_iovec)),
> -            ('msg_iovlen', _msg_iovlen_t),
> -            ('msg_control', ctypes.c_void_p),
> -            ('msg_controllen', _msg_controllen_t),
> -            ('msg_flags', ctypes.c_int),
> +            (u'msg_name', ctypes.c_void_p),
> +            (u'msg_namelen', _socklen_t),
> +            (u'msg_iov', ctypes.POINTER(_iovec)),
> +            (u'msg_iovlen', _msg_iovlen_t),
> +            (u'msg_control', ctypes.c_void_p),
> +            (u'msg_controllen', _msg_controllen_t),
> +            (u'msg_flags', ctypes.c_int),
>          ]
>
>      class _cmsghdr(ctypes.Structure):
>          _fields_ = [
> -            ('cmsg_len', _cmsg_len_t),
> -            ('cmsg_level', ctypes.c_int),
> -            ('cmsg_type', ctypes.c_int),
> -            ('cmsg_data', ctypes.c_ubyte * 0),
> +            (u'cmsg_len', _cmsg_len_t),
> +            (u'cmsg_level', ctypes.c_int),
> +            (u'cmsg_type', ctypes.c_int),
> +            (u'cmsg_data', ctypes.c_ubyte * 0),
>          ]
>
> -    _libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
> -    _recvmsg = getattr(_libc, 'recvmsg', None)
> +    _libc = ctypes.CDLL(ctypes.util.find_library(u'c'), use_errno=True)
> +    _recvmsg = getattr(_libc, u'recvmsg', None)
>      if _recvmsg:
> -        _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long)
> +        _recvmsg.restype = getattr(ctypes, u'c_ssize_t', ctypes.c_long)
>          _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr),
>                               ctypes.c_int)
>      else:
>          # recvmsg isn't always provided by libc; such systems are
> unsupported
>          def _recvmsg(sockfd, msg, flags):
>              raise NotImplementedError('unsupported platform')
>
>      def _CMSG_FIRSTHDR(msgh):
> diff --git a/mercurial/pycompat.py b/mercurial/pycompat.py
> --- a/mercurial/pycompat.py
> +++ b/mercurial/pycompat.py
> @@ -29,17 +29,17 @@ class _pycompatstub(object):
>      pass
>
>  def _alias(alias, origin, items):
>      """ populate a _pycompatstub
>
>      copies items from origin to alias
>      """
>      def hgcase(item):
> -        return item.replace('_', '').lower()
> +        return item.replace(u'_', u'').lower()
>      for item in items:
>          try:
>              setattr(alias, hgcase(item), getattr(origin, item))
>          except AttributeError:
>              pass
>
>  urlreq = _pycompatstub()
>  urlerr = _pycompatstub()
> @@ -80,44 +80,44 @@ try:
>      _alias(urlerr, urllib2, (
>          "HTTPError",
>          "URLError",
>      ))
>
>  except ImportError:
>      import urllib.request
>      _alias(urlreq, urllib.request, (
> -        "AbstractHTTPHandler",
> -        "addclosehook",
> -        "addinfourl",
> -        "BaseHandler",
> -        "build_opener",
> -        "FileHandler",
> -        "FTPHandler",
> -        "ftpwrapper",
> -        "HTTPHandler",
> -        "HTTPSHandler",
> -        "install_opener",
> -        "pathname2url",
> -        "HTTPBasicAuthHandler",
> -        "HTTPDigestAuthHandler",
> -        "ProxyHandler",
> -        "quote",
> -        "Request",
> -        "splitattr",
> -        "splitpasswd",
> -        "splitport",
> -        "splituser",
> -        "unquote",
> -        "url2pathname",
> -        "urlopen",
> +        u"AbstractHTTPHandler",
> +        u"addclosehook",
> +        u"addinfourl",
> +        u"BaseHandler",
> +        u"build_opener",
> +        u"FileHandler",
> +        u"FTPHandler",
> +        u"ftpwrapper",
> +        u"HTTPHandler",
> +        u"HTTPSHandler",
> +        u"install_opener",
> +        u"pathname2url",
> +        u"HTTPBasicAuthHandler",
> +        u"HTTPDigestAuthHandler",
> +        u"ProxyHandler",
> +        u"quote",
> +        u"Request",
> +        u"splitattr",
> +        u"splitpasswd",
> +        u"splitport",
> +        u"splituser",
> +        u"unquote",
> +        u"url2pathname",
> +        u"urlopen",
>      ))
>      import urllib.error
>      _alias(urlerr, urllib.error, (
> -        "HTTPError",
> -        "URLError",
> +        u"HTTPError",
> +        u"URLError",
>      ))
>
>  try:
>      xrange
>  except NameError:
>      import builtins
>      builtins.xrange = range
> diff --git a/mercurial/registrar.py b/mercurial/registrar.py
> --- a/mercurial/registrar.py
> +++ b/mercurial/registrar.py
> @@ -44,17 +44,17 @@ class _funcregistrarbase(object):
>              self._table = table
>
>      def __call__(self, decl, *args, **kwargs):
>          return lambda func: self._doregister(func, decl, *args, **kwargs)
>
>      def _doregister(self, func, decl, *args, **kwargs):
>          name = self._getname(decl)
>
> -        if func.__doc__ and not util.safehasattr(func, '_origdoc'):
> +        if func.__doc__ and not util.safehasattr(func, u'_origdoc'):
>              doc = func.__doc__.strip()
>              func._origdoc = doc
>              func.__doc__ = self._formatdoc(decl, doc)
>
>          self._table[name] = func
>          self._extrasetup(name, func, *args, **kwargs)
>
>          return func
> @@ -78,17 +78,19 @@ class _funcregistrarbase(object):
>
>      _docformat = None
>
>      def _formatdoc(self, decl, doc):
>          """Return formatted document of the registered function for help
>
>          'doc' is '__doc__.strip()' of the registered function.
>          """
> -        return self._docformat % (decl, doc)
> +        # docstrings are using the source file encoding, which should be
> +        # utf-8.
> +        return self._docformat % (decl, doc.encode(u'utf-8'))
>
>      def _extrasetup(self, name, func):
>          """Execute exra setup for registered function, if needed
>          """
>          pass
>
>  class revsetpredicate(_funcregistrarbase):
>      """Decorator to register revset predicate
> diff --git a/mercurial/revset.py b/mercurial/revset.py
> --- a/mercurial/revset.py
> +++ b/mercurial/revset.py
> @@ -171,21 +171,21 @@ elements = {
>      "string": (0, "string", None, None, None),
>      "end": (0, None, None, None, None),
>  }
>
>  keywords = set(['and', 'or', 'not'])
>
>  # default set of valid characters for the initial letter of symbols
>  _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
> -                      if c.isalnum() or c in '._@' or ord(c) > 127)
> +                      if c.isalnum() or c in u'._@' or ord(c) > 127)
>
>  # default set of valid characters for non-initial letters of symbols
>  _symletters = set(c for c in  [chr(i) for i in xrange(256)]
> -                  if c.isalnum() or c in '-._/@' or ord(c) > 127)
> +                  if c.isalnum() or c in u'-._/@' or ord(c) > 127)
>
>  def tokenize(program, lookup=None, syminitletters=None, symletters=None):
>      '''
>      Parse a revset statement into a stream of tokens
>
>      ``syminitletters`` is the set of valid characters for the initial
>      letter of symbols.
>
> @@ -2215,17 +2215,17 @@ def _optimize(x, small):
>
>  def optimize(tree):
>      _weight, newtree = _optimize(tree, small=True)
>      return newtree
>
>  # the set of valid characters for the initial letter of symbols in
>  # alias declarations and definitions
>  _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
> -                           if c.isalnum() or c in '._@$' or ord(c) > 127)
> +                           if c.isalnum() or c in u'._@$' or ord(c) >
> 127)
>
>  def _parsewith(spec, lookup=None, syminitletters=None):
>      """Generate a parse tree of given spec with given tokenizing options
>
>      >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
>      ('func', ('symbol', 'foo'), ('symbol', '$1'))
>      >>> _parsewith('$1')
>      Traceback (most recent call last):
> diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py
> --- a/mercurial/sslutil.py
> +++ b/mercurial/sslutil.py
> @@ -22,31 +22,31 @@ from . import (
>  # Python 2.7.9+ overhauled the built-in SSL/TLS features of Python. It
> added
>  # support for TLS 1.1, TLS 1.2, SNI, system CA stores, etc. These
> features are
>  # all exposed via the "ssl" module.
>  #
>  # Depending on the version of Python being used, SSL/TLS support is either
>  # modern/secure or legacy/insecure. Many operations in this module have
>  # separate code paths depending on support in Python.
>
> -hassni = getattr(ssl, 'HAS_SNI', False)
> +hassni = getattr(ssl, u'HAS_SNI', False)
>
>  try:
>      OP_NO_SSLv2 = ssl.OP_NO_SSLv2
>      OP_NO_SSLv3 = ssl.OP_NO_SSLv3
>  except AttributeError:
>      OP_NO_SSLv2 = 0x1000000
>      OP_NO_SSLv3 = 0x2000000
>
>  try:
>      # ssl.SSLContext was added in 2.7.9 and presence indicates modern
>      # SSL/TLS features are available.
>      SSLContext = ssl.SSLContext
>      modernssl = True
> -    _canloaddefaultcerts = util.safehasattr(SSLContext,
> 'load_default_certs')
> +    _canloaddefaultcerts = util.safehasattr(SSLContext,
> u'load_default_certs')
>  except AttributeError:
>      modernssl = False
>      _canloaddefaultcerts = False
>
>      # We implement SSLContext using the interface from the standard
> library.
>      class SSLContext(object):
>          # ssl.wrap_socket gained the "ciphers" named argument in 2.7.
>          _supportsciphers = sys.version_info >= (2, 7)
> diff --git a/mercurial/util.py b/mercurial/util.py
> --- a/mercurial/util.py
> +++ b/mercurial/util.py
> @@ -41,22 +41,22 @@ from . import (
>      error,
>      i18n,
>      osutil,
>      parsers,
>      pycompat,
>  )
>
>  for attr in (
> -    'empty',
> -    'queue',
> -    'urlerr',
> +    u'empty',
> +    u'queue',
> +    u'urlerr',
>      # we do import urlreq, but we do it outside the loop
>      #'urlreq',
> -    'stringio',
> +    u'stringio',
>  ):
>      globals()[attr] = getattr(pycompat, attr)
>
>  # This line is to make pyflakes happy:
>  urlreq = pycompat.urlreq
>
>  if os.name == 'nt':
>      from . import windows as platform
> @@ -108,17 +108,17 @@ samefile = platform.samefile
>  samestat = platform.samestat
>  setbinary = platform.setbinary
>  setflags = platform.setflags
>  setsignalhandler = platform.setsignalhandler
>  shellquote = platform.shellquote
>  spawndetached = platform.spawndetached
>  split = platform.split
>  sshargs = platform.sshargs
> -statfiles = getattr(osutil, 'statfiles', platform.statfiles)
> +statfiles = getattr(osutil, u'statfiles', platform.statfiles)
>  statisexec = platform.statisexec
>  statislink = platform.statislink
>  termwidth = platform.termwidth
>  testpid = platform.testpid
>  umask = platform.umask
>  unlink = platform.unlink
>  unlinkpath = platform.unlinkpath
>  username = platform.username
> @@ -525,17 +525,17 @@ class sortdict(dict):
>          dict.__setitem__(self, key, val)
>
>  class _lrucachenode(object):
>      """A node in a doubly linked list.
>
>      Holds a reference to nodes on either side as well as a key-value
>      pair for the dictionary entry.
>      """
> -    __slots__ = ('next', 'prev', 'key', 'value')
> +    __slots__ = (u'next', u'prev', u'key', u'value')
>
>      def __init__(self):
>          self.next = None
>          self.prev = None
>
>          self.key = _notset
>          self.value = None
>
> @@ -887,22 +887,22 @@ def pathto(root, n1, n2):
>      return os.sep.join((['..'] * len(a)) + b) or '.'
>
>  def mainfrozen():
>      """return True if we are a frozen executable.
>
>      The code supports py2exe (most common, Windows only) and tools/freeze
>      (portable, not much used).
>      """
> -    return (safehasattr(sys, "frozen") or # new py2exe
> -            safehasattr(sys, "importers") or # old py2exe
> -            imp.is_frozen("__main__")) # tools/freeze
> +    return (safehasattr(sys, u"frozen") or # new py2exe
> +            safehasattr(sys, u"importers") or # old py2exe
> +            imp.is_frozen(u"__main__")) # tools/freeze
>
>  # the location of data files matching the source code
> -if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
> +if mainfrozen() and getattr(sys, u'frozen', None) != u'macosx_app':
>      # executable version (py2exe) doesn't support __file__
>      datapath = os.path.dirname(sys.executable)
>  else:
>      datapath = os.path.dirname(__file__)
>
>  i18n.setdatapath(datapath)
>
>  _hgexecutable = None
> @@ -2135,17 +2135,17 @@ def getport(port):
>
>  def parsebool(s):
>      """Parse s into a boolean.
>
>      If s is not a valid boolean, returns None.
>      """
>      return _booleans.get(s.lower(), None)
>
> -_hexdig = '0123456789ABCDEFabcdef'
> +_hexdig = u'0123456789ABCDEFabcdef'
>  _hextochr = dict((a + b, chr(int(a + b, 16)))
>                   for a in _hexdig for b in _hexdig)
>
>  def _urlunquote(s):
>      """Decode HTTP/HTML % encoding.
>
>      >>> _urlunquote('abc%20def')
>      'abc def'
> @@ -2628,17 +2628,17 @@ class dirs(object):
>              del dirs[base]
>
>      def __iter__(self):
>          return self._dirs.iterkeys()
>
>      def __contains__(self, d):
>          return d in self._dirs
>
> -if safehasattr(parsers, 'dirs'):
> +if safehasattr(parsers, u'dirs'):
>      dirs = parsers.dirs
>
>  def finddirs(path):
>      pos = path.rfind('/')
>      while pos != -1:
>          yield path[:pos]
>          pos = path.rfind('/', 0, pos)
>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
>
Gregory Szorc - May 16, 2016, 3:47 p.m.
> On May 16, 2016, at 08:25, timeless <timeless@gmail.com> wrote:
> 
> I think I sent an environment bytes class for run tests, if we like it, we could use it generally to address all environment hunks.
> 
> That wouldn't handle calls to encode, which might be handled by saying we only use encoding.encode/decode
> 
The u'' prefixing in os.environ in this patch is most certainly wrong. We very well might need your environment bytes class.

>> On May 16, 2016 12:03 AM, "Gregory Szorc" <gregory.szorc@gmail.com> wrote:
>> # HG changeset patch
>> # User Gregory Szorc <gregory.szorc@gmail.com>
>> # Date 1463371145 25200
>> #      Sun May 15 20:59:05 2016 -0700
>> # Node ID effc44c0ec7440fabb134a076e974ca478d89e11
>> # Parent  7c5d1f8db9618f511f40bc4089145310671ca57b
>> global: u'' prefix some literals to appease module importing
>> 
>> This demonstrates how global mass rewriting of string literals
>> to b'' in Python 3 can be too aggressive and result in Python 3
>> complaining. All these changes (and more) are needed to preserve
>> literals in Python 3 as the str (not bytes) type.
>> 
>> With this change, running `hg` fails on Python due to encountering
>> an "iteritems," which of course does not exist.
>> 
>> This change also breaks Python 2, so it isn't fit for checkin.
>> 
>> diff --git a/mercurial/demandimport.py b/mercurial/demandimport.py
>> --- a/mercurial/demandimport.py
>> +++ b/mercurial/demandimport.py
>> @@ -262,17 +262,17 @@ ignore = [
>>      'distutils.msvc9compiler'
>>      ]
>> 
>>  def isenabled():
>>      return builtins.__import__ == _demandimport
>> 
>>  def enable():
>>      "enable global demand-loading of modules"
>> -    if os.environ.get('HGDEMANDIMPORT') != 'disable':
>> +    if os.environ.get(u'HGDEMANDIMPORT') != u'disable':
>>          builtins.__import__ = _demandimport
>> 
>>  def disable():
>>      "disable global demand-loading of modules"
>>      builtins.__import__ = _origimport
>> 
>>  @contextmanager
>>  def deactivated():
>> diff --git a/mercurial/encoding.py b/mercurial/encoding.py
>> --- a/mercurial/encoding.py
>> +++ b/mercurial/encoding.py
>> @@ -18,17 +18,17 @@ from . import (
>>  )
>> 
>>  if sys.version_info[0] >= 3:
>>      unichr = chr
>> 
>>  # These unicode characters are ignored by HFS+ (Apple Technote 1150,
>>  # "Unicode Subtleties"), so we need to ignore them in some places for
>>  # sanity.
>> -_ignore = [unichr(int(x, 16)).encode("utf-8") for x in
>> +_ignore = [unichr(int(x, 16)).encode(u"utf-8") for x in
>>             "200c 200d 200e 200f 202a 202b 202c 202d 202e "
>>             "206a 206b 206c 206d 206e 206f feff".split()]
>>  # verify the next function will work
>>  if sys.version_info[0] >= 3:
>>      assert set(i[0] for i in _ignore) == set([ord(b'\xe2'), ord(b'\xef')])
>>  else:
>>      assert set(i[0] for i in _ignore) == set(["\xe2", "\xef"])
>> 
>> @@ -71,23 +71,23 @@ def _getpreferredencoding():
>> 
>>  _encodingfixers = {
>>      '646': lambda: 'ascii',
>>      'ANSI_X3.4-1968': lambda: 'ascii',
>>      'mac-roman': _getpreferredencoding
>>  }
>> 
>>  try:
>> -    encoding = os.environ.get("HGENCODING")
>> +    encoding = os.environ.get(u"HGENCODING")
>>      if not encoding:
>>          encoding = locale.getpreferredencoding() or 'ascii'
>>          encoding = _encodingfixers.get(encoding, lambda: encoding)()
>>  except locale.Error:
>>      encoding = 'ascii'
>> -encodingmode = os.environ.get("HGENCODINGMODE", "strict")
>> +encodingmode = os.environ.get(u"HGENCODINGMODE", u"strict")
>>  fallbackencoding = 'ISO-8859-1'
>> 
>>  class localstr(str):
>>      '''This class allows strings that are unmodified to be
>>      round-tripped to the local encoding and back'''
>>      def __new__(cls, u, l):
>>          s = str.__new__(cls, l)
>>          s._utf8 = u
>> @@ -175,17 +175,17 @@ def fromlocal(s):
>>          return s.decode(encoding, encodingmode).encode("utf-8")
>>      except UnicodeDecodeError as inst:
>>          sub = s[max(0, inst.start - 10):inst.start + 10]
>>          raise error.Abort("decoding near '%s': %s!" % (sub, inst))
>>      except LookupError as k:
>>          raise error.Abort(k, hint="please check your locale settings")
>> 
>>  # How to treat ambiguous-width characters. Set to 'wide' to treat as wide.
>> -wide = (os.environ.get("HGENCODINGAMBIGUOUS", "narrow") == "wide"
>> +wide = (os.environ.get(u"HGENCODINGAMBIGUOUS", u"narrow") == u"wide"
>>          and "WFA" or "WF")
>> 
>>  def colwidth(s):
>>      "Find the column width of a string for display in the local encoding"
>>      return ucolwidth(s.decode(encoding, 'replace'))
>> 
>>  def ucolwidth(d):
>>      "Find the column width of a Unicode string for display"
>> diff --git a/mercurial/i18n.py b/mercurial/i18n.py
>> --- a/mercurial/i18n.py
>> +++ b/mercurial/i18n.py
>> @@ -10,17 +10,17 @@ from __future__ import absolute_import
>>  import gettext as gettextmod
>>  import locale
>>  import os
>>  import sys
>> 
>>  from . import encoding
>> 
>>  # modelled after templater.templatepath:
>> -if getattr(sys, 'frozen', None) is not None:
>> +if getattr(sys, u'frozen', None) is not None:
>>      module = sys.executable
>>  else:
>>      module = __file__
>> 
>>  try:
>>      unicode
>>  except NameError:
>>      unicode = str
>> @@ -41,17 +41,17 @@ if (os.name == 'nt'
>>          _languages = [locale.windows_locale[langid]]
>>      except (ImportError, AttributeError, KeyError):
>>          # ctypes not found or unknown langid
>>          pass
>> 
>>  _ugettext = None
>> 
>>  def setdatapath(datapath):
>> -    localedir = os.path.join(datapath, 'locale')
>> +    localedir = os.path.join(datapath, u'locale')
>>      t = gettextmod.translation('hg', localedir, _languages, fallback=True)
>>      global _ugettext
>>      try:
>>          _ugettext = t.ugettext
>>      except AttributeError:
>>          _ugettext = t.gettext
>> 
>>  _msgcache = {}
>> @@ -70,34 +70,34 @@ def gettext(message):
>>      if message is None or not _ugettext:
>>          return message
>> 
>>      if message not in _msgcache:
>>          if type(message) is unicode:
>>              # goofy unicode docstrings in test
>>              paragraphs = message.split(u'\n\n')
>>          else:
>> -            paragraphs = [p.decode("ascii") for p in message.split('\n\n')]
>> +            paragraphs = [p.decode(u"ascii") for p in message.split('\n\n')]
>>          # Be careful not to translate the empty string -- it holds the
>>          # meta data of the .po file.
>>          u = u'\n\n'.join([p and _ugettext(p) or '' for p in paragraphs])
>>          try:
>>              # encoding.tolocal cannot be used since it will first try to
>>              # decode the Unicode string. Calling u.decode(enc) really
>>              # means u.encode(sys.getdefaultencoding()).decode(enc). Since
>>              # the Python encoding defaults to 'ascii', this fails if the
>>              # translated string use non-ASCII characters.
>> -            _msgcache[message] = u.encode(encoding.encoding, "replace")
>> +            _msgcache[message] = u.encode(encoding.encoding, u"replace")
>>          except LookupError:
>>              # An unknown encoding results in a LookupError.
>>              _msgcache[message] = message
>>      return _msgcache[message]
>> 
>>  def _plain():
>> -    if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
>> +    if u'HGPLAIN' not in os.environ and u'HGPLAINEXCEPT' not in os.environ:
>>          return False
>> -    exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
>> -    return 'i18n' not in exceptions
>> +    exceptions = os.environ.get(u'HGPLAINEXCEPT', u'').strip().split(u',')
>> +    return u'i18n' not in exceptions
>> 
>>  if _plain():
>>      _ = lambda message: message
>>  else:
>>      _ = gettext
>> diff --git a/mercurial/mdiff.py b/mercurial/mdiff.py
>> --- a/mercurial/mdiff.py
>> +++ b/mercurial/mdiff.py
>> @@ -40,27 +40,27 @@ class diffopts(object):
>>      noprefix disables the 'a/' and 'b/' prefixes (ignored in plain mode)
>>      ignorews ignores all whitespace changes in the diff
>>      ignorewsamount ignores changes in the amount of whitespace
>>      ignoreblanklines ignores changes whose lines are all blank
>>      upgrade generates git diffs to avoid data loss
>>      '''
>> 
>>      defaults = {
>> -        'context': 3,
>> -        'text': False,
>> -        'showfunc': False,
>> -        'git': False,
>> -        'nodates': False,
>> -        'nobinary': False,
>> -        'noprefix': False,
>> -        'ignorews': False,
>> -        'ignorewsamount': False,
>> -        'ignoreblanklines': False,
>> -        'upgrade': False,
>> +        u'context': 3,
>> +        u'text': False,
>> +        u'showfunc': False,
>> +        u'git': False,
>> +        u'nodates': False,
>> +        u'nobinary': False,
>> +        u'noprefix': False,
>> +        u'ignorews': False,
>> +        u'ignorewsamount': False,
>> +        u'ignoreblanklines': False,
>> +        u'upgrade': False,
>>          }
>> 
>>      __slots__ = defaults.keys()
>> 
>>      def __init__(self, **opts):
>>          for k in self.__slots__:
>>              v = opts.get(k)
>>              if v is None:
>> diff --git a/mercurial/pure/osutil.py b/mercurial/pure/osutil.py
>> --- a/mercurial/pure/osutil.py
>> +++ b/mercurial/pure/osutil.py
>> @@ -75,43 +75,43 @@ if os.name != 'nt':
>>          _msg_iovlen_t = ctypes.c_size_t
>>      else:
>>          _cmsg_len_t = _socklen_t
>>          _msg_controllen_t = _socklen_t
>>          _msg_iovlen_t = ctypes.c_int
>> 
>>      class _iovec(ctypes.Structure):
>>          _fields_ = [
>> -            ('iov_base', ctypes.c_void_p),
>> -            ('iov_len', ctypes.c_size_t),
>> +            (u'iov_base', ctypes.c_void_p),
>> +            (u'iov_len', ctypes.c_size_t),
>>          ]
>> 
>>      class _msghdr(ctypes.Structure):
>>          _fields_ = [
>> -            ('msg_name', ctypes.c_void_p),
>> -            ('msg_namelen', _socklen_t),
>> -            ('msg_iov', ctypes.POINTER(_iovec)),
>> -            ('msg_iovlen', _msg_iovlen_t),
>> -            ('msg_control', ctypes.c_void_p),
>> -            ('msg_controllen', _msg_controllen_t),
>> -            ('msg_flags', ctypes.c_int),
>> +            (u'msg_name', ctypes.c_void_p),
>> +            (u'msg_namelen', _socklen_t),
>> +            (u'msg_iov', ctypes.POINTER(_iovec)),
>> +            (u'msg_iovlen', _msg_iovlen_t),
>> +            (u'msg_control', ctypes.c_void_p),
>> +            (u'msg_controllen', _msg_controllen_t),
>> +            (u'msg_flags', ctypes.c_int),
>>          ]
>> 
>>      class _cmsghdr(ctypes.Structure):
>>          _fields_ = [
>> -            ('cmsg_len', _cmsg_len_t),
>> -            ('cmsg_level', ctypes.c_int),
>> -            ('cmsg_type', ctypes.c_int),
>> -            ('cmsg_data', ctypes.c_ubyte * 0),
>> +            (u'cmsg_len', _cmsg_len_t),
>> +            (u'cmsg_level', ctypes.c_int),
>> +            (u'cmsg_type', ctypes.c_int),
>> +            (u'cmsg_data', ctypes.c_ubyte * 0),
>>          ]
>> 
>> -    _libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
>> -    _recvmsg = getattr(_libc, 'recvmsg', None)
>> +    _libc = ctypes.CDLL(ctypes.util.find_library(u'c'), use_errno=True)
>> +    _recvmsg = getattr(_libc, u'recvmsg', None)
>>      if _recvmsg:
>> -        _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long)
>> +        _recvmsg.restype = getattr(ctypes, u'c_ssize_t', ctypes.c_long)
>>          _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr),
>>                               ctypes.c_int)
>>      else:
>>          # recvmsg isn't always provided by libc; such systems are unsupported
>>          def _recvmsg(sockfd, msg, flags):
>>              raise NotImplementedError('unsupported platform')
>> 
>>      def _CMSG_FIRSTHDR(msgh):
>> diff --git a/mercurial/pycompat.py b/mercurial/pycompat.py
>> --- a/mercurial/pycompat.py
>> +++ b/mercurial/pycompat.py
>> @@ -29,17 +29,17 @@ class _pycompatstub(object):
>>      pass
>> 
>>  def _alias(alias, origin, items):
>>      """ populate a _pycompatstub
>> 
>>      copies items from origin to alias
>>      """
>>      def hgcase(item):
>> -        return item.replace('_', '').lower()
>> +        return item.replace(u'_', u'').lower()
>>      for item in items:
>>          try:
>>              setattr(alias, hgcase(item), getattr(origin, item))
>>          except AttributeError:
>>              pass
>> 
>>  urlreq = _pycompatstub()
>>  urlerr = _pycompatstub()
>> @@ -80,44 +80,44 @@ try:
>>      _alias(urlerr, urllib2, (
>>          "HTTPError",
>>          "URLError",
>>      ))
>> 
>>  except ImportError:
>>      import urllib.request
>>      _alias(urlreq, urllib.request, (
>> -        "AbstractHTTPHandler",
>> -        "addclosehook",
>> -        "addinfourl",
>> -        "BaseHandler",
>> -        "build_opener",
>> -        "FileHandler",
>> -        "FTPHandler",
>> -        "ftpwrapper",
>> -        "HTTPHandler",
>> -        "HTTPSHandler",
>> -        "install_opener",
>> -        "pathname2url",
>> -        "HTTPBasicAuthHandler",
>> -        "HTTPDigestAuthHandler",
>> -        "ProxyHandler",
>> -        "quote",
>> -        "Request",
>> -        "splitattr",
>> -        "splitpasswd",
>> -        "splitport",
>> -        "splituser",
>> -        "unquote",
>> -        "url2pathname",
>> -        "urlopen",
>> +        u"AbstractHTTPHandler",
>> +        u"addclosehook",
>> +        u"addinfourl",
>> +        u"BaseHandler",
>> +        u"build_opener",
>> +        u"FileHandler",
>> +        u"FTPHandler",
>> +        u"ftpwrapper",
>> +        u"HTTPHandler",
>> +        u"HTTPSHandler",
>> +        u"install_opener",
>> +        u"pathname2url",
>> +        u"HTTPBasicAuthHandler",
>> +        u"HTTPDigestAuthHandler",
>> +        u"ProxyHandler",
>> +        u"quote",
>> +        u"Request",
>> +        u"splitattr",
>> +        u"splitpasswd",
>> +        u"splitport",
>> +        u"splituser",
>> +        u"unquote",
>> +        u"url2pathname",
>> +        u"urlopen",
>>      ))
>>      import urllib.error
>>      _alias(urlerr, urllib.error, (
>> -        "HTTPError",
>> -        "URLError",
>> +        u"HTTPError",
>> +        u"URLError",
>>      ))
>> 
>>  try:
>>      xrange
>>  except NameError:
>>      import builtins
>>      builtins.xrange = range
>> diff --git a/mercurial/registrar.py b/mercurial/registrar.py
>> --- a/mercurial/registrar.py
>> +++ b/mercurial/registrar.py
>> @@ -44,17 +44,17 @@ class _funcregistrarbase(object):
>>              self._table = table
>> 
>>      def __call__(self, decl, *args, **kwargs):
>>          return lambda func: self._doregister(func, decl, *args, **kwargs)
>> 
>>      def _doregister(self, func, decl, *args, **kwargs):
>>          name = self._getname(decl)
>> 
>> -        if func.__doc__ and not util.safehasattr(func, '_origdoc'):
>> +        if func.__doc__ and not util.safehasattr(func, u'_origdoc'):
>>              doc = func.__doc__.strip()
>>              func._origdoc = doc
>>              func.__doc__ = self._formatdoc(decl, doc)
>> 
>>          self._table[name] = func
>>          self._extrasetup(name, func, *args, **kwargs)
>> 
>>          return func
>> @@ -78,17 +78,19 @@ class _funcregistrarbase(object):
>> 
>>      _docformat = None
>> 
>>      def _formatdoc(self, decl, doc):
>>          """Return formatted document of the registered function for help
>> 
>>          'doc' is '__doc__.strip()' of the registered function.
>>          """
>> -        return self._docformat % (decl, doc)
>> +        # docstrings are using the source file encoding, which should be
>> +        # utf-8.
>> +        return self._docformat % (decl, doc.encode(u'utf-8'))
>> 
>>      def _extrasetup(self, name, func):
>>          """Execute exra setup for registered function, if needed
>>          """
>>          pass
>> 
>>  class revsetpredicate(_funcregistrarbase):
>>      """Decorator to register revset predicate
>> diff --git a/mercurial/revset.py b/mercurial/revset.py
>> --- a/mercurial/revset.py
>> +++ b/mercurial/revset.py
>> @@ -171,21 +171,21 @@ elements = {
>>      "string": (0, "string", None, None, None),
>>      "end": (0, None, None, None, None),
>>  }
>> 
>>  keywords = set(['and', 'or', 'not'])
>> 
>>  # default set of valid characters for the initial letter of symbols
>>  _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
>> -                      if c.isalnum() or c in '._@' or ord(c) > 127)
>> +                      if c.isalnum() or c in u'._@' or ord(c) > 127)
>> 
>>  # default set of valid characters for non-initial letters of symbols
>>  _symletters = set(c for c in  [chr(i) for i in xrange(256)]
>> -                  if c.isalnum() or c in '-._/@' or ord(c) > 127)
>> +                  if c.isalnum() or c in u'-._/@' or ord(c) > 127)
>> 
>>  def tokenize(program, lookup=None, syminitletters=None, symletters=None):
>>      '''
>>      Parse a revset statement into a stream of tokens
>> 
>>      ``syminitletters`` is the set of valid characters for the initial
>>      letter of symbols.
>> 
>> @@ -2215,17 +2215,17 @@ def _optimize(x, small):
>> 
>>  def optimize(tree):
>>      _weight, newtree = _optimize(tree, small=True)
>>      return newtree
>> 
>>  # the set of valid characters for the initial letter of symbols in
>>  # alias declarations and definitions
>>  _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
>> -                           if c.isalnum() or c in '._@$' or ord(c) > 127)
>> +                           if c.isalnum() or c in u'._@$' or ord(c) > 127)
>> 
>>  def _parsewith(spec, lookup=None, syminitletters=None):
>>      """Generate a parse tree of given spec with given tokenizing options
>> 
>>      >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
>>      ('func', ('symbol', 'foo'), ('symbol', '$1'))
>>      >>> _parsewith('$1')
>>      Traceback (most recent call last):
>> diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py
>> --- a/mercurial/sslutil.py
>> +++ b/mercurial/sslutil.py
>> @@ -22,31 +22,31 @@ from . import (
>>  # Python 2.7.9+ overhauled the built-in SSL/TLS features of Python. It added
>>  # support for TLS 1.1, TLS 1.2, SNI, system CA stores, etc. These features are
>>  # all exposed via the "ssl" module.
>>  #
>>  # Depending on the version of Python being used, SSL/TLS support is either
>>  # modern/secure or legacy/insecure. Many operations in this module have
>>  # separate code paths depending on support in Python.
>> 
>> -hassni = getattr(ssl, 'HAS_SNI', False)
>> +hassni = getattr(ssl, u'HAS_SNI', False)
>> 
>>  try:
>>      OP_NO_SSLv2 = ssl.OP_NO_SSLv2
>>      OP_NO_SSLv3 = ssl.OP_NO_SSLv3
>>  except AttributeError:
>>      OP_NO_SSLv2 = 0x1000000
>>      OP_NO_SSLv3 = 0x2000000
>> 
>>  try:
>>      # ssl.SSLContext was added in 2.7.9 and presence indicates modern
>>      # SSL/TLS features are available.
>>      SSLContext = ssl.SSLContext
>>      modernssl = True
>> -    _canloaddefaultcerts = util.safehasattr(SSLContext, 'load_default_certs')
>> +    _canloaddefaultcerts = util.safehasattr(SSLContext, u'load_default_certs')
>>  except AttributeError:
>>      modernssl = False
>>      _canloaddefaultcerts = False
>> 
>>      # We implement SSLContext using the interface from the standard library.
>>      class SSLContext(object):
>>          # ssl.wrap_socket gained the "ciphers" named argument in 2.7.
>>          _supportsciphers = sys.version_info >= (2, 7)
>> diff --git a/mercurial/util.py b/mercurial/util.py
>> --- a/mercurial/util.py
>> +++ b/mercurial/util.py
>> @@ -41,22 +41,22 @@ from . import (
>>      error,
>>      i18n,
>>      osutil,
>>      parsers,
>>      pycompat,
>>  )
>> 
>>  for attr in (
>> -    'empty',
>> -    'queue',
>> -    'urlerr',
>> +    u'empty',
>> +    u'queue',
>> +    u'urlerr',
>>      # we do import urlreq, but we do it outside the loop
>>      #'urlreq',
>> -    'stringio',
>> +    u'stringio',
>>  ):
>>      globals()[attr] = getattr(pycompat, attr)
>> 
>>  # This line is to make pyflakes happy:
>>  urlreq = pycompat.urlreq
>> 
>>  if os.name == 'nt':
>>      from . import windows as platform
>> @@ -108,17 +108,17 @@ samefile = platform.samefile
>>  samestat = platform.samestat
>>  setbinary = platform.setbinary
>>  setflags = platform.setflags
>>  setsignalhandler = platform.setsignalhandler
>>  shellquote = platform.shellquote
>>  spawndetached = platform.spawndetached
>>  split = platform.split
>>  sshargs = platform.sshargs
>> -statfiles = getattr(osutil, 'statfiles', platform.statfiles)
>> +statfiles = getattr(osutil, u'statfiles', platform.statfiles)
>>  statisexec = platform.statisexec
>>  statislink = platform.statislink
>>  termwidth = platform.termwidth
>>  testpid = platform.testpid
>>  umask = platform.umask
>>  unlink = platform.unlink
>>  unlinkpath = platform.unlinkpath
>>  username = platform.username
>> @@ -525,17 +525,17 @@ class sortdict(dict):
>>          dict.__setitem__(self, key, val)
>> 
>>  class _lrucachenode(object):
>>      """A node in a doubly linked list.
>> 
>>      Holds a reference to nodes on either side as well as a key-value
>>      pair for the dictionary entry.
>>      """
>> -    __slots__ = ('next', 'prev', 'key', 'value')
>> +    __slots__ = (u'next', u'prev', u'key', u'value')
>> 
>>      def __init__(self):
>>          self.next = None
>>          self.prev = None
>> 
>>          self.key = _notset
>>          self.value = None
>> 
>> @@ -887,22 +887,22 @@ def pathto(root, n1, n2):
>>      return os.sep.join((['..'] * len(a)) + b) or '.'
>> 
>>  def mainfrozen():
>>      """return True if we are a frozen executable.
>> 
>>      The code supports py2exe (most common, Windows only) and tools/freeze
>>      (portable, not much used).
>>      """
>> -    return (safehasattr(sys, "frozen") or # new py2exe
>> -            safehasattr(sys, "importers") or # old py2exe
>> -            imp.is_frozen("__main__")) # tools/freeze
>> +    return (safehasattr(sys, u"frozen") or # new py2exe
>> +            safehasattr(sys, u"importers") or # old py2exe
>> +            imp.is_frozen(u"__main__")) # tools/freeze
>> 
>>  # the location of data files matching the source code
>> -if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
>> +if mainfrozen() and getattr(sys, u'frozen', None) != u'macosx_app':
>>      # executable version (py2exe) doesn't support __file__
>>      datapath = os.path.dirname(sys.executable)
>>  else:
>>      datapath = os.path.dirname(__file__)
>> 
>>  i18n.setdatapath(datapath)
>> 
>>  _hgexecutable = None
>> @@ -2135,17 +2135,17 @@ def getport(port):
>> 
>>  def parsebool(s):
>>      """Parse s into a boolean.
>> 
>>      If s is not a valid boolean, returns None.
>>      """
>>      return _booleans.get(s.lower(), None)
>> 
>> -_hexdig = '0123456789ABCDEFabcdef'
>> +_hexdig = u'0123456789ABCDEFabcdef'
>>  _hextochr = dict((a + b, chr(int(a + b, 16)))
>>                   for a in _hexdig for b in _hexdig)
>> 
>>  def _urlunquote(s):
>>      """Decode HTTP/HTML % encoding.
>> 
>>      >>> _urlunquote('abc%20def')
>>      'abc def'
>> @@ -2628,17 +2628,17 @@ class dirs(object):
>>              del dirs[base]
>> 
>>      def __iter__(self):
>>          return self._dirs.iterkeys()
>> 
>>      def __contains__(self, d):
>>          return d in self._dirs
>> 
>> -if safehasattr(parsers, 'dirs'):
>> +if safehasattr(parsers, u'dirs'):
>>      dirs = parsers.dirs
>> 
>>  def finddirs(path):
>>      pos = path.rfind('/')
>>      while pos != -1:
>>          yield path[:pos]
>>          pos = path.rfind('/', 0, pos)
>> 
>> _______________________________________________
>> Mercurial-devel mailing list
>> Mercurial-devel@mercurial-scm.org
>> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel

Patch

diff --git a/mercurial/demandimport.py b/mercurial/demandimport.py
--- a/mercurial/demandimport.py
+++ b/mercurial/demandimport.py
@@ -262,17 +262,17 @@  ignore = [
     'distutils.msvc9compiler'
     ]
 
 def isenabled():
     return builtins.__import__ == _demandimport
 
 def enable():
     "enable global demand-loading of modules"
-    if os.environ.get('HGDEMANDIMPORT') != 'disable':
+    if os.environ.get(u'HGDEMANDIMPORT') != u'disable':
         builtins.__import__ = _demandimport
 
 def disable():
     "disable global demand-loading of modules"
     builtins.__import__ = _origimport
 
 @contextmanager
 def deactivated():
diff --git a/mercurial/encoding.py b/mercurial/encoding.py
--- a/mercurial/encoding.py
+++ b/mercurial/encoding.py
@@ -18,17 +18,17 @@  from . import (
 )
 
 if sys.version_info[0] >= 3:
     unichr = chr
 
 # These unicode characters are ignored by HFS+ (Apple Technote 1150,
 # "Unicode Subtleties"), so we need to ignore them in some places for
 # sanity.
-_ignore = [unichr(int(x, 16)).encode("utf-8") for x in
+_ignore = [unichr(int(x, 16)).encode(u"utf-8") for x in
            "200c 200d 200e 200f 202a 202b 202c 202d 202e "
            "206a 206b 206c 206d 206e 206f feff".split()]
 # verify the next function will work
 if sys.version_info[0] >= 3:
     assert set(i[0] for i in _ignore) == set([ord(b'\xe2'), ord(b'\xef')])
 else:
     assert set(i[0] for i in _ignore) == set(["\xe2", "\xef"])
 
@@ -71,23 +71,23 @@  def _getpreferredencoding():
 
 _encodingfixers = {
     '646': lambda: 'ascii',
     'ANSI_X3.4-1968': lambda: 'ascii',
     'mac-roman': _getpreferredencoding
 }
 
 try:
-    encoding = os.environ.get("HGENCODING")
+    encoding = os.environ.get(u"HGENCODING")
     if not encoding:
         encoding = locale.getpreferredencoding() or 'ascii'
         encoding = _encodingfixers.get(encoding, lambda: encoding)()
 except locale.Error:
     encoding = 'ascii'
-encodingmode = os.environ.get("HGENCODINGMODE", "strict")
+encodingmode = os.environ.get(u"HGENCODINGMODE", u"strict")
 fallbackencoding = 'ISO-8859-1'
 
 class localstr(str):
     '''This class allows strings that are unmodified to be
     round-tripped to the local encoding and back'''
     def __new__(cls, u, l):
         s = str.__new__(cls, l)
         s._utf8 = u
@@ -175,17 +175,17 @@  def fromlocal(s):
         return s.decode(encoding, encodingmode).encode("utf-8")
     except UnicodeDecodeError as inst:
         sub = s[max(0, inst.start - 10):inst.start + 10]
         raise error.Abort("decoding near '%s': %s!" % (sub, inst))
     except LookupError as k:
         raise error.Abort(k, hint="please check your locale settings")
 
 # How to treat ambiguous-width characters. Set to 'wide' to treat as wide.
-wide = (os.environ.get("HGENCODINGAMBIGUOUS", "narrow") == "wide"
+wide = (os.environ.get(u"HGENCODINGAMBIGUOUS", u"narrow") == u"wide"
         and "WFA" or "WF")
 
 def colwidth(s):
     "Find the column width of a string for display in the local encoding"
     return ucolwidth(s.decode(encoding, 'replace'))
 
 def ucolwidth(d):
     "Find the column width of a Unicode string for display"
diff --git a/mercurial/i18n.py b/mercurial/i18n.py
--- a/mercurial/i18n.py
+++ b/mercurial/i18n.py
@@ -10,17 +10,17 @@  from __future__ import absolute_import
 import gettext as gettextmod
 import locale
 import os
 import sys
 
 from . import encoding
 
 # modelled after templater.templatepath:
-if getattr(sys, 'frozen', None) is not None:
+if getattr(sys, u'frozen', None) is not None:
     module = sys.executable
 else:
     module = __file__
 
 try:
     unicode
 except NameError:
     unicode = str
@@ -41,17 +41,17 @@  if (os.name == 'nt'
         _languages = [locale.windows_locale[langid]]
     except (ImportError, AttributeError, KeyError):
         # ctypes not found or unknown langid
         pass
 
 _ugettext = None
 
 def setdatapath(datapath):
-    localedir = os.path.join(datapath, 'locale')
+    localedir = os.path.join(datapath, u'locale')
     t = gettextmod.translation('hg', localedir, _languages, fallback=True)
     global _ugettext
     try:
         _ugettext = t.ugettext
     except AttributeError:
         _ugettext = t.gettext
 
 _msgcache = {}
@@ -70,34 +70,34 @@  def gettext(message):
     if message is None or not _ugettext:
         return message
 
     if message not in _msgcache:
         if type(message) is unicode:
             # goofy unicode docstrings in test
             paragraphs = message.split(u'\n\n')
         else:
-            paragraphs = [p.decode("ascii") for p in message.split('\n\n')]
+            paragraphs = [p.decode(u"ascii") for p in message.split('\n\n')]
         # Be careful not to translate the empty string -- it holds the
         # meta data of the .po file.
         u = u'\n\n'.join([p and _ugettext(p) or '' for p in paragraphs])
         try:
             # encoding.tolocal cannot be used since it will first try to
             # decode the Unicode string. Calling u.decode(enc) really
             # means u.encode(sys.getdefaultencoding()).decode(enc). Since
             # the Python encoding defaults to 'ascii', this fails if the
             # translated string use non-ASCII characters.
-            _msgcache[message] = u.encode(encoding.encoding, "replace")
+            _msgcache[message] = u.encode(encoding.encoding, u"replace")
         except LookupError:
             # An unknown encoding results in a LookupError.
             _msgcache[message] = message
     return _msgcache[message]
 
 def _plain():
-    if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
+    if u'HGPLAIN' not in os.environ and u'HGPLAINEXCEPT' not in os.environ:
         return False
-    exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
-    return 'i18n' not in exceptions
+    exceptions = os.environ.get(u'HGPLAINEXCEPT', u'').strip().split(u',')
+    return u'i18n' not in exceptions
 
 if _plain():
     _ = lambda message: message
 else:
     _ = gettext
diff --git a/mercurial/mdiff.py b/mercurial/mdiff.py
--- a/mercurial/mdiff.py
+++ b/mercurial/mdiff.py
@@ -40,27 +40,27 @@  class diffopts(object):
     noprefix disables the 'a/' and 'b/' prefixes (ignored in plain mode)
     ignorews ignores all whitespace changes in the diff
     ignorewsamount ignores changes in the amount of whitespace
     ignoreblanklines ignores changes whose lines are all blank
     upgrade generates git diffs to avoid data loss
     '''
 
     defaults = {
-        'context': 3,
-        'text': False,
-        'showfunc': False,
-        'git': False,
-        'nodates': False,
-        'nobinary': False,
-        'noprefix': False,
-        'ignorews': False,
-        'ignorewsamount': False,
-        'ignoreblanklines': False,
-        'upgrade': False,
+        u'context': 3,
+        u'text': False,
+        u'showfunc': False,
+        u'git': False,
+        u'nodates': False,
+        u'nobinary': False,
+        u'noprefix': False,
+        u'ignorews': False,
+        u'ignorewsamount': False,
+        u'ignoreblanklines': False,
+        u'upgrade': False,
         }
 
     __slots__ = defaults.keys()
 
     def __init__(self, **opts):
         for k in self.__slots__:
             v = opts.get(k)
             if v is None:
diff --git a/mercurial/pure/osutil.py b/mercurial/pure/osutil.py
--- a/mercurial/pure/osutil.py
+++ b/mercurial/pure/osutil.py
@@ -75,43 +75,43 @@  if os.name != 'nt':
         _msg_iovlen_t = ctypes.c_size_t
     else:
         _cmsg_len_t = _socklen_t
         _msg_controllen_t = _socklen_t
         _msg_iovlen_t = ctypes.c_int
 
     class _iovec(ctypes.Structure):
         _fields_ = [
-            ('iov_base', ctypes.c_void_p),
-            ('iov_len', ctypes.c_size_t),
+            (u'iov_base', ctypes.c_void_p),
+            (u'iov_len', ctypes.c_size_t),
         ]
 
     class _msghdr(ctypes.Structure):
         _fields_ = [
-            ('msg_name', ctypes.c_void_p),
-            ('msg_namelen', _socklen_t),
-            ('msg_iov', ctypes.POINTER(_iovec)),
-            ('msg_iovlen', _msg_iovlen_t),
-            ('msg_control', ctypes.c_void_p),
-            ('msg_controllen', _msg_controllen_t),
-            ('msg_flags', ctypes.c_int),
+            (u'msg_name', ctypes.c_void_p),
+            (u'msg_namelen', _socklen_t),
+            (u'msg_iov', ctypes.POINTER(_iovec)),
+            (u'msg_iovlen', _msg_iovlen_t),
+            (u'msg_control', ctypes.c_void_p),
+            (u'msg_controllen', _msg_controllen_t),
+            (u'msg_flags', ctypes.c_int),
         ]
 
     class _cmsghdr(ctypes.Structure):
         _fields_ = [
-            ('cmsg_len', _cmsg_len_t),
-            ('cmsg_level', ctypes.c_int),
-            ('cmsg_type', ctypes.c_int),
-            ('cmsg_data', ctypes.c_ubyte * 0),
+            (u'cmsg_len', _cmsg_len_t),
+            (u'cmsg_level', ctypes.c_int),
+            (u'cmsg_type', ctypes.c_int),
+            (u'cmsg_data', ctypes.c_ubyte * 0),
         ]
 
-    _libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
-    _recvmsg = getattr(_libc, 'recvmsg', None)
+    _libc = ctypes.CDLL(ctypes.util.find_library(u'c'), use_errno=True)
+    _recvmsg = getattr(_libc, u'recvmsg', None)
     if _recvmsg:
-        _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long)
+        _recvmsg.restype = getattr(ctypes, u'c_ssize_t', ctypes.c_long)
         _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr),
                              ctypes.c_int)
     else:
         # recvmsg isn't always provided by libc; such systems are unsupported
         def _recvmsg(sockfd, msg, flags):
             raise NotImplementedError('unsupported platform')
 
     def _CMSG_FIRSTHDR(msgh):
diff --git a/mercurial/pycompat.py b/mercurial/pycompat.py
--- a/mercurial/pycompat.py
+++ b/mercurial/pycompat.py
@@ -29,17 +29,17 @@  class _pycompatstub(object):
     pass
 
 def _alias(alias, origin, items):
     """ populate a _pycompatstub
 
     copies items from origin to alias
     """
     def hgcase(item):
-        return item.replace('_', '').lower()
+        return item.replace(u'_', u'').lower()
     for item in items:
         try:
             setattr(alias, hgcase(item), getattr(origin, item))
         except AttributeError:
             pass
 
 urlreq = _pycompatstub()
 urlerr = _pycompatstub()
@@ -80,44 +80,44 @@  try:
     _alias(urlerr, urllib2, (
         "HTTPError",
         "URLError",
     ))
 
 except ImportError:
     import urllib.request
     _alias(urlreq, urllib.request, (
-        "AbstractHTTPHandler",
-        "addclosehook",
-        "addinfourl",
-        "BaseHandler",
-        "build_opener",
-        "FileHandler",
-        "FTPHandler",
-        "ftpwrapper",
-        "HTTPHandler",
-        "HTTPSHandler",
-        "install_opener",
-        "pathname2url",
-        "HTTPBasicAuthHandler",
-        "HTTPDigestAuthHandler",
-        "ProxyHandler",
-        "quote",
-        "Request",
-        "splitattr",
-        "splitpasswd",
-        "splitport",
-        "splituser",
-        "unquote",
-        "url2pathname",
-        "urlopen",
+        u"AbstractHTTPHandler",
+        u"addclosehook",
+        u"addinfourl",
+        u"BaseHandler",
+        u"build_opener",
+        u"FileHandler",
+        u"FTPHandler",
+        u"ftpwrapper",
+        u"HTTPHandler",
+        u"HTTPSHandler",
+        u"install_opener",
+        u"pathname2url",
+        u"HTTPBasicAuthHandler",
+        u"HTTPDigestAuthHandler",
+        u"ProxyHandler",
+        u"quote",
+        u"Request",
+        u"splitattr",
+        u"splitpasswd",
+        u"splitport",
+        u"splituser",
+        u"unquote",
+        u"url2pathname",
+        u"urlopen",
     ))
     import urllib.error
     _alias(urlerr, urllib.error, (
-        "HTTPError",
-        "URLError",
+        u"HTTPError",
+        u"URLError",
     ))
 
 try:
     xrange
 except NameError:
     import builtins
     builtins.xrange = range
diff --git a/mercurial/registrar.py b/mercurial/registrar.py
--- a/mercurial/registrar.py
+++ b/mercurial/registrar.py
@@ -44,17 +44,17 @@  class _funcregistrarbase(object):
             self._table = table
 
     def __call__(self, decl, *args, **kwargs):
         return lambda func: self._doregister(func, decl, *args, **kwargs)
 
     def _doregister(self, func, decl, *args, **kwargs):
         name = self._getname(decl)
 
-        if func.__doc__ and not util.safehasattr(func, '_origdoc'):
+        if func.__doc__ and not util.safehasattr(func, u'_origdoc'):
             doc = func.__doc__.strip()
             func._origdoc = doc
             func.__doc__ = self._formatdoc(decl, doc)
 
         self._table[name] = func
         self._extrasetup(name, func, *args, **kwargs)
 
         return func
@@ -78,17 +78,19 @@  class _funcregistrarbase(object):
 
     _docformat = None
 
     def _formatdoc(self, decl, doc):
         """Return formatted document of the registered function for help
 
         'doc' is '__doc__.strip()' of the registered function.
         """
-        return self._docformat % (decl, doc)
+        # docstrings are using the source file encoding, which should be
+        # utf-8.
+        return self._docformat % (decl, doc.encode(u'utf-8'))
 
     def _extrasetup(self, name, func):
         """Execute exra setup for registered function, if needed
         """
         pass
 
 class revsetpredicate(_funcregistrarbase):
     """Decorator to register revset predicate
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -171,21 +171,21 @@  elements = {
     "string": (0, "string", None, None, None),
     "end": (0, None, None, None, None),
 }
 
 keywords = set(['and', 'or', 'not'])
 
 # default set of valid characters for the initial letter of symbols
 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
-                      if c.isalnum() or c in '._@' or ord(c) > 127)
+                      if c.isalnum() or c in u'._@' or ord(c) > 127)
 
 # default set of valid characters for non-initial letters of symbols
 _symletters = set(c for c in  [chr(i) for i in xrange(256)]
-                  if c.isalnum() or c in '-._/@' or ord(c) > 127)
+                  if c.isalnum() or c in u'-._/@' or ord(c) > 127)
 
 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
     '''
     Parse a revset statement into a stream of tokens
 
     ``syminitletters`` is the set of valid characters for the initial
     letter of symbols.
 
@@ -2215,17 +2215,17 @@  def _optimize(x, small):
 
 def optimize(tree):
     _weight, newtree = _optimize(tree, small=True)
     return newtree
 
 # the set of valid characters for the initial letter of symbols in
 # alias declarations and definitions
 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
-                           if c.isalnum() or c in '._@$' or ord(c) > 127)
+                           if c.isalnum() or c in u'._@$' or ord(c) > 127)
 
 def _parsewith(spec, lookup=None, syminitletters=None):
     """Generate a parse tree of given spec with given tokenizing options
 
     >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
     ('func', ('symbol', 'foo'), ('symbol', '$1'))
     >>> _parsewith('$1')
     Traceback (most recent call last):
diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py
--- a/mercurial/sslutil.py
+++ b/mercurial/sslutil.py
@@ -22,31 +22,31 @@  from . import (
 # Python 2.7.9+ overhauled the built-in SSL/TLS features of Python. It added
 # support for TLS 1.1, TLS 1.2, SNI, system CA stores, etc. These features are
 # all exposed via the "ssl" module.
 #
 # Depending on the version of Python being used, SSL/TLS support is either
 # modern/secure or legacy/insecure. Many operations in this module have
 # separate code paths depending on support in Python.
 
-hassni = getattr(ssl, 'HAS_SNI', False)
+hassni = getattr(ssl, u'HAS_SNI', False)
 
 try:
     OP_NO_SSLv2 = ssl.OP_NO_SSLv2
     OP_NO_SSLv3 = ssl.OP_NO_SSLv3
 except AttributeError:
     OP_NO_SSLv2 = 0x1000000
     OP_NO_SSLv3 = 0x2000000
 
 try:
     # ssl.SSLContext was added in 2.7.9 and presence indicates modern
     # SSL/TLS features are available.
     SSLContext = ssl.SSLContext
     modernssl = True
-    _canloaddefaultcerts = util.safehasattr(SSLContext, 'load_default_certs')
+    _canloaddefaultcerts = util.safehasattr(SSLContext, u'load_default_certs')
 except AttributeError:
     modernssl = False
     _canloaddefaultcerts = False
 
     # We implement SSLContext using the interface from the standard library.
     class SSLContext(object):
         # ssl.wrap_socket gained the "ciphers" named argument in 2.7.
         _supportsciphers = sys.version_info >= (2, 7)
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -41,22 +41,22 @@  from . import (
     error,
     i18n,
     osutil,
     parsers,
     pycompat,
 )
 
 for attr in (
-    'empty',
-    'queue',
-    'urlerr',
+    u'empty',
+    u'queue',
+    u'urlerr',
     # we do import urlreq, but we do it outside the loop
     #'urlreq',
-    'stringio',
+    u'stringio',
 ):
     globals()[attr] = getattr(pycompat, attr)
 
 # This line is to make pyflakes happy:
 urlreq = pycompat.urlreq
 
 if os.name == 'nt':
     from . import windows as platform
@@ -108,17 +108,17 @@  samefile = platform.samefile
 samestat = platform.samestat
 setbinary = platform.setbinary
 setflags = platform.setflags
 setsignalhandler = platform.setsignalhandler
 shellquote = platform.shellquote
 spawndetached = platform.spawndetached
 split = platform.split
 sshargs = platform.sshargs
-statfiles = getattr(osutil, 'statfiles', platform.statfiles)
+statfiles = getattr(osutil, u'statfiles', platform.statfiles)
 statisexec = platform.statisexec
 statislink = platform.statislink
 termwidth = platform.termwidth
 testpid = platform.testpid
 umask = platform.umask
 unlink = platform.unlink
 unlinkpath = platform.unlinkpath
 username = platform.username
@@ -525,17 +525,17 @@  class sortdict(dict):
         dict.__setitem__(self, key, val)
 
 class _lrucachenode(object):
     """A node in a doubly linked list.
 
     Holds a reference to nodes on either side as well as a key-value
     pair for the dictionary entry.
     """
-    __slots__ = ('next', 'prev', 'key', 'value')
+    __slots__ = (u'next', u'prev', u'key', u'value')
 
     def __init__(self):
         self.next = None
         self.prev = None
 
         self.key = _notset
         self.value = None
 
@@ -887,22 +887,22 @@  def pathto(root, n1, n2):
     return os.sep.join((['..'] * len(a)) + b) or '.'
 
 def mainfrozen():
     """return True if we are a frozen executable.
 
     The code supports py2exe (most common, Windows only) and tools/freeze
     (portable, not much used).
     """
-    return (safehasattr(sys, "frozen") or # new py2exe
-            safehasattr(sys, "importers") or # old py2exe
-            imp.is_frozen("__main__")) # tools/freeze
+    return (safehasattr(sys, u"frozen") or # new py2exe
+            safehasattr(sys, u"importers") or # old py2exe
+            imp.is_frozen(u"__main__")) # tools/freeze
 
 # the location of data files matching the source code
-if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
+if mainfrozen() and getattr(sys, u'frozen', None) != u'macosx_app':
     # executable version (py2exe) doesn't support __file__
     datapath = os.path.dirname(sys.executable)
 else:
     datapath = os.path.dirname(__file__)
 
 i18n.setdatapath(datapath)
 
 _hgexecutable = None
@@ -2135,17 +2135,17 @@  def getport(port):
 
 def parsebool(s):
     """Parse s into a boolean.
 
     If s is not a valid boolean, returns None.
     """
     return _booleans.get(s.lower(), None)
 
-_hexdig = '0123456789ABCDEFabcdef'
+_hexdig = u'0123456789ABCDEFabcdef'
 _hextochr = dict((a + b, chr(int(a + b, 16)))
                  for a in _hexdig for b in _hexdig)
 
 def _urlunquote(s):
     """Decode HTTP/HTML % encoding.
 
     >>> _urlunquote('abc%20def')
     'abc def'
@@ -2628,17 +2628,17 @@  class dirs(object):
             del dirs[base]
 
     def __iter__(self):
         return self._dirs.iterkeys()
 
     def __contains__(self, d):
         return d in self._dirs
 
-if safehasattr(parsers, 'dirs'):
+if safehasattr(parsers, u'dirs'):
     dirs = parsers.dirs
 
 def finddirs(path):
     pos = path.rfind('/')
     while pos != -1:
         yield path[:pos]
         pos = path.rfind('/', 0, pos)