Patchwork [2,of,5] run-tests: add --with-python3 to define a Python 3 interpreter

login
register
mail settings
Submitter Gregory Szorc
Date March 12, 2016, 10:11 p.m.
Message ID <8f46c2b82dee58117e86.1457820693@ubuntu-vm-main>
Download mbox | patch
Permalink /patch/13834/
State Superseded
Headers show

Comments

Gregory Szorc - March 12, 2016, 10:11 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1457818877 28800
#      Sat Mar 12 13:41:17 2016 -0800
# Node ID 8f46c2b82dee58117e861f1c374c8c229a8fd12d
# Parent  afb4d2d519921346fd33b05880c677740ac8f086
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.
Pierre-Yves David - March 14, 2016, 1:48 p.m.
On 03/12/2016 10:11 PM, Gregory Szorc wrote:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@gmail.com>
> # Date 1457818877 28800
> #      Sat Mar 12 13:41:17 2016 -0800
> # Node ID 8f46c2b82dee58117e861f1c374c8c229a8fd12d
> # Parent  afb4d2d519921346fd33b05880c677740ac8f086
> 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.

Can we make it clear that this is temporary and why we need it in the 
code base itself? so that we can proceed to some cleanup in the future?

Patch

diff --git a/tests/hghave.py b/tests/hghave.py
--- a/tests/hghave.py
+++ b/tests/hghave.py
@@ -438,16 +438,20 @@  def has_absimport():
     import __future__
     from mercurial import util
     return util.safehasattr(__future__, "absolute_import")
 
 @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",
         os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
     ])
 
 @check("slow", "allow slow tests")
diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -250,16 +250,18 @@  def getparser():
         help="test using specified hg script rather than a "
              "temporary installation")
     parser.add_option("--chg", action="store_true",
                       help="install and use chg wrapper in place of hg")
     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+")
+    parser.add_option("--with-python3", metavar="PYTHON3",
+                      help="Python 3 interpreter (if running under Python 2)")
     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')
     parser.add_option('--profile-runner', action='store_true',
                       help='run statprof on run-tests')
     parser.add_option('--allow-slow-tests', action='store_true',
                       help='allow extremely slow tests')
@@ -348,16 +350,37 @@  def parseargs(args, parser):
             sys.stderr.write(
                 'warning: --slowtimeout option ignored with --debug\n')
         options.timeout = 0
         options.slowtimeout = 0
     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')
     else:
         options.whitelisted = {}
 
     if options.showchannels:
@@ -1970,16 +1993,19 @@  class TestRunner(object):
             self._hgcommand = b'chg'
         elif self.options.with_chg:
             chgbindir = os.path.dirname(os.path.realpath(self.options.with_chg))
             self._hgcommand = os.path.basename(self.options.with_chg)
 
         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:
             sepb = _bytespath(os.pathsep)
         else:
             sepb = os.pathsep
         path = [self._bindir, runtestdir] + osenvironb[b"PATH"].split(sepb)