Patchwork [3,of,3,V2] py3: fix str vs bytes in enough places to run `hg version` on Windows

login
register
mail settings
Submitter Matt Harbison
Date Sept. 15, 2018, 2:39 a.m.
Message ID <bafe274973423eaa7a49.1536979167@Envy>
Download mbox | patch
Permalink /patch/34670/
State Accepted
Headers show

Comments

Matt Harbison - Sept. 15, 2018, 2:39 a.m.
# HG changeset patch
# User Matt Harbison <matt_harbison@yahoo.com>
# Date 1536890820 14400
#      Thu Sep 13 22:07:00 2018 -0400
# Node ID bafe274973423eaa7a49f2f2003366fddbdaca61
# Parent  8490cde7d20031c0c6295f5df531db02bca1a6a9
py3: fix str vs bytes in enough places to run `hg version` on Windows

I don't have Visual Studio 2015 at home, but this now works with a handful of
extensions (blackbox, extdiff, patchbomb, phabricator and rebase, but not
evolve):

    $ HGMODULEPOLICY=py py -3 ../hg version

Enabling the evolve extension causes the usual "failed to import ..." line, but
then print this before the usual version output:

    ('commit', '[b'debugancestor', b'debugapplystreamclonebundle', ...,
     b'verify', b'version']')

... where the elided part seems to be every command and alias known.
Yuya Nishihara - Sept. 15, 2018, 6:09 a.m.
On Fri, 14 Sep 2018 22:39:27 -0400, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <matt_harbison@yahoo.com>
> # Date 1536890820 14400
> #      Thu Sep 13 22:07:00 2018 -0400
> # Node ID bafe274973423eaa7a49f2f2003366fddbdaca61
> # Parent  8490cde7d20031c0c6295f5df531db02bca1a6a9
> py3: fix str vs bytes in enough places to run `hg version` on Windows

> --- a/mercurial/windows.py
> +++ b/mercurial/windows.py
> @@ -389,7 +389,7 @@ def shellquote(s):
>      """
>      global _quotere
>      if _quotere is None:
> -        _quotere = re.compile(r'(\\*)("|\\$)')
> +        _quotere = re.compile(b'(\\*)("|\\$)')
>      global _needsshellquote
>      if _needsshellquote is None:
>          # ":" is also treated as "safe character", because it is used as a part
> @@ -397,11 +397,11 @@ def shellquote(s):
>          # safe because shlex.split() (kind of) treats it as an escape char and
>          # drops it.  It will leave the next character, even if it is another
>          # "\".
> -        _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/-]').search
> +        _needsshellquote = re.compile(b'[^a-zA-Z0-9._:/-]').search
>      if s and not _needsshellquote(s) and not _quotere.search(s):
>          # "s" shouldn't have to be quoted
>          return s
> -    return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
> +    return b'"%s"' % _quotere.sub(r'\1\1\\\2', s)

Fixed these to br''.

Patch

diff --git a/mercurial/color.py b/mercurial/color.py
--- a/mercurial/color.py
+++ b/mercurial/color.py
@@ -408,21 +408,21 @@  if pycompat.iswindows:
     _INVALID_HANDLE_VALUE = -1
 
     class _COORD(ctypes.Structure):
-        _fields_ = [('X', ctypes.c_short),
-                    ('Y', ctypes.c_short)]
+        _fields_ = [(r'X', ctypes.c_short),
+                    (r'Y', ctypes.c_short)]
 
     class _SMALL_RECT(ctypes.Structure):
-        _fields_ = [('Left', ctypes.c_short),
-                    ('Top', ctypes.c_short),
-                    ('Right', ctypes.c_short),
-                    ('Bottom', ctypes.c_short)]
+        _fields_ = [(r'Left', ctypes.c_short),
+                    (r'Top', ctypes.c_short),
+                    (r'Right', ctypes.c_short),
+                    (r'Bottom', ctypes.c_short)]
 
     class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
-        _fields_ = [('dwSize', _COORD),
-                    ('dwCursorPosition', _COORD),
-                    ('wAttributes', _WORD),
-                    ('srWindow', _SMALL_RECT),
-                    ('dwMaximumWindowSize', _COORD)]
+        _fields_ = [(r'dwSize', _COORD),
+                    (r'dwCursorPosition', _COORD),
+                    (r'wAttributes', _WORD),
+                    (r'srWindow', _SMALL_RECT),
+                    (r'dwMaximumWindowSize', _COORD)]
 
     _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
     _STD_ERROR_HANDLE = 0xfffffff4  # (DWORD)-12
@@ -484,7 +484,7 @@  if pycompat.iswindows:
             w32effects = None
         else:
             origattr = csbi.wAttributes
-            ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
+            ansire = re.compile(b'\033\[([^m]*)m([^\033]*)(.*)',
                                 re.MULTILINE | re.DOTALL)
 
     def win32print(ui, writefunc, *msgs, **opts):
@@ -516,15 +516,15 @@  if pycompat.iswindows:
                     # them if not found
                     pass
         # hack to ensure regexp finds data
-        if not text.startswith('\033['):
-            text = '\033[m' + text
+        if not text.startswith(b'\033['):
+            text = b'\033[m' + text
 
         # Look for ANSI-like codes embedded in text
         m = re.match(ansire, text)
 
         try:
             while m:
-                for sattr in m.group(1).split(';'):
+                for sattr in m.group(1).split(b';'):
                     if sattr:
                         attr = mapcolor(int(sattr), attr)
                 ui.flush()
diff --git a/mercurial/pure/osutil.py b/mercurial/pure/osutil.py
--- a/mercurial/pure/osutil.py
+++ b/mercurial/pure/osutil.py
@@ -14,6 +14,7 @@  import socket
 import stat as statmod
 
 from .. import (
+    encoding,
     pycompat,
 )
 
@@ -193,7 +194,8 @@  else:
 
     def _raiseioerror(name):
         err = ctypes.WinError()
-        raise IOError(err.errno, '%s: %s' % (name, err.strerror))
+        raise IOError(err.errno, r'%s: %s' % (encoding.strfromlocal(name),
+                      err.strerror))
 
     class posixfile(object):
         '''a file object aiming for POSIX-like semantics
@@ -207,14 +209,14 @@  else:
         remains but cannot be opened again or be recreated under the same name,
         until all reading processes have closed the file.'''
 
-        def __init__(self, name, mode='r', bufsize=-1):
-            if 'b' in mode:
+        def __init__(self, name, mode=b'r', bufsize=-1):
+            if b'b' in mode:
                 flags = _O_BINARY
             else:
                 flags = _O_TEXT
 
-            m0 = mode[0]
-            if m0 == 'r' and '+' not in mode:
+            m0 = mode[0:1]
+            if m0 == b'r' and b'+' not in mode:
                 flags |= _O_RDONLY
                 access = _GENERIC_READ
             else:
@@ -223,15 +225,15 @@  else:
                 flags |= _O_RDWR
                 access = _GENERIC_READ | _GENERIC_WRITE
 
-            if m0 == 'r':
+            if m0 == b'r':
                 creation = _OPEN_EXISTING
-            elif m0 == 'w':
+            elif m0 == b'w':
                 creation = _CREATE_ALWAYS
-            elif m0 == 'a':
+            elif m0 == b'a':
                 creation = _OPEN_ALWAYS
                 flags |= _O_APPEND
             else:
-                raise ValueError("invalid mode: %s" % mode)
+                raise ValueError(r"invalid mode: %s" % pycompat.sysstr(mode))
 
             fh = _kernel32.CreateFileA(name, access,
                     _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
diff --git a/mercurial/windows.py b/mercurial/windows.py
--- a/mercurial/windows.py
+++ b/mercurial/windows.py
@@ -389,7 +389,7 @@  def shellquote(s):
     """
     global _quotere
     if _quotere is None:
-        _quotere = re.compile(r'(\\*)("|\\$)')
+        _quotere = re.compile(b'(\\*)("|\\$)')
     global _needsshellquote
     if _needsshellquote is None:
         # ":" is also treated as "safe character", because it is used as a part
@@ -397,11 +397,11 @@  def shellquote(s):
         # safe because shlex.split() (kind of) treats it as an escape char and
         # drops it.  It will leave the next character, even if it is another
         # "\".
-        _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/-]').search
+        _needsshellquote = re.compile(b'[^a-zA-Z0-9._:/-]').search
     if s and not _needsshellquote(s) and not _quotere.search(s):
         # "s" shouldn't have to be quoted
         return s
-    return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
+    return b'"%s"' % _quotere.sub(r'\1\1\\\2', s)
 
 def _unquote(s):
     if s.startswith(b'"') and s.endswith(b'"'):