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

timeless timeless at gmail.com
Mon May 16 11:25:47 EDT 2016


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 at gmail.com> wrote:

> # HG changeset patch
> # User Gregory Szorc <gregory.szorc at 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 at mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.mercurial-scm.org/pipermail/mercurial-devel/attachments/20160516/336956a5/attachment.html>


More information about the Mercurial-devel mailing list