Patchwork [1,of,4,V2] run-tests: add --with-python3 to define a Python 3 interpreter

login
register
mail settings
Submitter Gregory Szorc
Date March 18, 2016, 11:19 p.m.
Message ID <049a0221014adeef44ab.1458343192@gps-mbp.local>
Download mbox | patch
Permalink /patch/13941/
State Accepted
Headers show

Comments

Gregory Szorc - March 18, 2016, 11:19 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1458343076 25200
#      Fri Mar 18 16:17:56 2016 -0700
# Node ID 049a0221014adeef44ab5dd1c1daf34caf9e0090
# Parent  1435a8e9b5fe38cfe900e0a75fefab046af73dd6
run-tests: add --with-python3 to define a Python 3 interpreter

Currently, very few parts of Mercurial run under Python 3, notably the
test harness.

We want to write tests that run Python 3. For example, we want to
extend test-check-py3-compat.t to parse and load Python files.
However, we have a problem: finding appropriate files requires
running `hg files` and this requires Python 2 until `hg` works
with Python 3.

As a temporary workaround, we add --with-python3 to the test harness
to allow us to define the path to a Python 3 interpreter. This
interpreter is made available to the test environment via $PYTHON3 so
tests can run things with Python 3 while the test harness and `hg`
invocations continue to run from Python 2. To round out the feature,
a "py3exe" hghave check has been added.

Patch

diff --git a/tests/hghave.py b/tests/hghave.py
--- a/tests/hghave.py
+++ b/tests/hghave.py
@@ -442,8 +442,12 @@  def has_absimport():
 @check("py3k", "running with Python 3.x")
 def has_py3k():
     return 3 == sys.version_info[0]
 
+@check("py3exe", "a Python 3.x interpreter is available")
+def has_python3exe():
+    return 'PYTHON3' in os.environ
+
 @check("pure", "running with pure Python code")
 def has_pure():
     return any([
         os.environ.get("HGMODULEPOLICY") == "py",
diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -254,8 +254,13 @@  def getparser():
     parser.add_option("--with-chg", metavar="CHG",
                       help="use specified chg wrapper in place of hg")
     parser.add_option("-3", "--py3k-warnings", action="store_true",
         help="enable Py3k warnings on Python 2.6+")
+    # This option should be deleted once test-check-py3-compat.t and other
+    # Python 3 tests run with Python 3.
+    parser.add_option("--with-python3", metavar="PYTHON3",
+                      help="Python 3 interpreter (if running under Python 2)"
+                           " (TEMPORARY)")
     parser.add_option('--extra-config-opt', action="append",
                       help='set the given config opt in the test hgrc')
     parser.add_option('--random', action="store_true",
                       help='run tests in random order')
@@ -352,8 +357,29 @@  def parseargs(args, parser):
     if options.py3k_warnings:
         if PYTHON3:
             parser.error(
                 '--py3k-warnings can only be used on Python 2.6 and 2.7')
+    if options.with_python3:
+        if PYTHON3:
+            parser.error('--with-python3 cannot be used when executing with '
+                         'Python 3')
+
+        # Verify Python3 executable is acceptable.
+        proc = subprocess.Popen([options.with_python3, b'--version'],
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.STDOUT)
+        out, _err = proc.communicate()
+        ret = proc.wait()
+        if ret != 0:
+            parser.error('could not determine version of python 3')
+        if not out.startswith('Python '):
+            parser.error('unexpected output from python3 --version: %s' %
+                         out)
+        vers = version.LooseVersion(out[len('Python '):])
+        if vers < version.LooseVersion('3.5.0'):
+            parser.error('--with-python3 version must be 3.5.0 or greater; '
+                         'got %s' % out)
+
     if options.blacklist:
         options.blacklist = parselistfiles(options.blacklist, 'blacklist')
     if options.whitelist:
         options.whitelisted = parselistfiles(options.whitelist, 'whitelist')
@@ -1974,8 +2000,11 @@  class TestRunner(object):
 
         osenvironb[b"BINDIR"] = self._bindir
         osenvironb[b"PYTHON"] = PYTHON
 
+        if self.options.with_python3:
+            osenvironb[b'PYTHON3'] = self.options.with_python3
+
         fileb = _bytespath(__file__)
         runtestdir = os.path.abspath(os.path.dirname(fileb))
         osenvironb[b'RUNTESTDIR'] = runtestdir
         if PYTHON3: