Bug 1459071 - Synchronize mozharness's virtualenv with vendored copy; r?ted draft
authorGregory Szorc <gps@mozilla.com>
Thu, 03 May 2018 15:58:45 -0700
changeset 792160 93bef3333002397901a08ca879361d4b52947b33
parent 792159 c4b546c3cc29b99df6cd792f1cb6dd5a51e67bef
child 792161 7433e1d913229738bd56891db88d587875bcd936
push id109015
push usermconley@mozilla.com
push dateMon, 07 May 2018 19:11:58 +0000
reviewersted
bugs1459071
milestone61.0a1
Bug 1459071 - Synchronize mozharness's virtualenv with vendored copy; r?ted We have two copies of virtualenv in the repo :/ This commit effectively copies third_party/python/virtualenv into the mozharness directory so the virtualenv versions are in sync. MozReview-Commit-ID: A6LIU3CW2XB
testing/mozharness/external_tools/virtualenv/PKG-INFO
testing/mozharness/external_tools/virtualenv/bin/rebuild-script.py
testing/mozharness/external_tools/virtualenv/docs/changes.rst
testing/mozharness/external_tools/virtualenv/docs/development.rst
testing/mozharness/external_tools/virtualenv/docs/installation.rst
testing/mozharness/external_tools/virtualenv/docs/userguide.rst
testing/mozharness/external_tools/virtualenv/setup.cfg
testing/mozharness/external_tools/virtualenv/setup.py
testing/mozharness/external_tools/virtualenv/site.py
testing/mozharness/external_tools/virtualenv/tests/test_virtualenv.py
testing/mozharness/external_tools/virtualenv/virtualenv.py
testing/mozharness/external_tools/virtualenv/virtualenv_embedded/activate.fish
testing/mozharness/external_tools/virtualenv/virtualenv_support/pip-8.1.2-py2.py3-none-any.whl
testing/mozharness/external_tools/virtualenv/virtualenv_support/pip-9.0.3-py2.py3-none-any.whl
testing/mozharness/external_tools/virtualenv/virtualenv_support/setuptools-25.2.0-py2.py3-none-any.whl
testing/mozharness/external_tools/virtualenv/virtualenv_support/setuptools-39.0.1-py2.py3-none-any.whl
testing/mozharness/external_tools/virtualenv/virtualenv_support/wheel-0.30.0-py2.py3-none-any.whl
--- a/testing/mozharness/external_tools/virtualenv/PKG-INFO
+++ b/testing/mozharness/external_tools/virtualenv/PKG-INFO
@@ -1,11 +1,11 @@
 Metadata-Version: 1.1
 Name: virtualenv
-Version: 15.0.1
+Version: 15.2.0
 Summary: Virtual Python Environment builder
 Home-page: https://virtualenv.pypa.io/
 Author: Jannis Leidel, Carl Meyer and Brian Rosner
 Author-email: python-virtualenv@groups.google.com
 License: MIT
 Description: Virtualenv
         ==========
         
@@ -42,46 +42,43 @@ Description: Virtualenv
         share libraries with other virtualenv environments (and optionally
         doesn't access the globally installed libraries either).
         
         .. comment: 
         
         Release History
         ===============
         
-        15.0.1 (2016-03-17)
+        15.2.0 (2018-03-21)
         -------------------
         
-        * Print error message when DEST_DIR exists and is a file
+        * Upgrade setuptools to 39.0.1.
         
-        * Upgrade setuptools to 20.3
+        * Upgrade pip to 9.0.3.
         
-        * Upgrade pip to 8.1.1.
+        * Upgrade wheel to 0.30.0.
         
         
-        15.0.0 (2016-03-05)
+        15.1.0 (2016-11-15)
         -------------------
         
-        * Remove the `virtualenv-N.N` script from the package; this can no longer be
-          correctly created from a wheel installation.
-          Resolves #851, #692
+        * Support Python 3.6.
+        
+        * Upgrade setuptools to 28.0.0.
         
-        * Remove accidental runtime dependency on pip by extracting certificate in the
-          subprocess.
+        * Upgrade pip to 9.0.1.
         
-        * Upgrade setuptools 20.2.2.
-        
-        * Upgrade pip to 8.1.0.
+        * Don't install pre-release versions of pip, setuptools, or wheel from PyPI.
         
         
         `Full Changelog <https://virtualenv.pypa.io/en/latest/changes.html>`_.
 Keywords: setuptools deployment installation distutils
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Programming Language :: Python :: 2
 Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
--- a/testing/mozharness/external_tools/virtualenv/bin/rebuild-script.py
+++ b/testing/mozharness/external_tools/virtualenv/bin/rebuild-script.py
@@ -2,29 +2,36 @@
 """
 Helper script to rebuild virtualenv.py from virtualenv_support
 """
 from __future__ import print_function
 
 import os
 import re
 import codecs
-from zlib import crc32
+from zlib import crc32 as _crc32
+
+
+def crc32(data):
+    """Python version idempotent"""
+    return _crc32(data) & 0xffffffff
+
 
 here = os.path.dirname(__file__)
 script = os.path.join(here, '..', 'virtualenv.py')
 
 gzip = codecs.lookup('zlib')
 b64 = codecs.lookup('base64')
 
 file_regex = re.compile(
     br'##file (.*?)\n([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*convert\("""\n(.*?)"""\)',
     re.S)
 file_template = b'##file %(filename)s\n%(varname)s = convert("""\n%(data)s""")'
 
+
 def rebuild(script_path):
     with open(script_path, 'rb') as f:
         script_content = f.read()
     parts = []
     last_pos = 0
     match = None
     for match in file_regex.finditer(script_content):
         parts += [script_content[last_pos:match.start()]]
@@ -37,22 +44,22 @@ def rebuild(script_path):
         pathname = os.path.join(here, '..', 'virtualenv_embedded', fn_decoded)
 
         with open(pathname, 'rb') as f:
             embedded = f.read()
         new_crc = crc32(embedded)
         new_data = b64.encode(gzip.encode(embedded)[0])[0]
 
         if new_data == data:
-            print('  File up to date (crc: %s)' % new_crc)
+            print('  File up to date (crc: %08x)' % new_crc)
             parts += [match.group(0)]
             continue
         # Else: content has changed
         crc = crc32(gzip.decode(b64.decode(data)[0])[0])
-        print('  Content changed (crc: %s -> %s)' %
+        print('  Content changed (crc: %08x -> %08x)' %
               (crc, new_crc))
         new_match = file_template % {
             b'filename': filename,
             b'varname': varname,
             b'data': new_data
         }
         parts += [new_match]
 
--- a/testing/mozharness/external_tools/virtualenv/docs/changes.rst
+++ b/testing/mozharness/external_tools/virtualenv/docs/changes.rst
@@ -1,11 +1,55 @@
 Release History
 ===============
 
+15.2.0 (2018-03-21)
+-------------------
+
+* Upgrade setuptools to 39.0.1.
+
+* Upgrade pip to 9.0.3.
+
+* Upgrade wheel to 0.30.0.
+
+
+15.1.0 (2016-11-15)
+-------------------
+
+* Support Python 3.6.
+
+* Upgrade setuptools to 28.0.0.
+
+* Upgrade pip to 9.0.1.
+
+* Don't install pre-release versions of pip, setuptools, or wheel from PyPI.
+
+
+15.0.3 (2016-08-05)
+-------------------
+
+* Test for given python path actually being an executable *file*, :issue:`939`
+
+* Only search for copy actual existing Tcl/Tk directories (:pull:`937`)
+
+* Generically search for correct Tcl/Tk version (:pull:`926`, :pull:`933`)
+
+* Upgrade setuptools to 22.0.5
+
+15.0.2 (2016-05-28)
+-------------------
+
+* Copy Tcl/Tk libs on Windows to allow them to run,
+  fixes :issue:`93` (:pull:`888`)
+
+* Upgrade setuptools to 21.2.1.
+
+* Upgrade pip to 8.1.2.
+
+
 15.0.1 (2016-03-17)
 -------------------
 
 * Print error message when DEST_DIR exists and is a file
 
 * Upgrade setuptools to 20.3
 
 * Upgrade pip to 8.1.1.
@@ -58,17 +102,17 @@ 14.0.3 (2016-01-28)
 * Upgrade setuptools to 19.6.1
 
 
 14.0.2 (2016-01-28)
 -------------------
 
 * Upgrade setuptools to 19.6
 
-* Suppress any errors from `unset` on different shells (:pull:`843`)
+* Supress any errors from `unset` on different shells (:pull:`843`)
 
 * Normalize letter case for prefix path checking. Fixes :issue:`837`
 
 
 14.0.1 (2016-01-21)
 -------------------
 
 * Upgrade from pip 8.0.0 to 8.0.2.
@@ -350,17 +394,17 @@ 1.10 (2013-07-23)
 * Fixed issue with readline on Windows.
 
 .. _Distribute: https://pypi.python.org/pypi/distribute
 
 1.9.1 (2013-03-08)
 ------------------
 
 * Updated to pip 1.3.1 that fixed a major backward incompatible change of
-  parsing URLs to externally hosted packages that got accidentally included
+  parsing URLs to externally hosted packages that got accidentily included
   in pip 1.3.
 
 1.9 (2013-03-07)
 ----------------
 
 * Unset VIRTUAL_ENV environment variable in deactivate.bat (Pull #364)
 * Upgraded distribute to 0.6.34.
 * Added ``--no-setuptools`` and ``--no-pip`` options (Pull #336).
--- a/testing/mozharness/external_tools/virtualenv/docs/development.rst
+++ b/testing/mozharness/external_tools/virtualenv/docs/development.rst
@@ -14,17 +14,17 @@ pip.
 
 Files in the `virtualenv_embedded/` subdirectory are embedded into
 `virtualenv.py` itself as base64-encoded strings (in order to support
 single-file use of `virtualenv.py` without installing it). If your patch
 changes any file in `virtualenv_embedded/`, run `bin/rebuild-script.py` to
 update the embedded version of that file in `virtualenv.py`; commit that and
 submit it as part of your patch / pull request.
 
-.. _pip development: http://www.pip-installer.org/en/latest/development.html
+.. _pip development: https://pip.pypa.io/en/latest/development/
 .. _virtualenv repo: https://github.com/pypa/virtualenv/
 
 Running the tests
 -----------------
 
 Virtualenv's test suite is small and not yet at all comprehensive, but we aim
 to grow it.
 
--- a/testing/mozharness/external_tools/virtualenv/docs/installation.rst
+++ b/testing/mozharness/external_tools/virtualenv/docs/installation.rst
@@ -22,17 +22,17 @@ To install globally with `pip` (if you h
 ::
 
  $ [sudo] pip install virtualenv
 
 Or to get the latest unreleased dev version:
 
 ::
 
- $ [sudo] pip install https://github.com/pypa/virtualenv/tarball/develop
+ $ [sudo] pip install https://github.com/pypa/virtualenv/tarball/master
 
 
 To install version X.X globally from source:
 
 ::
 
  $ curl -O https://pypi.python.org/packages/source/v/virtualenv/virtualenv-X.X.tar.gz
  $ tar xvfz virtualenv-X.X.tar.gz
--- a/testing/mozharness/external_tools/virtualenv/docs/userguide.rst
+++ b/testing/mozharness/external_tools/virtualenv/docs/userguide.rst
@@ -107,17 +107,17 @@ below.
         and is not trusted on your system. Only run scripts from trusted publishers.
         [V] Never run  [D] Do not run  [R] Run once  [A] Always run  [?] Help
         (default is "D"):A
         (foo) PS C:\>
 
     If you select ``[A] Always Run``, the certificate will be added to the
     Trusted Publishers of your user account, and will be trusted in this
     user's context henceforth. If you select ``[R] Run Once``, the script will
-    be run, but you will be prometed on a subsequent invocation. Advanced users
+    be run, but you will be prompted on a subsequent invocation. Advanced users
     can add the signer's certificate to the Trusted Publishers of the Computer
     account to apply to all users (though this technique is out of scope of this
     document).
 
     Alternatively, you may relax the system execution policy to allow running
     of local scripts without verifying the code signature using the following::
 
         PS C:\> Set-ExecutionPolicy RemoteSigned
--- a/testing/mozharness/external_tools/virtualenv/setup.cfg
+++ b/testing/mozharness/external_tools/virtualenv/setup.cfg
@@ -1,8 +1,7 @@
 [bdist_wheel]
 universal = 1
 
 [egg_info]
+tag_build = 
 tag_date = 0
-tag_build = 
-tag_svn_revision = 0
 
--- a/testing/mozharness/external_tools/virtualenv/setup.py
+++ b/testing/mozharness/external_tools/virtualenv/setup.py
@@ -101,19 +101,19 @@ setup(
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: MIT License',
         'Programming Language :: Python :: 2',
         'Programming Language :: Python :: 2.6',
         'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.3',
         'Programming Language :: Python :: 3.4',
         'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
     ],
     keywords='setuptools deployment installation distutils',
     author='Ian Bicking',
     author_email='ianb@colorstudy.com',
     maintainer='Jannis Leidel, Carl Meyer and Brian Rosner',
     maintainer_email='python-virtualenv@groups.google.com',
     url='https://virtualenv.pypa.io/',
     license='MIT',
deleted file mode 100644
--- a/testing/mozharness/external_tools/virtualenv/site.py
+++ /dev/null
@@ -1,760 +0,0 @@
-"""Append module search paths for third-party packages to sys.path.
-
-****************************************************************
-* This module is automatically imported during initialization. *
-****************************************************************
-
-In earlier versions of Python (up to 1.5a3), scripts or modules that
-needed to use site-specific modules would place ``import site''
-somewhere near the top of their code.  Because of the automatic
-import, this is no longer necessary (but code that does it still
-works).
-
-This will append site-specific paths to the module search path.  On
-Unix, it starts with sys.prefix and sys.exec_prefix (if different) and
-appends lib/python<version>/site-packages as well as lib/site-python.
-It also supports the Debian convention of
-lib/python<version>/dist-packages.  On other platforms (mainly Mac and
-Windows), it uses just sys.prefix (and sys.exec_prefix, if different,
-but this is unlikely).  The resulting directories, if they exist, are
-appended to sys.path, and also inspected for path configuration files.
-
-FOR DEBIAN, this sys.path is augmented with directories in /usr/local.
-Local addons go into /usr/local/lib/python<version>/site-packages
-(resp. /usr/local/lib/site-python), Debian addons install into
-/usr/{lib,share}/python<version>/dist-packages.
-
-A path configuration file is a file whose name has the form
-<package>.pth; its contents are additional directories (one per line)
-to be added to sys.path.  Non-existing directories (or
-non-directories) are never added to sys.path; no directory is added to
-sys.path more than once.  Blank lines and lines beginning with
-'#' are skipped. Lines starting with 'import' are executed.
-
-For example, suppose sys.prefix and sys.exec_prefix are set to
-/usr/local and there is a directory /usr/local/lib/python2.X/site-packages
-with three subdirectories, foo, bar and spam, and two path
-configuration files, foo.pth and bar.pth.  Assume foo.pth contains the
-following:
-
-  # foo package configuration
-  foo
-  bar
-  bletch
-
-and bar.pth contains:
-
-  # bar package configuration
-  bar
-
-Then the following directories are added to sys.path, in this order:
-
-  /usr/local/lib/python2.X/site-packages/bar
-  /usr/local/lib/python2.X/site-packages/foo
-
-Note that bletch is omitted because it doesn't exist; bar precedes foo
-because bar.pth comes alphabetically before foo.pth; and spam is
-omitted because it is not mentioned in either path configuration file.
-
-After these path manipulations, an attempt is made to import a module
-named sitecustomize, which can perform arbitrary additional
-site-specific customizations.  If this import fails with an
-ImportError exception, it is silently ignored.
-
-"""
-
-import sys
-import os
-
-try:
-    import __builtin__ as builtins
-except ImportError:
-    import builtins
-try:
-    set
-except NameError:
-    from sets import Set as set
-
-# Prefixes for site-packages; add additional prefixes like /usr/local here
-PREFIXES = [sys.prefix, sys.exec_prefix]
-# Enable per user site-packages directory
-# set it to False to disable the feature or True to force the feature
-ENABLE_USER_SITE = None
-# for distutils.commands.install
-USER_SITE = None
-USER_BASE = None
-
-_is_64bit = (getattr(sys, 'maxsize', None) or getattr(sys, 'maxint')) > 2**32
-_is_pypy = hasattr(sys, 'pypy_version_info')
-_is_jython = sys.platform[:4] == 'java'
-if _is_jython:
-    ModuleType = type(os)
-
-def makepath(*paths):
-    dir = os.path.join(*paths)
-    if _is_jython and (dir == '__classpath__' or
-                       dir.startswith('__pyclasspath__')):
-        return dir, dir
-    dir = os.path.abspath(dir)
-    return dir, os.path.normcase(dir)
-
-def abs__file__():
-    """Set all module' __file__ attribute to an absolute path"""
-    for m in sys.modules.values():
-        if ((_is_jython and not isinstance(m, ModuleType)) or
-            hasattr(m, '__loader__')):
-            # only modules need the abspath in Jython. and don't mess
-            # with a PEP 302-supplied __file__
-            continue
-        f = getattr(m, '__file__', None)
-        if f is None:
-            continue
-        m.__file__ = os.path.abspath(f)
-
-def removeduppaths():
-    """ Remove duplicate entries from sys.path along with making them
-    absolute"""
-    # This ensures that the initial path provided by the interpreter contains
-    # only absolute pathnames, even if we're running from the build directory.
-    L = []
-    known_paths = set()
-    for dir in sys.path:
-        # Filter out duplicate paths (on case-insensitive file systems also
-        # if they only differ in case); turn relative paths into absolute
-        # paths.
-        dir, dircase = makepath(dir)
-        if not dircase in known_paths:
-            L.append(dir)
-            known_paths.add(dircase)
-    sys.path[:] = L
-    return known_paths
-
-# XXX This should not be part of site.py, since it is needed even when
-# using the -S option for Python.  See http://www.python.org/sf/586680
-def addbuilddir():
-    """Append ./build/lib.<platform> in case we're running in the build dir
-    (especially for Guido :-)"""
-    from distutils.util import get_platform
-    s = "build/lib.%s-%.3s" % (get_platform(), sys.version)
-    if hasattr(sys, 'gettotalrefcount'):
-        s += '-pydebug'
-    s = os.path.join(os.path.dirname(sys.path[-1]), s)
-    sys.path.append(s)
-
-def _init_pathinfo():
-    """Return a set containing all existing directory entries from sys.path"""
-    d = set()
-    for dir in sys.path:
-        try:
-            if os.path.isdir(dir):
-                dir, dircase = makepath(dir)
-                d.add(dircase)
-        except TypeError:
-            continue
-    return d
-
-def addpackage(sitedir, name, known_paths):
-    """Add a new path to known_paths by combining sitedir and 'name' or execute
-    sitedir if it starts with 'import'"""
-    if known_paths is None:
-        _init_pathinfo()
-        reset = 1
-    else:
-        reset = 0
-    fullname = os.path.join(sitedir, name)
-    try:
-        f = open(fullname, "rU")
-    except IOError:
-        return
-    try:
-        for line in f:
-            if line.startswith("#"):
-                continue
-            if line.startswith("import"):
-                exec(line)
-                continue
-            line = line.rstrip()
-            dir, dircase = makepath(sitedir, line)
-            if not dircase in known_paths and os.path.exists(dir):
-                sys.path.append(dir)
-                known_paths.add(dircase)
-    finally:
-        f.close()
-    if reset:
-        known_paths = None
-    return known_paths
-
-def addsitedir(sitedir, known_paths=None):
-    """Add 'sitedir' argument to sys.path if missing and handle .pth files in
-    'sitedir'"""
-    if known_paths is None:
-        known_paths = _init_pathinfo()
-        reset = 1
-    else:
-        reset = 0
-    sitedir, sitedircase = makepath(sitedir)
-    if not sitedircase in known_paths:
-        sys.path.append(sitedir)        # Add path component
-    try:
-        names = os.listdir(sitedir)
-    except os.error:
-        return
-    names.sort()
-    for name in names:
-        if name.endswith(os.extsep + "pth"):
-            addpackage(sitedir, name, known_paths)
-    if reset:
-        known_paths = None
-    return known_paths
-
-def addsitepackages(known_paths, sys_prefix=sys.prefix, exec_prefix=sys.exec_prefix):
-    """Add site-packages (and possibly site-python) to sys.path"""
-    prefixes = [os.path.join(sys_prefix, "local"), sys_prefix]
-    if exec_prefix != sys_prefix:
-        prefixes.append(os.path.join(exec_prefix, "local"))
-
-    for prefix in prefixes:
-        if prefix:
-            if sys.platform in ('os2emx', 'riscos') or _is_jython:
-                sitedirs = [os.path.join(prefix, "Lib", "site-packages")]
-            elif _is_pypy:
-                sitedirs = [os.path.join(prefix, 'site-packages')]
-            elif sys.platform == 'darwin' and prefix == sys_prefix:
-
-                if prefix.startswith("/System/Library/Frameworks/"): # Apple's Python
-
-                    sitedirs = [os.path.join("/Library/Python", sys.version[:3], "site-packages"),
-                                os.path.join(prefix, "Extras", "lib", "python")]
-
-                else: # any other Python distros on OSX work this way
-                    sitedirs = [os.path.join(prefix, "lib",
-                                             "python" + sys.version[:3], "site-packages")]
-
-            elif os.sep == '/':
-                sitedirs = [os.path.join(prefix,
-                                         "lib",
-                                         "python" + sys.version[:3],
-                                         "site-packages"),
-                            os.path.join(prefix, "lib", "site-python"),
-                            os.path.join(prefix, "python" + sys.version[:3], "lib-dynload")]
-                lib64_dir = os.path.join(prefix, "lib64", "python" + sys.version[:3], "site-packages")
-                if (os.path.exists(lib64_dir) and
-                    os.path.realpath(lib64_dir) not in [os.path.realpath(p) for p in sitedirs]):
-                    if _is_64bit:
-                        sitedirs.insert(0, lib64_dir)
-                    else:
-                        sitedirs.append(lib64_dir)
-                try:
-                    # sys.getobjects only available in --with-pydebug build
-                    sys.getobjects
-                    sitedirs.insert(0, os.path.join(sitedirs[0], 'debug'))
-                except AttributeError:
-                    pass
-                # Debian-specific dist-packages directories:
-                sitedirs.append(os.path.join(prefix, "local/lib",
-                                             "python" + sys.version[:3],
-                                             "dist-packages"))
-                if sys.version[0] == '2':
-                    sitedirs.append(os.path.join(prefix, "lib",
-                                                 "python" + sys.version[:3],
-                                                 "dist-packages"))
-                else:
-                    sitedirs.append(os.path.join(prefix, "lib",
-                                                 "python" + sys.version[0],
-                                                 "dist-packages"))
-                sitedirs.append(os.path.join(prefix, "lib", "dist-python"))
-            else:
-                sitedirs = [prefix, os.path.join(prefix, "lib", "site-packages")]
-            if sys.platform == 'darwin':
-                # for framework builds *only* we add the standard Apple
-                # locations. Currently only per-user, but /Library and
-                # /Network/Library could be added too
-                if 'Python.framework' in prefix:
-                    home = os.environ.get('HOME')
-                    if home:
-                        sitedirs.append(
-                            os.path.join(home,
-                                         'Library',
-                                         'Python',
-                                         sys.version[:3],
-                                         'site-packages'))
-            for sitedir in sitedirs:
-                if os.path.isdir(sitedir):
-                    addsitedir(sitedir, known_paths)
-    return None
-
-def check_enableusersite():
-    """Check if user site directory is safe for inclusion
-
-    The function tests for the command line flag (including environment var),
-    process uid/gid equal to effective uid/gid.
-
-    None: Disabled for security reasons
-    False: Disabled by user (command line option)
-    True: Safe and enabled
-    """
-    if hasattr(sys, 'flags') and getattr(sys.flags, 'no_user_site', False):
-        return False
-
-    if hasattr(os, "getuid") and hasattr(os, "geteuid"):
-        # check process uid == effective uid
-        if os.geteuid() != os.getuid():
-            return None
-    if hasattr(os, "getgid") and hasattr(os, "getegid"):
-        # check process gid == effective gid
-        if os.getegid() != os.getgid():
-            return None
-
-    return True
-
-def addusersitepackages(known_paths):
-    """Add a per user site-package to sys.path
-
-    Each user has its own python directory with site-packages in the
-    home directory.
-
-    USER_BASE is the root directory for all Python versions
-
-    USER_SITE is the user specific site-packages directory
-
-    USER_SITE/.. can be used for data.
-    """
-    global USER_BASE, USER_SITE, ENABLE_USER_SITE
-    env_base = os.environ.get("PYTHONUSERBASE", None)
-
-    def joinuser(*args):
-        return os.path.expanduser(os.path.join(*args))
-
-    #if sys.platform in ('os2emx', 'riscos'):
-    #    # Don't know what to put here
-    #    USER_BASE = ''
-    #    USER_SITE = ''
-    if os.name == "nt":
-        base = os.environ.get("APPDATA") or "~"
-        if env_base:
-            USER_BASE = env_base
-        else:
-            USER_BASE = joinuser(base, "Python")
-        USER_SITE = os.path.join(USER_BASE,
-                                 "Python" + sys.version[0] + sys.version[2],
-                                 "site-packages")
-    else:
-        if env_base:
-            USER_BASE = env_base
-        else:
-            USER_BASE = joinuser("~", ".local")
-        USER_SITE = os.path.join(USER_BASE, "lib",
-                                 "python" + sys.version[:3],
-                                 "site-packages")
-
-    if ENABLE_USER_SITE and os.path.isdir(USER_SITE):
-        addsitedir(USER_SITE, known_paths)
-    if ENABLE_USER_SITE:
-        for dist_libdir in ("lib", "local/lib"):
-            user_site = os.path.join(USER_BASE, dist_libdir,
-                                     "python" + sys.version[:3],
-                                     "dist-packages")
-            if os.path.isdir(user_site):
-                addsitedir(user_site, known_paths)
-    return known_paths
-
-
-
-def setBEGINLIBPATH():
-    """The OS/2 EMX port has optional extension modules that do double duty
-    as DLLs (and must use the .DLL file extension) for other extensions.
-    The library search path needs to be amended so these will be found
-    during module import.  Use BEGINLIBPATH so that these are at the start
-    of the library search path.
-
-    """
-    dllpath = os.path.join(sys.prefix, "Lib", "lib-dynload")
-    libpath = os.environ['BEGINLIBPATH'].split(';')
-    if libpath[-1]:
-        libpath.append(dllpath)
-    else:
-        libpath[-1] = dllpath
-    os.environ['BEGINLIBPATH'] = ';'.join(libpath)
-
-
-def setquit():
-    """Define new built-ins 'quit' and 'exit'.
-    These are simply strings that display a hint on how to exit.
-
-    """
-    if os.sep == ':':
-        eof = 'Cmd-Q'
-    elif os.sep == '\\':
-        eof = 'Ctrl-Z plus Return'
-    else:
-        eof = 'Ctrl-D (i.e. EOF)'
-
-    class Quitter(object):
-        def __init__(self, name):
-            self.name = name
-        def __repr__(self):
-            return 'Use %s() or %s to exit' % (self.name, eof)
-        def __call__(self, code=None):
-            # Shells like IDLE catch the SystemExit, but listen when their
-            # stdin wrapper is closed.
-            try:
-                sys.stdin.close()
-            except:
-                pass
-            raise SystemExit(code)
-    builtins.quit = Quitter('quit')
-    builtins.exit = Quitter('exit')
-
-
-class _Printer(object):
-    """interactive prompt objects for printing the license text, a list of
-    contributors and the copyright notice."""
-
-    MAXLINES = 23
-
-    def __init__(self, name, data, files=(), dirs=()):
-        self.__name = name
-        self.__data = data
-        self.__files = files
-        self.__dirs = dirs
-        self.__lines = None
-
-    def __setup(self):
-        if self.__lines:
-            return
-        data = None
-        for dir in self.__dirs:
-            for filename in self.__files:
-                filename = os.path.join(dir, filename)
-                try:
-                    fp = open(filename, "rU")
-                    data = fp.read()
-                    fp.close()
-                    break
-                except IOError:
-                    pass
-            if data:
-                break
-        if not data:
-            data = self.__data
-        self.__lines = data.split('\n')
-        self.__linecnt = len(self.__lines)
-
-    def __repr__(self):
-        self.__setup()
-        if len(self.__lines) <= self.MAXLINES:
-            return "\n".join(self.__lines)
-        else:
-            return "Type %s() to see the full %s text" % ((self.__name,)*2)
-
-    def __call__(self):
-        self.__setup()
-        prompt = 'Hit Return for more, or q (and Return) to quit: '
-        lineno = 0
-        while 1:
-            try:
-                for i in range(lineno, lineno + self.MAXLINES):
-                    print(self.__lines[i])
-            except IndexError:
-                break
-            else:
-                lineno += self.MAXLINES
-                key = None
-                while key is None:
-                    try:
-                        key = raw_input(prompt)
-                    except NameError:
-                        key = input(prompt)
-                    if key not in ('', 'q'):
-                        key = None
-                if key == 'q':
-                    break
-
-def setcopyright():
-    """Set 'copyright' and 'credits' in __builtin__"""
-    builtins.copyright = _Printer("copyright", sys.copyright)
-    if _is_jython:
-        builtins.credits = _Printer(
-            "credits",
-            "Jython is maintained by the Jython developers (www.jython.org).")
-    elif _is_pypy:
-        builtins.credits = _Printer(
-            "credits",
-            "PyPy is maintained by the PyPy developers: http://pypy.org/")
-    else:
-        builtins.credits = _Printer("credits", """\
-    Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
-    for supporting Python development.  See www.python.org for more information.""")
-    here = os.path.dirname(os.__file__)
-    builtins.license = _Printer(
-        "license", "See http://www.python.org/%.3s/license.html" % sys.version,
-        ["LICENSE.txt", "LICENSE"],
-        [os.path.join(here, os.pardir), here, os.curdir])
-
-
-class _Helper(object):
-    """Define the built-in 'help'.
-    This is a wrapper around pydoc.help (with a twist).
-
-    """
-
-    def __repr__(self):
-        return "Type help() for interactive help, " \
-               "or help(object) for help about object."
-    def __call__(self, *args, **kwds):
-        import pydoc
-        return pydoc.help(*args, **kwds)
-
-def sethelper():
-    builtins.help = _Helper()
-
-def aliasmbcs():
-    """On Windows, some default encodings are not provided by Python,
-    while they are always available as "mbcs" in each locale. Make
-    them usable by aliasing to "mbcs" in such a case."""
-    if sys.platform == 'win32':
-        import locale, codecs
-        enc = locale.getdefaultlocale()[1]
-        if enc.startswith('cp'):            # "cp***" ?
-            try:
-                codecs.lookup(enc)
-            except LookupError:
-                import encodings
-                encodings._cache[enc] = encodings._unknown
-                encodings.aliases.aliases[enc] = 'mbcs'
-
-def setencoding():
-    """Set the string encoding used by the Unicode implementation.  The
-    default is 'ascii', but if you're willing to experiment, you can
-    change this."""
-    encoding = "ascii" # Default value set by _PyUnicode_Init()
-    if 0:
-        # Enable to support locale aware default string encodings.
-        import locale
-        loc = locale.getdefaultlocale()
-        if loc[1]:
-            encoding = loc[1]
-    if 0:
-        # Enable to switch off string to Unicode coercion and implicit
-        # Unicode to string conversion.
-        encoding = "undefined"
-    if encoding != "ascii":
-        # On Non-Unicode builds this will raise an AttributeError...
-        sys.setdefaultencoding(encoding) # Needs Python Unicode build !
-
-
-def execsitecustomize():
-    """Run custom site specific code, if available."""
-    try:
-        import sitecustomize
-    except ImportError:
-        pass
-
-def virtual_install_main_packages():
-    f = open(os.path.join(os.path.dirname(__file__), 'orig-prefix.txt'))
-    sys.real_prefix = f.read().strip()
-    f.close()
-    pos = 2
-    hardcoded_relative_dirs = []
-    if sys.path[0] == '':
-        pos += 1
-    if _is_jython:
-        paths = [os.path.join(sys.real_prefix, 'Lib')]
-    elif _is_pypy:
-        if sys.version_info > (3, 2):
-            cpyver = '%d' % sys.version_info[0]
-        elif sys.pypy_version_info >= (1, 5):
-            cpyver = '%d.%d' % sys.version_info[:2]
-        else:
-            cpyver = '%d.%d.%d' % sys.version_info[:3]
-        paths = [os.path.join(sys.real_prefix, 'lib_pypy'),
-                 os.path.join(sys.real_prefix, 'lib-python', cpyver)]
-        if sys.pypy_version_info < (1, 9):
-            paths.insert(1, os.path.join(sys.real_prefix,
-                                         'lib-python', 'modified-%s' % cpyver))
-        hardcoded_relative_dirs = paths[:] # for the special 'darwin' case below
-        #
-        # This is hardcoded in the Python executable, but relative to sys.prefix:
-        for path in paths[:]:
-            plat_path = os.path.join(path, 'plat-%s' % sys.platform)
-            if os.path.exists(plat_path):
-                paths.append(plat_path)
-    # MOZ: The MSYS2 and MinGW versions of Python have their main packages in the UNIX directory this checks specifically for the native win32 python
-    elif sys.platform == 'win32' and os.sep == '\\':
-        paths = [os.path.join(sys.real_prefix, 'Lib'), os.path.join(sys.real_prefix, 'DLLs')]
-    else:
-        paths = [os.path.join(sys.real_prefix, 'lib', 'python'+sys.version[:3])]
-        hardcoded_relative_dirs = paths[:] # for the special 'darwin' case below
-        lib64_path = os.path.join(sys.real_prefix, 'lib64', 'python'+sys.version[:3])
-        if os.path.exists(lib64_path):
-            if _is_64bit:
-                paths.insert(0, lib64_path)
-            else:
-                paths.append(lib64_path)
-        # This is hardcoded in the Python executable, but relative to
-        # sys.prefix.  Debian change: we need to add the multiarch triplet
-        # here, which is where the real stuff lives.  As per PEP 421, in
-        # Python 3.3+, this lives in sys.implementation, while in Python 2.7
-        # it lives in sys.
-        try:
-            arch = getattr(sys, 'implementation', sys)._multiarch
-        except AttributeError:
-            # This is a non-multiarch aware Python.  Fallback to the old way.
-            arch = sys.platform
-        plat_path = os.path.join(sys.real_prefix, 'lib',
-                                 'python'+sys.version[:3],
-                                 'plat-%s' % arch)
-        if os.path.exists(plat_path):
-            paths.append(plat_path)
-    # This is hardcoded in the Python executable, but
-    # relative to sys.prefix, so we have to fix up:
-    for path in list(paths):
-        tk_dir = os.path.join(path, 'lib-tk')
-        if os.path.exists(tk_dir):
-            paths.append(tk_dir)
-
-    # These are hardcoded in the Apple's Python executable,
-    # but relative to sys.prefix, so we have to fix them up:
-    if sys.platform == 'darwin':
-        hardcoded_paths = [os.path.join(relative_dir, module)
-                           for relative_dir in hardcoded_relative_dirs
-                           for module in ('plat-darwin', 'plat-mac', 'plat-mac/lib-scriptpackages')]
-
-        for path in hardcoded_paths:
-            if os.path.exists(path):
-                paths.append(path)
-
-    sys.path.extend(paths)
-
-def force_global_eggs_after_local_site_packages():
-    """
-    Force easy_installed eggs in the global environment to get placed
-    in sys.path after all packages inside the virtualenv.  This
-    maintains the "least surprise" result that packages in the
-    virtualenv always mask global packages, never the other way
-    around.
-
-    """
-    egginsert = getattr(sys, '__egginsert', 0)
-    for i, path in enumerate(sys.path):
-        if i > egginsert and path.startswith(sys.prefix):
-            egginsert = i
-    sys.__egginsert = egginsert + 1
-
-def virtual_addsitepackages(known_paths):
-    force_global_eggs_after_local_site_packages()
-    return addsitepackages(known_paths, sys_prefix=sys.real_prefix)
-
-def fixclasspath():
-    """Adjust the special classpath sys.path entries for Jython. These
-    entries should follow the base virtualenv lib directories.
-    """
-    paths = []
-    classpaths = []
-    for path in sys.path:
-        if path == '__classpath__' or path.startswith('__pyclasspath__'):
-            classpaths.append(path)
-        else:
-            paths.append(path)
-    sys.path = paths
-    sys.path.extend(classpaths)
-
-def execusercustomize():
-    """Run custom user specific code, if available."""
-    try:
-        import usercustomize
-    except ImportError:
-        pass
-
-
-def main():
-    global ENABLE_USER_SITE
-    virtual_install_main_packages()
-    abs__file__()
-    paths_in_sys = removeduppaths()
-    if (os.name == "posix" and sys.path and
-        os.path.basename(sys.path[-1]) == "Modules"):
-        addbuilddir()
-    if _is_jython:
-        fixclasspath()
-    GLOBAL_SITE_PACKAGES = not os.path.exists(os.path.join(os.path.dirname(__file__), 'no-global-site-packages.txt'))
-    if not GLOBAL_SITE_PACKAGES:
-        ENABLE_USER_SITE = False
-    if ENABLE_USER_SITE is None:
-        ENABLE_USER_SITE = check_enableusersite()
-    paths_in_sys = addsitepackages(paths_in_sys)
-    paths_in_sys = addusersitepackages(paths_in_sys)
-    if GLOBAL_SITE_PACKAGES:
-        paths_in_sys = virtual_addsitepackages(paths_in_sys)
-    if sys.platform == 'os2emx':
-        setBEGINLIBPATH()
-    setquit()
-    setcopyright()
-    sethelper()
-    aliasmbcs()
-    setencoding()
-    execsitecustomize()
-    if ENABLE_USER_SITE:
-        execusercustomize()
-    # Remove sys.setdefaultencoding() so that users cannot change the
-    # encoding after initialization.  The test for presence is needed when
-    # this module is run as a script, because this code is executed twice.
-    if hasattr(sys, "setdefaultencoding"):
-        del sys.setdefaultencoding
-
-main()
-
-def _script():
-    help = """\
-    %s [--user-base] [--user-site]
-
-    Without arguments print some useful information
-    With arguments print the value of USER_BASE and/or USER_SITE separated
-    by '%s'.
-
-    Exit codes with --user-base or --user-site:
-      0 - user site directory is enabled
-      1 - user site directory is disabled by user
-      2 - uses site directory is disabled by super user
-          or for security reasons
-     >2 - unknown error
-    """
-    args = sys.argv[1:]
-    if not args:
-        print("sys.path = [")
-        for dir in sys.path:
-            print("    %r," % (dir,))
-        print("]")
-        def exists(path):
-            if os.path.isdir(path):
-                return "exists"
-            else:
-                return "doesn't exist"
-        print("USER_BASE: %r (%s)" % (USER_BASE, exists(USER_BASE)))
-        print("USER_SITE: %r (%s)" % (USER_SITE, exists(USER_BASE)))
-        print("ENABLE_USER_SITE: %r" %  ENABLE_USER_SITE)
-        sys.exit(0)
-
-    buffer = []
-    if '--user-base' in args:
-        buffer.append(USER_BASE)
-    if '--user-site' in args:
-        buffer.append(USER_SITE)
-
-    if buffer:
-        print(os.pathsep.join(buffer))
-        if ENABLE_USER_SITE:
-            sys.exit(0)
-        elif ENABLE_USER_SITE is False:
-            sys.exit(1)
-        elif ENABLE_USER_SITE is None:
-            sys.exit(2)
-        else:
-            sys.exit(3)
-    else:
-        import textwrap
-        print(textwrap.dedent(help % (sys.argv[0], os.pathsep)))
-        sys.exit(10)
-
-if __name__ == '__main__':
-    _script()
--- a/testing/mozharness/external_tools/virtualenv/tests/test_virtualenv.py
+++ b/testing/mozharness/external_tools/virtualenv/tests/test_virtualenv.py
@@ -20,16 +20,17 @@ def test_resolve_interpreter_with_absolu
     """Should return absolute path if given and exists"""
     mock_exists.return_value = True
     virtualenv.is_executable = Mock(return_value=True)
     test_abs_path = os.path.abspath("/usr/bin/python53")
 
     exe = virtualenv.resolve_interpreter(test_abs_path)
 
     assert exe == test_abs_path, "Absolute path should return as is"
+
     mock_exists.assert_called_with(test_abs_path)
     virtualenv.is_executable.assert_called_with(test_abs_path)
 
 
 @patch('os.path.exists')
 def test_resolve_interpreter_with_nonexistent_interpreter(mock_exists):
     """Should SystemExit with an nonexistent python interpreter path"""
     mock_exists.return_value = False
@@ -91,38 +92,39 @@ def test_cop_update_defaults_with_store_
         action='store_false',
         help="Don't give access to the global site-packages dir to the "
              "virtual environment (default)")
 
     defaults = {}
     cop.update_defaults(defaults)
     assert defaults == {'system_site_packages': 0}
 
+
 def test_install_python_bin():
     """Should create the right python executables and links"""
     tmp_virtualenv = tempfile.mkdtemp()
     try:
         home_dir, lib_dir, inc_dir, bin_dir = \
                                 virtualenv.path_locations(tmp_virtualenv)
         virtualenv.install_python(home_dir, lib_dir, inc_dir, bin_dir, False,
                                   False)
 
         if virtualenv.is_win:
-            required_executables = [ 'python.exe', 'pythonw.exe']
+            required_executables = ['python.exe', 'pythonw.exe']
         else:
             py_exe_no_version = 'python'
             py_exe_version_major = 'python%s' % sys.version_info[0]
             py_exe_version_major_minor = 'python%s.%s' % (
                 sys.version_info[0], sys.version_info[1])
-            required_executables = [ py_exe_no_version, py_exe_version_major,
-                                     py_exe_version_major_minor ]
+            required_executables = [py_exe_no_version, py_exe_version_major,
+                                    py_exe_version_major_minor]
 
         for pth in required_executables:
-            assert os.path.exists(os.path.join(bin_dir, pth)), ("%s should "
-                            "exist in bin_dir" % pth)
+            assert os.path.exists(os.path.join(bin_dir, pth)), \
+                   ("%s should exist in bin_dir" % pth)
     finally:
         shutil.rmtree(tmp_virtualenv)
 
 
 @pytest.mark.skipif("platform.python_implementation() == 'PyPy'")
 def test_always_copy_option():
     """Should be no symlinks in directory tree"""
     tmp_virtualenv = tempfile.mkdtemp()
--- a/testing/mozharness/external_tools/virtualenv/virtualenv.py
+++ b/testing/mozharness/external_tools/virtualenv/virtualenv.py
@@ -17,50 +17,50 @@ import base64
 import codecs
 import optparse
 import re
 import shutil
 import logging
 import zlib
 import errno
 import glob
+import distutils.spawn
 import distutils.sysconfig
 import struct
 import subprocess
 import pkgutil
 import tempfile
 import textwrap
 from distutils.util import strtobool
 from os.path import join
 
 try:
     import ConfigParser
 except ImportError:
     import configparser as ConfigParser
 
-__version__ = "15.0.1"
+__version__ = "15.2.0"
 virtualenv_version = __version__  # legacy
 
 if sys.version_info < (2, 6):
     print('ERROR: %s' % sys.exc_info()[1])
     print('ERROR: this script requires Python 2.6 or greater.')
     sys.exit(101)
 
 try:
     basestring
 except NameError:
     basestring = str
 
 py_version = 'python%s.%s' % (sys.version_info[0], sys.version_info[1])
 
 is_jython = sys.platform.startswith('java')
 is_pypy = hasattr(sys, 'pypy_version_info')
-is_win = (sys.platform == 'win32' and os.sep == '\\')
+is_win = (sys.platform == 'win32')
 is_cygwin = (sys.platform == 'cygwin')
-is_msys2 = (sys.platform == 'win32' and os.sep == '/')
 is_darwin = (sys.platform == 'darwin')
 abiflags = getattr(sys, 'abiflags', '')
 
 user_dir = os.path.expanduser('~')
 if is_win:
     default_storage_dir = os.path.join(user_dir, 'virtualenv')
 else:
     default_storage_dir = os.path.join(user_dir, '.virtualenv')
@@ -126,18 +126,16 @@ REQUIRED_MODULES = ['os', 'posix', 'posi
 REQUIRED_FILES = ['lib-dynload', 'config']
 
 majver, minver = sys.version_info[:2]
 if majver == 2:
     if minver >= 6:
         REQUIRED_MODULES.extend(['warnings', 'linecache', '_abcoll', 'abc'])
     if minver >= 7:
         REQUIRED_MODULES.extend(['_weakrefset'])
-    if is_msys2:
-        REQUIRED_MODULES.extend(['functools'])
 elif majver == 3:
     # Some extra modules are needed for Python 3, but different ones
     # for different versions.
     REQUIRED_MODULES.extend([
     	'_abcoll', 'warnings', 'linecache', 'abc', 'io', '_weakrefset',
     	'copyreg', 'tempfile', 'random', '__future__', 'collections',
     	'keyword', 'tarfile', 'shutil', 'struct', 'copy', 'tokenize',
     	'token', 'functools', 'heapq', 'bisect', 'weakref', 'reprlib'
@@ -153,22 +151,29 @@ elif majver == 3:
         	'imp', 'importlib', 'rlcompleter'
         ])
     if minver >= 4:
         REQUIRED_MODULES.extend([
             'operator',
             '_collections_abc',
             '_bootlocale',
         ])
+    if minver >= 6:
+        REQUIRED_MODULES.extend(['enum'])
 
 if is_pypy:
     # these are needed to correctly display the exceptions that may happen
     # during the bootstrap
     REQUIRED_MODULES.extend(['traceback', 'linecache'])
 
+    if majver == 3:
+        # _functools is needed to import locale during stdio initialization and
+        # needs to be copied on PyPy because it's not built in
+        REQUIRED_MODULES.append('_functools')
+
 
 class Logger(object):
 
     """
     Logging object for use in command-line script.  Allows ranges of
     levels, to avoid some redundancy of displayed information.
     """
 
@@ -295,43 +300,62 @@ class Logger(object):
             if start is not None and start > consumer_level:
                 return False
             if stop is not None and stop <= consumer_level:
                 return False
             return True
         else:
             return level >= consumer_level
 
-    #@classmethod
+    @classmethod
     def level_for_integer(cls, level):
         levels = cls.LEVELS
         if level < 0:
             return levels[0]
         if level >= len(levels):
             return levels[-1]
         return levels[level]
 
-    level_for_integer = classmethod(level_for_integer)
-
 # create a silent logger just to prevent this from being undefined
 # will be overridden with requested verbosity main() is called.
 logger = Logger([(Logger.LEVELS[-1], sys.stdout)])
 
 def mkdir(path):
     if not os.path.exists(path):
         logger.info('Creating %s', path)
         os.makedirs(path)
     else:
         logger.info('Directory %s already exists', path)
 
 def copyfileordir(src, dest, symlink=True):
-    if os.path.isdir(src):
-        shutil.copytree(src, dest, symlink)
-    else:
-        shutil.copy2(src, dest)
+    # MOZ HACK
+    # On macOS, we can't set the SF_RESTRICTED stat value. This value isn't
+    # important for virtualenvs (it has to do with system file protection).
+    # So we monkeypatch os.chflags to silently drop this flag and UF_TRACKED.
+    # This mimics the behavior in copyfile_stat() from macOS:
+    # https://opensource.apple.com/source/copyfile/copyfile-146/copyfile.c
+    # .auto.html.
+    orig_chflags = getattr(os, 'chflags', None)
+
+    if orig_chflags:
+        def patched_chflags(path, flags):
+            # SF_RESTRICTED and UF_TRACKED.
+            OMIT_FLAGS = 0x80000 | 0x40
+            return orig_chflags(path, flags & ~OMIT_FLAGS)
+
+        os.chflags = patched_chflags
+
+    try:
+        if os.path.isdir(src):
+            shutil.copytree(src, dest, symlink)
+        else:
+            shutil.copy2(src, dest)
+    finally:
+        if orig_chflags:
+            os.chflags = orig_chflags
 
 def copyfile(src, dest, symlink=True):
     if not os.path.exists(src):
         # Some bad symlink in the src
         logger.warn('Cannot find file %s (bad symlink)', src)
         return
     if os.path.exists(dest):
         logger.debug('File %s already exists', dest)
@@ -531,17 +555,17 @@ def main():
         dest='quiet',
         default=0,
         help='Decrease verbosity.')
 
     parser.add_option(
         '-p', '--python',
         dest='python',
         metavar='PYTHON_EXE',
-        help='The Python interpreter to use, e.g., --python=python2.5 will use the python2.5 '
+        help='The Python interpreter to use, e.g., --python=python3.5 will use the python3.5 '
         'interpreter to create the new environment.  The default is the interpreter that '
         'virtualenv was installed with (%s)' % sys.executable)
 
     parser.add_option(
         '--clear',
         dest='clear',
         action='store_true',
         help="Clear out the non-root install and start from scratch.")
@@ -563,22 +587,16 @@ def main():
     parser.add_option(
         '--always-copy',
         dest='symlink',
         action='store_false',
         default=True,
         help="Always copy files rather than symlinking.")
 
     parser.add_option(
-        '--unzip-setuptools',
-        dest='unzip_setuptools',
-        action='store_true',
-        help="Unzip Setuptools when installing it.")
-
-    parser.add_option(
         '--relocatable',
         dest='relocatable',
         action='store_true',
         help='Make an EXISTING virtualenv environment relocatable. '
              'This fixes up scripts and makes all .pth files relative.')
 
     parser.add_option(
         '--no-setuptools',
@@ -636,16 +654,21 @@ def main():
         help="DEPRECATED. Retained only for backward compatibility. This option has no effect.")
 
     parser.add_option(
         '--distribute',
         dest='distribute',
         action='store_true',
         help="DEPRECATED. Retained only for backward compatibility. This option has no effect.")
 
+    parser.add_option(
+        '--unzip-setuptools',
+        action='store_true',
+        help="DEPRECATED.  Retained only for backward compatibility. This option has no effect.")
+
     if 'extend_parser' in globals():
         extend_parser(parser)
 
     options, args = parser.parse_args()
 
     global logger
 
     if 'adjust_options' in globals():
@@ -696,24 +719,23 @@ def main():
 
     if options.relocatable:
         make_environment_relocatable(home_dir)
         return
 
     create_environment(home_dir,
                        site_packages=options.system_site_packages,
                        clear=options.clear,
-                       unzip_setuptools=options.unzip_setuptools,
                        prompt=options.prompt,
                        search_dirs=options.search_dirs,
                        download=options.download,
                        no_setuptools=options.no_setuptools,
                        no_pip=options.no_pip,
                        no_wheel=options.no_wheel,
-                       symlink=options.symlink and hasattr(os, 'symlink')) # MOZ: Make sure we don't use symlink when we don't have it
+                       symlink=options.symlink)
     if 'after_install' in globals():
         after_install(options, home_dir)
 
 def call_subprocess(cmd, show_stdout=True,
                     filter_stdout=None, cwd=None,
                     raise_on_returncode=True, extra_env=None,
                     remove_from_env=None, stdin=None):
     cmd_parts = []
@@ -852,64 +874,66 @@ def install_wheel(project_names, py_exec
     findlinks = ' '.join(space_path2url(d) for d in search_dirs)
 
     SCRIPT = textwrap.dedent("""
         import sys
         import pkgutil
         import tempfile
         import os
 
-        import pip
+        try:
+            from pip._internal import main as _main
+            cert_data = pkgutil.get_data("pip._vendor.certifi", "cacert.pem")
+        except ImportError:
+            from pip import main as _main
+            cert_data = pkgutil.get_data("pip._vendor.requests", "cacert.pem")
 
-        cert_data = pkgutil.get_data("pip._vendor.requests", "cacert.pem")
         if cert_data is not None:
             cert_file = tempfile.NamedTemporaryFile(delete=False)
             cert_file.write(cert_data)
             cert_file.close()
         else:
             cert_file = None
 
         try:
             args = ["install", "--ignore-installed"]
             if cert_file is not None:
                 args += ["--cert", cert_file.name]
             args += sys.argv[1:]
 
-            sys.exit(pip.main(args))
+            sys.exit(_main(args))
         finally:
             if cert_file is not None:
                 os.remove(cert_file.name)
     """).encode("utf8")
 
     cmd = [py_executable, '-'] + project_names
     logger.start_progress('Installing %s...' % (', '.join(project_names)))
     logger.indent += 2
 
     env = {
         "PYTHONPATH": pythonpath,
         "JYTHONPATH": pythonpath,  # for Jython < 3.x
         "PIP_FIND_LINKS": findlinks,
         "PIP_USE_WHEEL": "1",
         "PIP_ONLY_BINARY": ":all:",
-        "PIP_PRE": "1",
         "PIP_USER": "0",
     }
 
     if not download:
         env["PIP_NO_INDEX"] = "1"
 
     try:
         call_subprocess(cmd, show_stdout=False, extra_env=env, stdin=SCRIPT)
     finally:
         logger.indent -= 2
         logger.end_progress()
 
 
 def create_environment(home_dir, site_packages=False, clear=False,
-                       unzip_setuptools=False,
                        prompt=None, search_dirs=None, download=False,
                        no_setuptools=False, no_pip=False, no_wheel=False,
                        symlink=True):
     """
     Creates a new environment in ``home_dir``.
 
     If ``site_packages`` is true, then the global ``site-packages/``
     directory will be on the path.
@@ -923,23 +947,29 @@ def create_environment(home_dir, site_pa
         home_dir, lib_dir, inc_dir, bin_dir,
         site_packages=site_packages, clear=clear, symlink=symlink))
 
     install_distutils(home_dir)
 
     to_install = []
 
     if not no_setuptools:
-        to_install.append('setuptools')
+        if sys.version_info[:2] == (2, 6):
+            to_install.append('setuptools<37')
+        else:
+            to_install.append('setuptools')
 
     if not no_pip:
         to_install.append('pip')
 
     if not no_wheel:
-        to_install.append('wheel')
+        if sys.version_info[:2] == (2, 6):
+            to_install.append("wheel<0.30")
+        else:
+            to_install.append('wheel')
 
     if to_install:
         install_wheel(
             to_install,
             py_executable,
             search_dirs,
             download=download,
         )
@@ -1060,16 +1090,26 @@ def copy_required_modules(dst_prefix, sy
             else:
                 dst_filename = change_prefix(filename, dst_prefix)
             copyfile(filename, dst_filename, symlink)
             if filename.endswith('.pyc'):
                 pyfile = filename[:-1]
                 if os.path.exists(pyfile):
                     copyfile(pyfile, dst_filename[:-1], symlink)
 
+def copy_tcltk(src, dest, symlink):
+    """ copy tcl/tk libraries on Windows (issue #93) """
+    for libversion in '8.5', '8.6':
+        for libname in 'tcl', 'tk':
+            srcdir = join(src, 'tcl', libname + libversion)
+            destdir = join(dest, 'tcl', libname + libversion)
+            # Only copy the dirs from the above combinations that exist
+            if os.path.exists(srcdir) and not os.path.exists(destdir):
+                copyfileordir(srcdir, destdir, symlink)
+
 
 def subst_path(prefix_path, prefix, home_dir):
     prefix_path = os.path.normpath(prefix_path)
     prefix = os.path.normpath(prefix)
     home_dir = os.path.normpath(home_dir)
     if not prefix_path.startswith(prefix):
         logger.warn('Path not in prefix %r %r', prefix_path, prefix)
         return
@@ -1116,32 +1156,29 @@ def install_python(home_dir, lib_dir, in
             for fn in os.listdir(stdlib_dir):
                 bn = os.path.splitext(fn)[0]
                 if fn != 'site-packages' and bn in REQUIRED_FILES:
                     copyfile(join(stdlib_dir, fn), join(lib_dir, fn), symlink)
         # ...and modules
         copy_required_modules(home_dir, symlink)
     finally:
         logger.indent -= 2
+    # ...copy tcl/tk
+    if is_win:
+        copy_tcltk(prefix, home_dir, symlink)
     mkdir(join(lib_dir, 'site-packages'))
     import site
     site_filename = site.__file__
     if site_filename.endswith('.pyc') or site_filename.endswith('.pyo'):
         site_filename = site_filename[:-1]
     elif site_filename.endswith('$py.class'):
         site_filename = site_filename.replace('$py.class', '.py')
     site_filename_dst = change_prefix(site_filename, home_dir)
     site_dir = os.path.dirname(site_filename_dst)
-    # MOZ: Copies a site.py if it exists instead of using the one hex encoded in
-    # this file. Necessary for some site.py fixes for MinGW64 version of python
-    site_py_src_path = os.path.join(os.path.dirname(__file__), 'site.py')
-    if os.path.isfile(site_py_src_path):
-        shutil.copy(site_py_src_path, site_filename_dst)
-    else:
-        writefile(site_filename_dst, SITE_PY)
+    writefile(site_filename_dst, SITE_PY)
     writefile(join(site_dir, 'orig-prefix.txt'), prefix)
     site_packages_filename = join(site_dir, 'no-global-site-packages.txt')
     if not site_packages:
         writefile(site_packages_filename, '')
 
     if is_pypy or is_win:
         stdinc_dir = join(prefix, 'include')
     else:
@@ -1230,44 +1267,61 @@ def install_python(home_dir, lib_dir, in
             python_d = os.path.join(os.path.dirname(sys.executable), 'python_d.exe')
             python_d_dest = os.path.join(os.path.dirname(py_executable), 'python_d.exe')
             if os.path.exists(python_d):
                 logger.info('Also created python_d.exe')
                 shutil.copyfile(python_d, python_d_dest)
             elif os.path.exists(python_d_dest):
                 logger.info('Removed python_d.exe as it is no longer at the source')
                 os.unlink(python_d_dest)
+
             # we need to copy the DLL to enforce that windows will load the correct one.
             # may not exist if we are cygwin.
-            py_executable_dll = 'python%s%s.dll' % (
-                sys.version_info[0], sys.version_info[1])
-            py_executable_dll_d = 'python%s%s_d.dll' % (
-                sys.version_info[0], sys.version_info[1])
-            pythondll = os.path.join(os.path.dirname(sys.executable), py_executable_dll)
-            pythondll_d = os.path.join(os.path.dirname(sys.executable), py_executable_dll_d)
-            pythondll_d_dest = os.path.join(os.path.dirname(py_executable), py_executable_dll_d)
-            if os.path.exists(pythondll):
-                logger.info('Also created %s' % py_executable_dll)
-                shutil.copyfile(pythondll, os.path.join(os.path.dirname(py_executable), py_executable_dll))
-            if os.path.exists(pythondll_d):
-                logger.info('Also created %s' % py_executable_dll_d)
-                shutil.copyfile(pythondll_d, pythondll_d_dest)
-            elif os.path.exists(pythondll_d_dest):
-                logger.info('Removed %s as the source does not exist' % pythondll_d_dest)
-                os.unlink(pythondll_d_dest)
+            if is_pypy:
+                py_executable_dlls = [
+                    (
+                        'libpypy-c.dll',
+                        'libpypy_d-c.dll',
+                    ),
+                ]
+            else:
+                py_executable_dlls = [
+                    (
+                        'python%s.dll' % (sys.version_info[0]),
+                        'python%s_d.dll' % (sys.version_info[0])
+                    ),
+                    (
+                        'python%s%s.dll' % (sys.version_info[0], sys.version_info[1]),
+                        'python%s%s_d.dll' % (sys.version_info[0], sys.version_info[1])
+                    )
+                ]
+
+            for py_executable_dll, py_executable_dll_d in py_executable_dlls:
+                pythondll = os.path.join(os.path.dirname(sys.executable), py_executable_dll)
+                pythondll_d = os.path.join(os.path.dirname(sys.executable), py_executable_dll_d)
+                pythondll_d_dest = os.path.join(os.path.dirname(py_executable), py_executable_dll_d)
+                if os.path.exists(pythondll):
+                    logger.info('Also created %s' % py_executable_dll)
+                    shutil.copyfile(pythondll, os.path.join(os.path.dirname(py_executable), py_executable_dll))
+                if os.path.exists(pythondll_d):
+                    logger.info('Also created %s' % py_executable_dll_d)
+                    shutil.copyfile(pythondll_d, pythondll_d_dest)
+                elif os.path.exists(pythondll_d_dest):
+                    logger.info('Removed %s as the source does not exist' % pythondll_d_dest)
+                    os.unlink(pythondll_d_dest)
         if is_pypy:
             # make a symlink python --> pypy-c
             python_executable = os.path.join(os.path.dirname(py_executable), 'python')
             if sys.platform in ('win32', 'cygwin'):
                 python_executable += '.exe'
             logger.info('Also created executable %s' % python_executable)
             copyfile(py_executable, python_executable, symlink)
 
             if is_win:
-                for name in ['libexpat.dll', 'libpypy.dll', 'libpypy-c.dll',
+                for name in ['libexpat.dll',
                             'libeay32.dll', 'ssleay32.dll', 'sqlite3.dll',
                             'tcl85.dll', 'tk85.dll']:
                     src = join(prefix, name)
                     if os.path.exists(src):
                         copyfile(src, join(bin_dir, name), symlink)
 
                 for d in sys.path:
                     if d.endswith('lib_pypy'):
@@ -1358,22 +1412,16 @@ def install_python(home_dir, lib_dir, in
             full_pth = join(bin_dir, pth)
             if os.path.exists(full_pth):
                 os.unlink(full_pth)
             if symlink:
                 os.symlink(py_executable_base, full_pth)
             else:
                 copyfile(py_executable, full_pth, symlink)
 
-    if is_win and ' ' in py_executable:
-        # There's a bug with subprocess on Windows when using a first
-        # argument that has a space in it.  Instead we have to quote
-        # the value:
-        py_executable = '"%s"' % py_executable
-    # NOTE: keep this check as one line, cmd.exe doesn't cope with line breaks
     cmd = [py_executable, '-c', 'import sys;out=sys.stdout;'
         'getattr(out, "buffer", out).write(sys.prefix.encode("utf-8"))']
     logger.info('Testing executable with %s %s "%s"' % tuple(cmd))
     try:
         proc = subprocess.Popen(cmd,
                             stdout=subprocess.PIPE)
         proc_stdout, proc_stderr = proc.communicate()
     except OSError:
@@ -1541,37 +1589,34 @@ def fix_lib64(lib_dir, symlink=True):
         copyfile('lib', lib64_link)
 
 def resolve_interpreter(exe):
     """
     If the executable given isn't an absolute path, search $PATH for the interpreter
     """
     # If the "executable" is a version number, get the installed executable for
     # that version
+    orig_exe = exe
     python_versions = get_installed_pythons()
     if exe in python_versions:
         exe = python_versions[exe]
 
     if os.path.abspath(exe) != exe:
-        paths = os.environ.get('PATH', '').split(os.pathsep)
-        for path in paths:
-            if os.path.exists(join(path, exe)):
-                exe = join(path, exe)
-                break
+        exe = distutils.spawn.find_executable(exe) or exe
     if not os.path.exists(exe):
-        logger.fatal('The executable %s (from --python=%s) does not exist' % (exe, exe))
+        logger.fatal('The path %s (from --python=%s) does not exist' % (exe, orig_exe))
         raise SystemExit(3)
     if not is_executable(exe):
-        logger.fatal('The executable %s (from --python=%s) is not executable' % (exe, exe))
+        logger.fatal('The path %s (from --python=%s) is not an executable file' % (exe, orig_exe))
         raise SystemExit(3)
     return exe
 
 def is_executable(exe):
     """Checks a file is executable"""
-    return os.access(exe, os.X_OK)
+    return os.path.isfile(exe) and os.access(exe, os.X_OK)
 
 ############################################################
 ## Relocating the environment:
 
 def make_environment_relocatable(home_dir):
     """
     Makes the already-existing environment use relative paths, and takes out
     the #!-based environment selection in scripts.
@@ -1969,31 +2014,31 @@ 7RBoGtNm9Denv1i0LVI7lxJDXLHSSBeWRflsyyqw
 YOgOu1c91/2cwYpznPPeDoQpGL2xSm09NKp7BsvQ2hnT3aMs07lUnskpxewvBk73/LLnXo9HV9eT
 ijB3hWBO2ygoiWg/bKuZxqCCQq0DD3vkWIVvI2KosIw+vqW1gIItEG5KJb+xb09g65ktwYKgTc51
 uGJ/EFQs0ayEWLCQM5V9N4g+1+8UbXOJzF8bqhKtIqIwicWvzNFROZJlpfD8A7Vc044R0FxkcezG
 VzsV75usvTdYef+57v5n1b225qhXfwEmxHEs
 """)
 
 ##file activate.fish
 ACTIVATE_FISH = convert("""
-eJyFVVFv0zAQfs+vONJO3RDNxCsSQoMVrdK2Vl03CSHkesllMXLsYDvZivjx2GmTOG0YfWhV+7u7
-73z33Y1gnTENKeMIeakNPCKUGhP7xcQTbCJ4ZOKcxoZV1GCUMp1t4O0zMxkTQEGVQjicO4dTyIwp
-Ppyfu386Q86jWOZwBhq1ZlK8jYIRXEoQ0jhDYAYSpjA2fBsFQVoKG0UKSLAJB9MEJrMXi6uYMiXl
-KCrIZYJARQIKTakEGAkmQ+tU5ZSDRTAlRY7CRJMA7GdkgRoNSJ74t1BRxegjR12jWAoGbfpTAeGY
-LK4vycN8tb6/uCbLi/VVWGPcx3maPr2AO4VjYB+HMAxAkQT/i/ptfbW4vVrczAZit3eHDNqL13n0
-Ya+w+Tq/uyLL1eJmuSaLh9lqNb/0+IzgznqnAjAvzBa4jG0BNmNXfdJUkxTU2I6xRaKcy+e6VApz
-WVmoTGFTgwslrYdN03ONrbbMN1E/FQ7H7gOP0UxRjV67TPRBjF3naCMV1mSkYk9MUN7F8cODZzsE
-iIHYviIe6n8WeGQxWKuhl+9Xa49uijq7fehXMRxT9VR9f/8jhDcfYSKkSOyxKp22cNIrIk+nzd2b
-Yc7FNpHx8FUn15ZfzXEE98JxZEohx4r6kosCT+R9ZkHQtLmXGYSEeH8JCTvYkcRgXAutp9Rw7Jmf
-E/J5fktuL25m1tMe3vLdjDt9bNxr2sMo2P3C9BccqGeYhqfQITz6XurXaqdf99LF1mT2YJrvzqCu
-5w7dKvV3PzNyOb+7+Hw923dOuB+AX2SxrZs9Lm0xbCH6kmhjUyuWw+7cC7DX8367H3VzDz6oBtty
-tMIeobE21JT6HaRS+TbaoqhbE7rgdGs3xtE4cOF3xo0TfxwsdyRlhUoxuzes18r+Jp88zDx1G+kd
-/HTrr1BY2CeuyfnbQtAcu9j+pOw6cy9X0k3IuoyKCZPC5ESf6MkgHE5tLiSW3Oa+W2NnrQfkGv/h
-7tR5PNFnMBlw4B9NJTxnzKA9fLTT0aXSb5vw7FUKzcTZPddqYHi2T9/axJmEEN3qHncVCuEPaFmq
-uEtpcBj2Z1wjrqGReJBHrY6/go21NA==
+eJyFVVFv2zYQftevuMoOnBS1gr0WGIZ08RADSRw4boBhGGhGOsUcKFIjKbUu9uN7lC2JsrXWDzZM
+fnf38e6+uwlsdsJCLiRCUVkHrwiVxYy+hHqDbQKvQl3z1ImaO0xyYXdbeP9FuJ1QwMFUSnmcP4dL
+2DlXfry+9v/sDqVMUl3AFVi0Vmj1PokmcKtBaecNQTjIhMHUyX0SRXmlKIpWkGEbDuYZzBZfCVcL
+4youUdVQ6AyBqwwMusoocBrcDsmpKbgEQgijVYHKJbMI6DMhoEUHWmbhLdTcCP4q0TYokYNDev5c
+QTxlq/tb9rJcbz7f3LOnm81d3GD8x3uav30FfwrnwCEOYRyAKot+FvXPzd3q8W71sBiJ3d2dMugu
+fsxjCPsBmz+Wz3fsab16eNqw1ctivV7eBnwm8EzeuQIsSrcHqVMqwHbqq8/aarKSO+oYKhKXUn9p
+SmWw0DVBdQ7bBlwaTR62bc+1tpaYb5PhUyScu48CRgvDLQbtMrMnMQ6dY5022JDRRrwJxWUfJwwP
+ge0YIAVGfcUC1M8s8MxitFZjmR9W64hui7p4fBlWMZ5y81b/9cvfMbz7FWZKq4yOTeW1hbNBEWU+
+b+/ejXMu95lOx696uXb8Go4T+Kw8R2EMSqx5KLkkCkQ+ZBZFbZsHL4OYseAvY3EPO5MYTBuhDZQa
+TwPza8Y+LR/Z483Dgjwd4R3f7bTXx9Znkw6T6PAL83/hRD3jNAKFjuEx9NJkq5t+fabLvdvRwbw4
+nEFTzwO6U+q34cvY7fL55tP94tg58XEA/q7LfdPsaUXFoEIMJdHF5iSW0+48CnDQ82G7n3XzAD6q
+Bmo5XuOA0NQ67ir7AXJtQhtLKO7XhC0l39PGOBsHPvzBuHUSjoOnA0ldozGC9gZ5rek3+y3ALHO/
+kT7AP379lQZLSnFDLtwWihfYxw4nZd+ZR7myfkI2ZTRCuRxmF/bCzkbhcElvYamW9PbDGrvqPKC0
++D/uLi/sFcxGjOHylYagZzzsjjhw206RQwrWIwOxS2dnk+40xOjX8bTPegz/gdWVSXuaowNuOLda
+wYyNuRPSTcd/B48Ppeg=
 """)
 
 ##file activate.csh
 ACTIVATE_CSH = convert("""
 eJx1U2FP2zAQ/e5f8TAV3Soo+0zXbYUiDQkKQgVp2ibjJNfFUuIg22nVf885SVFLO3+I7Lt3fr6X
 d8eY58ZjYQpCWfuAhFB7yrAyIYf0Ve1SQmLsuU6DWepAw9TnEoOFq0rwdjAUx/hV1Ui1tVWAqy1M
 QGYcpaFYx+yVI67LkKwx1UuTEaYGl4X2Bl+zJpAlP/6V2hTDtCq/DYXQhdEeGW040Q/Eb+t9V/e3
 U/V88zh/mtyqh8n8J47G+IKTE3gKZJdoYrK3h5MRU1tGYS83gqNc+3yEgyyP93cP820evHLvr2H8
--- a/testing/mozharness/external_tools/virtualenv/virtualenv_embedded/activate.fish
+++ b/testing/mozharness/external_tools/virtualenv/virtualenv_embedded/activate.fish
@@ -59,17 +59,17 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
         # Save the current $status, for fish_prompts that display it.
         set -l old_status $status
 
         # Prompt override provided?
         # If not, just prepend the environment name.
         if test -n "__VIRTUAL_PROMPT__"
             printf '%s%s' "__VIRTUAL_PROMPT__" (set_color normal)
         else
-            printf '%s(%s%s%s) ' (set_color normal) (set_color -o white) (basename "$VIRTUAL_ENV") (set_color normal)
+            printf '%s(%s) ' (set_color normal) (basename "$VIRTUAL_ENV")
         end
 
         # Restore the original $status
         echo "exit $old_status" | source
         _old_fish_prompt
     end
 
     set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
deleted file mode 100644
index cc49227a0c7e13757f4863a9b7ace1eb56c3ce61..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
copy from third_party/python/virtualenv/virtualenv_support/pip-9.0.3-py2.py3-none-any.whl
copy to testing/mozharness/external_tools/virtualenv/virtualenv_support/pip-9.0.3-py2.py3-none-any.whl
deleted file mode 100644
index 02c8ce8737bea57315cfff3b6b3b1a49eadacc6e..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
copy from third_party/python/virtualenv/virtualenv_support/setuptools-39.0.1-py2.py3-none-any.whl
copy to testing/mozharness/external_tools/virtualenv/virtualenv_support/setuptools-39.0.1-py2.py3-none-any.whl
copy from third_party/python/virtualenv/virtualenv_support/wheel-0.30.0-py2.py3-none-any.whl
copy to testing/mozharness/external_tools/virtualenv/virtualenv_support/wheel-0.30.0-py2.py3-none-any.whl