# HG changeset patch # User ymh # Date 1579915377 -3600 # Node ID a14feef87fd76e03dca85c0bc1f0fbad8fd12910 # Parent a5b9bb4dd63d416483cd5d6b9d77d11d863b661a increment version and upgrade librairies diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/res/lib/lib_create_env.py --- a/virtualenv/res/lib/lib_create_env.py Tue Jan 21 22:11:51 2020 +0100 +++ b/virtualenv/res/lib/lib_create_env.py Sat Jan 25 02:22:57 2020 +0100 @@ -50,10 +50,10 @@ }) else: URLS.update({ - 'PSYCOPG2': {'setup': 'psycopg2','url': 'http://www.psycopg.org/psycopg/tarballs/PSYCOPG-2-4/psycopg2-2.4.5.tar.gz', 'local':"psycopg2-2.4.5.tar.gz"}, + 'PSYCOPG2': {'setup': 'psycopg2','url': 'http://initd.org/psycopg/tarballs/PSYCOPG-2-8/psycopg2-2.8.4.tar.gz', 'local':"psycopg2-2.8.4.tar.gz"}, 'PYLUCENE': {'setup': 'pylucene', 'url': 'http://mirrors.ircam.fr/pub/apache/lucene/pylucene/pylucene-3.6.0-2-src.tar.gz', 'local':"pylucene-3.6.0-2-src.tar.gz"}, 'PIL': {'setup': 'pil', 'url': 'http://effbot.org/downloads/Imaging-1.1.7.tar.gz', 'local':"Imaging-1.1.7.tar.gz"}, - 'LXML': {'setup': 'lxml', 'url':"lxml-2.3.4.tar.bz2", 'local':"lxml-2.3.4.tar.bz2"} + 'LXML': {'setup': 'lxml', 'url':"lxml-4.4.2.tar.gz", 'local':"lxml-4.4.2.tar.gz"} }) diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/res/src/lxml-2.3.4.tar.bz2 Binary file virtualenv/res/src/lxml-2.3.4.tar.bz2 has changed diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/res/src/lxml-4.4.2.tar.gz Binary file virtualenv/res/src/lxml-4.4.2.tar.gz has changed diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/res/src/psycopg2-2.4.5.tar.gz Binary file virtualenv/res/src/psycopg2-2.4.5.tar.gz has changed diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/res/src/psycopg2-2.8.4.tar.gz Binary file virtualenv/res/src/psycopg2-2.8.4.tar.gz has changed diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/web/virtualenv.py --- a/virtualenv/web/virtualenv.py Tue Jan 21 22:11:51 2020 +0100 +++ b/virtualenv/web/virtualenv.py Sat Jan 25 02:22:57 2020 +0100 @@ -1,253 +1,244 @@ #!/usr/bin/env python -"""Create a "virtual" Python installation -""" +"""Create a "virtual" Python installation""" + +# fmt: off +import os # isort:skip +import sys # isort:skip + +# If we are running in a new interpreter to create a virtualenv, +# we do NOT want paths from our existing location interfering with anything, +# So we remove this file's directory from sys.path - most likely to be +# the previous interpreter's site-packages. Solves #705, #763, #779 + -# If you change the version here, change it in setup.py -# and docs/conf.py as well. -virtualenv_version = "1.7.1.2" +if os.environ.get("VIRTUALENV_INTERPRETER_RUNNING"): + for path in sys.path[:]: + if os.path.realpath(os.path.dirname(__file__)) == os.path.realpath(path): + sys.path.remove(path) +# fmt: on +import ast import base64 -import sys +import codecs +import contextlib +import distutils.spawn +import distutils.sysconfig +import errno +import glob +import logging +import optparse import os -import optparse import re import shutil -import logging +import struct +import subprocess +import sys import tempfile +import textwrap +import zipfile import zlib -import errno -import distutils.sysconfig from distutils.util import strtobool - -try: - import subprocess -except ImportError: - if sys.version_info <= (2, 3): - print('ERROR: %s' % sys.exc_info()[1]) - print('ERROR: this script requires Python 2.4 or greater; or at least the subprocess module.') - print('If you copy subprocess.py from a newer version of Python this script will probably work') - sys.exit(101) - else: - raise -try: - set -except NameError: - from sets import Set as set -try: - basestring -except NameError: - basestring = str +from os.path import join try: import ConfigParser except ImportError: + # noinspection PyPep8Naming import configparser as ConfigParser -join = os.path.join -py_version = 'python%s.%s' % (sys.version_info[0], sys.version_info[1]) +__version__ = "16.7.9" +virtualenv_version = __version__ # legacy +DEBUG = os.environ.get("_VIRTUALENV_DEBUG", None) == "1" +if sys.version_info < (2, 7): + print("ERROR: {}".format(sys.exc_info()[1])) + print("ERROR: this script requires Python 2.7 or greater.") + sys.exit(101) -is_jython = sys.platform.startswith('java') -is_pypy = hasattr(sys, 'pypy_version_info') -is_win = (sys.platform == 'win32') -abiflags = getattr(sys, 'abiflags', '') +HERE = os.path.dirname(os.path.abspath(__file__)) +IS_ZIPAPP = os.path.isfile(HERE) + +try: + # noinspection PyUnresolvedReferences,PyUnboundLocalVariable + basestring +except NameError: + basestring = str + +VERSION = "{}.{}".format(*sys.version_info) +PY_VERSION = "python{}.{}".format(*sys.version_info) -user_dir = os.path.expanduser('~') -if sys.platform == 'win32': - user_dir = os.environ.get('APPDATA', user_dir) # Use %APPDATA% for roaming - default_storage_dir = os.path.join(user_dir, 'virtualenv') +IS_PYPY = hasattr(sys, "pypy_version_info") +IS_WIN = sys.platform == "win32" +IS_CYGWIN = sys.platform == "cygwin" +IS_DARWIN = sys.platform == "darwin" +ABI_FLAGS = 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') -default_config_file = os.path.join(default_storage_dir, 'virtualenv.ini') + DEFAULT_STORAGE_DIR = os.path.join(USER_DIR, ".virtualenv") +DEFAULT_CONFIG_FILE = os.path.join(DEFAULT_STORAGE_DIR, "virtualenv.ini") -if is_pypy: - expected_exe = 'pypy' -elif is_jython: - expected_exe = 'jython' +if IS_PYPY: + EXPECTED_EXE = "pypy" else: - expected_exe = 'python' + EXPECTED_EXE = "python" + +# Return a mapping of version -> Python executable +# Only provided for Windows, where the information in the registry is used +if not IS_WIN: + + def get_installed_pythons(): + return {} -REQUIRED_MODULES = ['os', 'posix', 'posixpath', 'nt', 'ntpath', 'genericpath', - 'fnmatch', 'locale', 'encodings', 'codecs', - 'stat', 'UserDict', 'readline', 'copy_reg', 'types', - 're', 'sre', 'sre_parse', 'sre_constants', 'sre_compile', - 'zlib'] +else: + try: + import winreg + except ImportError: + # noinspection PyUnresolvedReferences + import _winreg as winreg + + def get_installed_pythons(): + final_exes = dict() -REQUIRED_FILES = ['lib-dynload', 'config'] + # Grab exes from 32-bit registry view + exes = _get_installed_pythons_for_view("-32", winreg.KEY_WOW64_32KEY) + # Grab exes from 64-bit registry view + exes_64 = _get_installed_pythons_for_view("-64", winreg.KEY_WOW64_64KEY) + # Check if exes are unique + if set(exes.values()) != set(exes_64.values()): + exes.update(exes_64) + + # Create dict with all versions found + for version, bitness in sorted(exes): + exe = exes[(version, bitness)] + # Add minor version (X.Y-32 or X.Y-64) + final_exes[version + bitness] = exe + # Add minor extensionless version (X.Y); 3.2-64 wins over 3.2-32 + final_exes[version] = exe + # Add major version (X-32 or X-64) + final_exes[version[0] + bitness] = exe + # Add major extensionless version (X); 3.3-32 wins over 3.2-64 + final_exes[version[0]] = exe + + return final_exes -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 minver <= 3: - REQUIRED_MODULES.extend(['sets', '__future__']) -elif majver == 3: + def _get_installed_pythons_for_view(bitness, view): + exes = dict() + # If both system and current user installations are found for a + # particular Python version, the current user one is used + for key in (winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER): + try: + python_core = winreg.OpenKey(key, "Software\\Python\\PythonCore", 0, view | winreg.KEY_READ) + except WindowsError: + # No registered Python installations + continue + i = 0 + while True: + try: + version = winreg.EnumKey(python_core, i) + i += 1 + try: + at_path = winreg.QueryValue(python_core, "{}\\InstallPath".format(version)) + except WindowsError: + continue + # Remove bitness from version + if version.endswith(bitness): + version = version[: -len(bitness)] + exes[(version, bitness)] = join(at_path, "python.exe") + except WindowsError: + break + winreg.CloseKey(python_core) + + return exes + + +REQUIRED_MODULES = [ + "os", + "posix", + "posixpath", + "nt", + "ntpath", + "genericpath", + "fnmatch", + "locale", + "encodings", + "codecs", + "stat", + "UserDict", + "readline", + "copy_reg", + "types", + "re", + "sre", + "sre_parse", + "sre_constants", + "sre_compile", + "zlib", +] + +REQUIRED_FILES = ["lib-dynload", "config"] + +MAJOR, MINOR = sys.version_info[:2] +if MAJOR == 2: + if MINOR >= 6: + REQUIRED_MODULES.extend(["warnings", "linecache", "_abcoll", "abc"]) + if MINOR >= 7: + REQUIRED_MODULES.extend(["_weakrefset"]) +elif MAJOR == 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']) - if minver >= 2: - REQUIRED_FILES[-1] = 'config-%s' % majver - if minver == 3: - # The whole list of 3.3 modules is reproduced below - the current - # uncommented ones are required for 3.3 as of now, but more may be - # added as 3.3 development continues. - REQUIRED_MODULES.extend([ - #"aifc", - #"antigravity", - #"argparse", - #"ast", - #"asynchat", - #"asyncore", - "base64", - #"bdb", - #"binhex", - "bisect", - #"calendar", - #"cgi", - #"cgitb", - #"chunk", - #"cmd", - #"codeop", - #"code", - #"colorsys", - #"_compat_pickle", - #"compileall", - #"concurrent", - #"configparser", - #"contextlib", - #"cProfile", - #"crypt", - #"csv", - #"ctypes", - #"curses", - #"datetime", - #"dbm", - #"decimal", - #"difflib", - #"dis", - #"doctest", - #"dummy_threading", - "_dummy_thread", - #"email", - #"filecmp", - #"fileinput", - #"formatter", - #"fractions", - #"ftplib", - #"functools", - #"getopt", - #"getpass", - #"gettext", - #"glob", - #"gzip", - "hashlib", + REQUIRED_MODULES.extend( + [ + "_abcoll", + "warnings", + "linecache", + "abc", + "io", + "_weakrefset", + "copyreg", + "tempfile", + "random", + "__future__", + "collections", + "keyword", + "tarfile", + "shutil", + "struct", + "copy", + "tokenize", + "token", + "functools", "heapq", - "hmac", - #"html", - #"http", - #"idlelib", - #"imaplib", - #"imghdr", - #"importlib", - #"inspect", - #"json", - #"lib2to3", - #"logging", - #"macpath", - #"macurl2path", - #"mailbox", - #"mailcap", - #"_markupbase", - #"mimetypes", - #"modulefinder", - #"multiprocessing", - #"netrc", - #"nntplib", - #"nturl2path", - #"numbers", - #"opcode", - #"optparse", - #"os2emxpath", - #"pdb", - #"pickle", - #"pickletools", - #"pipes", - #"pkgutil", - #"platform", - #"plat-linux2", - #"plistlib", - #"poplib", - #"pprint", - #"profile", - #"pstats", - #"pty", - #"pyclbr", - #"py_compile", - #"pydoc_data", - #"pydoc", - #"_pyio", - #"queue", - #"quopri", + "bisect", + "weakref", "reprlib", - "rlcompleter", - #"runpy", - #"sched", - #"shelve", - #"shlex", - #"smtpd", - #"smtplib", - #"sndhdr", - #"socket", - #"socketserver", - #"sqlite3", - #"ssl", - #"stringprep", - #"string", - #"_strptime", - #"subprocess", - #"sunau", - #"symbol", - #"symtable", - #"sysconfig", - #"tabnanny", - #"telnetlib", - #"test", - #"textwrap", - #"this", - #"_threading_local", - #"threading", - #"timeit", - #"tkinter", - #"tokenize", - #"token", - #"traceback", - #"trace", - #"tty", - #"turtledemo", - #"turtle", - #"unittest", - #"urllib", - #"uuid", - #"uu", - #"wave", - "weakref", - #"webbrowser", - #"wsgiref", - #"xdrlib", - #"xml", - #"xmlrpc", - #"zipfile", - ]) + ] + ) + if MINOR >= 2: + REQUIRED_FILES[-1] = "config-{}".format(MAJOR) + if MINOR >= 3: + import sysconfig -if is_pypy: + platform_dir = sysconfig.get_config_var("PLATDIR") + REQUIRED_FILES.append(platform_dir) + REQUIRED_MODULES.extend(["base64", "_dummy_thread", "hashlib", "hmac", "imp", "importlib", "rlcompleter"]) + if MINOR >= 4: + REQUIRED_MODULES.extend(["operator", "_collections_abc", "_bootlocale"]) + if MINOR >= 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']) + REQUIRED_MODULES.extend(["traceback", "linecache"]) + + if MAJOR == 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): @@ -258,7 +249,7 @@ DEBUG = logging.DEBUG INFO = logging.INFO - NOTIFY = (logging.INFO+logging.WARN)/2 + NOTIFY = (logging.INFO + logging.WARN) / 2 WARN = WARNING = logging.WARN ERROR = logging.ERROR FATAL = logging.FATAL @@ -273,63 +264,66 @@ def debug(self, msg, *args, **kw): self.log(self.DEBUG, msg, *args, **kw) + def info(self, msg, *args, **kw): self.log(self.INFO, msg, *args, **kw) + def notify(self, msg, *args, **kw): self.log(self.NOTIFY, msg, *args, **kw) + def warn(self, msg, *args, **kw): self.log(self.WARN, msg, *args, **kw) + def error(self, msg, *args, **kw): - self.log(self.WARN, msg, *args, **kw) + self.log(self.ERROR, msg, *args, **kw) + def fatal(self, msg, *args, **kw): self.log(self.FATAL, msg, *args, **kw) + def log(self, level, msg, *args, **kw): if args: if kw: - raise TypeError( - "You may give positional or keyword arguments, not both") + raise TypeError("You may give positional or keyword arguments, not both") args = args or kw rendered = None for consumer_level, consumer in self.consumers: if self.level_matches(level, consumer_level): - if (self.in_progress_hanging - and consumer in (sys.stdout, sys.stderr)): + if self.in_progress_hanging and consumer in (sys.stdout, sys.stderr): self.in_progress_hanging = False - sys.stdout.write('\n') + print("") sys.stdout.flush() if rendered is None: if args: rendered = msg % args else: rendered = msg - rendered = ' '*self.indent + rendered - if hasattr(consumer, 'write'): - consumer.write(rendered+'\n') + rendered = " " * self.indent + rendered + if hasattr(consumer, "write"): + consumer.write(rendered + "\n") else: consumer(rendered) def start_progress(self, msg): - assert not self.in_progress, ( - "Tried to start_progress(%r) while in_progress %r" - % (msg, self.in_progress)) + assert not self.in_progress, "Tried to start_progress({!r}) while in_progress {!r}".format( + msg, self.in_progress + ) if self.level_matches(self.NOTIFY, self._stdout_level()): - sys.stdout.write(msg) + print(msg) sys.stdout.flush() self.in_progress_hanging = True else: self.in_progress_hanging = False self.in_progress = msg - def end_progress(self, msg='done.'): - assert self.in_progress, ( - "Tried to end_progress without start_progress") + def end_progress(self, msg="done."): + assert self.in_progress, "Tried to end_progress without start_progress" if self.stdout_level_matches(self.NOTIFY): if not self.in_progress_hanging: # Some message has been printed out since start_progress - sys.stdout.write('...' + self.in_progress + msg + '\n') + print("...{}{}".format(self.in_progress, msg)) sys.stdout.flush() else: - sys.stdout.write(msg + '\n') + print(msg) sys.stdout.flush() self.in_progress = None self.in_progress_hanging = False @@ -338,7 +332,7 @@ """If we are in a progress scope, and no log messages have been shown, write out another '.'""" if self.in_progress_hanging: - sys.stdout.write('.') + print(".") sys.stdout.flush() def stdout_level_matches(self, level): @@ -352,7 +346,8 @@ return level return self.FATAL - def level_matches(self, level, consumer_level): + @staticmethod + def level_matches(level, consumer_level): """ >>> l = Logger([]) >>> l.level_matches(3, 4) @@ -378,7 +373,7 @@ else: return level >= consumer_level - #@classmethod + @classmethod def level_for_integer(cls, level): levels = cls.LEVELS if level < 0: @@ -387,279 +382,123 @@ 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) + +def mkdir(at_path): + if not os.path.exists(at_path): + logger.info("Creating %s", at_path) + os.makedirs(at_path) else: - logger.info('Directory %s already exists', path) + logger.info("Directory %s already exists", at_path) + -def copyfileordir(src, dest): +def copy_file_or_folder(src, dest, symlink=True): if os.path.isdir(src): - shutil.copytree(src, dest, True) + shutil.copytree(src, dest, symlink) else: shutil.copy2(src, dest) + 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) + logger.warn("Cannot find file %s (bad symlink)", src) return if os.path.exists(dest): - logger.debug('File %s already exists', dest) + logger.debug("File %s already exists", dest) return if not os.path.exists(os.path.dirname(dest)): - logger.info('Creating parent directories for %s' % os.path.dirname(dest)) + logger.info("Creating parent directories for %s", os.path.dirname(dest)) os.makedirs(os.path.dirname(dest)) - if not os.path.islink(src): - srcpath = os.path.abspath(src) + if symlink and hasattr(os, "symlink") and not IS_WIN: + logger.info("Symlinking %s", dest) + try: + os.symlink(os.path.realpath(src), dest) + except (OSError, NotImplementedError): + logger.info("Symlinking failed, copying to %s", dest) + copy_file_or_folder(src, dest, symlink) else: - srcpath = os.readlink(src) - if symlink and hasattr(os, 'symlink') and not is_win: - logger.info('Symlinking %s', dest) - try: - os.symlink(srcpath, dest) - except (OSError, NotImplementedError): - logger.info('Symlinking failed, copying to %s', dest) - copyfileordir(src, dest) - else: - logger.info('Copying to %s', dest) - copyfileordir(src, dest) + logger.info("Copying to %s", dest) + copy_file_or_folder(src, dest, symlink) + def writefile(dest, content, overwrite=True): if not os.path.exists(dest): - logger.info('Writing %s', dest) - f = open(dest, 'wb') - f.write(content.encode('utf-8')) - f.close() + logger.info("Writing %s", dest) + with open(dest, "wb") as f: + f.write(content.encode("utf-8")) return else: - f = open(dest, 'rb') - c = f.read() - f.close() - if c != content: + with open(dest, "rb") as f: + c = f.read() + if c != content.encode("utf-8"): if not overwrite: - logger.notify('File %s exists with different content; not overwriting', dest) + logger.notify("File %s exists with different content; not overwriting", dest) return - logger.notify('Overwriting %s with new content', dest) - f = open(dest, 'wb') - f.write(content.encode('utf-8')) - f.close() + logger.notify("Overwriting %s with new content", dest) + with open(dest, "wb") as f: + f.write(content.encode("utf-8")) else: - logger.info('Content %s already in place', dest) + logger.info("Content %s already in place", dest) + -def rmtree(dir): - if os.path.exists(dir): - logger.notify('Deleting tree %s', dir) - shutil.rmtree(dir) +def rm_tree(folder): + if os.path.exists(folder): + logger.notify("Deleting tree %s", folder) + shutil.rmtree(folder) else: - logger.info('Do not need to delete %s; already gone', dir) + logger.info("Do not need to delete %s; already gone", folder) + def make_exe(fn): - if hasattr(os, 'chmod'): - oldmode = os.stat(fn).st_mode & 0xFFF # 0o7777 - newmode = (oldmode | 0x16D) & 0xFFF # 0o555, 0o7777 - os.chmod(fn, newmode) - logger.info('Changed mode of %s to %s', fn, oct(newmode)) + if hasattr(os, "chmod"): + old_mode = os.stat(fn).st_mode & 0xFFF # 0o7777 + new_mode = (old_mode | 0x16D) & 0xFFF # 0o555, 0o7777 + os.chmod(fn, new_mode) + logger.info("Changed mode of %s to %s", fn, oct(new_mode)) -def _find_file(filename, dirs): - for dir in reversed(dirs): - if os.path.exists(join(dir, filename)): - return join(dir, filename) - return filename - -def _install_req(py_executable, unzip=False, distribute=False, - search_dirs=None, never_download=False): - - if search_dirs is None: - search_dirs = file_search_dirs() - if not distribute: - setup_fn = 'setuptools-0.6c11-py%s.egg' % sys.version[:3] - project_name = 'setuptools' - bootstrap_script = EZ_SETUP_PY - source = None - else: - setup_fn = None - source = 'distribute-0.6.24.tar.gz' - project_name = 'distribute' - bootstrap_script = DISTRIBUTE_SETUP_PY - - if setup_fn is not None: - setup_fn = _find_file(setup_fn, search_dirs) +def _find_file(filename, folders): + for folder in reversed(folders): + files = glob.glob(os.path.join(folder, filename)) + if files and os.path.isfile(files[0]): + return True, files[0] + return False, filename - if source is not None: - source = _find_file(source, search_dirs) - - if is_jython and os._name == 'nt': - # Jython's .bat sys.executable can't handle a command line - # argument with newlines - fd, ez_setup = tempfile.mkstemp('.py') - os.write(fd, bootstrap_script) - os.close(fd) - cmd = [py_executable, ez_setup] - else: - cmd = [py_executable, '-c', bootstrap_script] - if unzip: - cmd.append('--always-unzip') - env = {} - remove_from_env = [] - if logger.stdout_level_matches(logger.DEBUG): - cmd.append('-v') - old_chdir = os.getcwd() - if setup_fn is not None and os.path.exists(setup_fn): - logger.info('Using existing %s egg: %s' % (project_name, setup_fn)) - cmd.append(setup_fn) - if os.environ.get('PYTHONPATH'): - env['PYTHONPATH'] = setup_fn + os.path.pathsep + os.environ['PYTHONPATH'] - else: - env['PYTHONPATH'] = setup_fn - else: - # the source is found, let's chdir - if source is not None and os.path.exists(source): - logger.info('Using existing %s egg: %s' % (project_name, source)) - os.chdir(os.path.dirname(source)) - # in this case, we want to be sure that PYTHONPATH is unset (not - # just empty, really unset), else CPython tries to import the - # site.py that it's in virtualenv_support - remove_from_env.append('PYTHONPATH') - else: - if never_download: - logger.fatal("Can't find any local distributions of %s to install " - "and --never-download is set. Either re-run virtualenv " - "without the --never-download option, or place a %s " - "distribution (%s) in one of these " - "locations: %r" % (project_name, project_name, - setup_fn or source, - search_dirs)) - sys.exit(1) +@contextlib.contextmanager +def virtualenv_support_dirs(): + """Context manager yielding either [virtualenv_support_dir] or []""" - logger.info('No %s egg found; downloading' % project_name) - cmd.extend(['--always-copy', '-U', project_name]) - logger.start_progress('Installing %s...' % project_name) - logger.indent += 2 - cwd = None - if project_name == 'distribute': - env['DONT_PATCH_SETUPTOOLS'] = 'true' - - def _filter_ez_setup(line): - return filter_ez_setup(line, project_name) - - if not os.access(os.getcwd(), os.W_OK): - cwd = tempfile.mkdtemp() - if source is not None and os.path.exists(source): - # the current working dir is hostile, let's copy the - # tarball to a temp dir - target = os.path.join(cwd, os.path.split(source)[-1]) - shutil.copy(source, target) - try: - call_subprocess(cmd, show_stdout=False, - filter_stdout=_filter_ez_setup, - extra_env=env, - remove_from_env=remove_from_env, - cwd=cwd) - finally: - logger.indent -= 2 - logger.end_progress() - if os.getcwd() != old_chdir: - os.chdir(old_chdir) - if is_jython and os._name == 'nt': - os.remove(ez_setup) - -def file_search_dirs(): - here = os.path.dirname(os.path.abspath(__file__)) - dirs = ['.', here, - join(here, 'virtualenv_support')] - if os.path.splitext(os.path.dirname(__file__))[0] != 'virtualenv': - # Probably some boot script; just in case virtualenv is installed... + # normal filesystem installation + if os.path.isdir(join(HERE, "virtualenv_support")): + yield [join(HERE, "virtualenv_support")] + elif IS_ZIPAPP: + tmpdir = tempfile.mkdtemp() try: + with zipfile.ZipFile(HERE) as zipf: + for member in zipf.namelist(): + if os.path.dirname(member) == "virtualenv_support": + zipf.extract(member, tmpdir) + yield [join(tmpdir, "virtualenv_support")] + finally: + shutil.rmtree(tmpdir) + # probably a bootstrap script + elif os.path.splitext(os.path.dirname(__file__))[0] != "virtualenv": + try: + # noinspection PyUnresolvedReferences import virtualenv except ImportError: - pass + yield [] else: - dirs.append(os.path.join(os.path.dirname(virtualenv.__file__), 'virtualenv_support')) - return [d for d in dirs if os.path.isdir(d)] - -def install_setuptools(py_executable, unzip=False, - search_dirs=None, never_download=False): - _install_req(py_executable, unzip, - search_dirs=search_dirs, never_download=never_download) - -def install_distribute(py_executable, unzip=False, - search_dirs=None, never_download=False): - _install_req(py_executable, unzip, distribute=True, - search_dirs=search_dirs, never_download=never_download) - -_pip_re = re.compile(r'^pip-.*(zip|tar.gz|tar.bz2|tgz|tbz)$', re.I) -def install_pip(py_executable, search_dirs=None, never_download=False): - if search_dirs is None: - search_dirs = file_search_dirs() - - filenames = [] - for dir in search_dirs: - filenames.extend([join(dir, fn) for fn in os.listdir(dir) - if _pip_re.search(fn)]) - filenames = [(os.path.basename(filename).lower(), i, filename) for i, filename in enumerate(filenames)] - filenames.sort() - filenames = [filename for basename, i, filename in filenames] - if not filenames: - filename = 'pip' + yield [join(os.path.dirname(virtualenv.__file__), "virtualenv_support")] + # we tried! else: - filename = filenames[-1] - easy_install_script = 'easy_install' - if sys.platform == 'win32': - easy_install_script = 'easy_install-script.py' - cmd = [join(os.path.dirname(py_executable), easy_install_script), filename] - if sys.platform == 'win32': - cmd.insert(0, py_executable) - if filename == 'pip': - if never_download: - logger.fatal("Can't find any local distributions of pip to install " - "and --never-download is set. Either re-run virtualenv " - "without the --never-download option, or place a pip " - "source distribution (zip/tar.gz/tar.bz2) in one of these " - "locations: %r" % search_dirs) - sys.exit(1) - logger.info('Installing pip from network...') - else: - logger.info('Installing existing %s distribution: %s' % ( - os.path.basename(filename), filename)) - logger.start_progress('Installing pip...') - logger.indent += 2 - def _filter_setup(line): - return filter_ez_setup(line, 'pip') - try: - call_subprocess(cmd, show_stdout=False, - filter_stdout=_filter_setup) - finally: - logger.indent -= 2 - logger.end_progress() - -def filter_ez_setup(line, project_name='setuptools'): - if not line.strip(): - return Logger.DEBUG - if project_name == 'distribute': - for prefix in ('Extracting', 'Now working', 'Installing', 'Before', - 'Scanning', 'Setuptools', 'Egg', 'Already', - 'running', 'writing', 'reading', 'installing', - 'creating', 'copying', 'byte-compiling', 'removing', - 'Processing'): - if line.startswith(prefix): - return Logger.DEBUG - return Logger.DEBUG - for prefix in ['Reading ', 'Best match', 'Processing setuptools', - 'Copying setuptools', 'Adding setuptools', - 'Installing ', 'Installed ']: - if line.startswith(prefix): - return Logger.DEBUG - return Logger.INFO + yield [] class UpdatingDefaultsHelpFormatter(optparse.IndentedHelpFormatter): @@ -668,6 +507,7 @@ the defaults before expanding them, allowing them to show up correctly in the help listing """ + def expand_default(self, option): if self.parser is not None: self.parser.update_defaults(self.parser.defaults) @@ -676,20 +516,22 @@ class ConfigOptionParser(optparse.OptionParser): """ - Custom option parser which updates its defaults by by checking the + Custom option parser which updates its defaults by checking the configuration files and environmental variables """ + def __init__(self, *args, **kwargs): self.config = ConfigParser.RawConfigParser() self.files = self.get_config_files() self.config.read(self.files) optparse.OptionParser.__init__(self, *args, **kwargs) - def get_config_files(self): - config_file = os.environ.get('VIRTUALENV_CONFIG_FILE', False) + @staticmethod + def get_config_files(): + config_file = os.environ.get("VIRTUALENV_CONFIG_FILE", False) if config_file and os.path.exists(config_file): return [config_file] - return [default_config_file] + return [DEFAULT_CONFIG_FILE] def update_defaults(self, defaults): """ @@ -700,31 +542,33 @@ # Then go and look for the other sources of configuration: config = {} # 1. config files - config.update(dict(self.get_config_section('virtualenv'))) + config.update(dict(self.get_config_section("virtualenv"))) # 2. environmental variables config.update(dict(self.get_environ_vars())) # Then set the options with those values for key, val in config.items(): - key = key.replace('_', '-') - if not key.startswith('--'): - key = '--%s' % key # only prefer long opts + key = key.replace("_", "-") + if not key.startswith("--"): + key = "--{}".format(key) # only prefer long opts option = self.get_option(key) if option is not None: # ignore empty values if not val: continue # handle multiline configs - if option.action == 'append': + if option.action == "append": val = val.split() else: option.nargs = 1 - if option.action in ('store_true', 'store_false', 'count'): + if option.action == "store_false": + val = not strtobool(val) + elif option.action in ("store_true", "count"): val = strtobool(val) try: val = option.convert_value(key, val) except optparse.OptionValueError: e = sys.exc_info()[1] - print("An error occured during configuration: %s" % e) + print("An error occurred during configuration: {!r}".format(e)) sys.exit(3) defaults[option.dest] = val return defaults @@ -737,24 +581,24 @@ return self.config.items(name) return [] - def get_environ_vars(self, prefix='VIRTUALENV_'): + def get_environ_vars(self, prefix="VIRTUALENV_"): """ Returns a generator with all environmental vars with prefix VIRTUALENV """ for key, val in os.environ.items(): if key.startswith(prefix): - yield (key.replace(prefix, '').lower(), val) + yield (key.replace(prefix, "").lower(), val) def get_default_values(self): """ - Overridding to make updating the defaults after instantiation of + Overriding to make updating the defaults after instantiation of the option parser possible, update_defaults() does the dirty work. """ if not self.process_default_values: # Old, pre-Optik 1.5 behaviour. return optparse.Values(self.defaults) - defaults = self.update_defaults(self.defaults.copy()) # ours + defaults = self.update_defaults(self.defaults.copy()) # ours for option in self._get_all_options(): default = defaults.get(option.dest) if isinstance(default, basestring): @@ -765,254 +609,544 @@ def main(): parser = ConfigOptionParser( - version=virtualenv_version, - usage="%prog [OPTIONS] DEST_DIR", - formatter=UpdatingDefaultsHelpFormatter()) + version=virtualenv_version, usage="%prog [OPTIONS] DEST_DIR", formatter=UpdatingDefaultsHelpFormatter() + ) parser.add_option( - '-v', '--verbose', - action='count', - dest='verbose', - default=0, - help="Increase verbosity") + "-v", "--verbose", action="count", dest="verbose", default=5 if DEBUG else 0, help="Increase verbosity." + ) + + parser.add_option("-q", "--quiet", action="count", dest="quiet", default=0, help="Decrease verbosity.") parser.add_option( - '-q', '--quiet', - action='count', - dest='quiet', - default=0, - help='Decrease verbosity') + "-p", + "--python", + dest="python", + metavar="PYTHON_EXE", + 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 ({})".format(sys.executable), + ) 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 ' - 'interpreter to create the new environment. The default is the interpreter that ' - 'virtualenv was installed with (%s)' % sys.executable) + "--clear", dest="clear", action="store_true", help="Clear out the non-root install and start from scratch." + ) + + parser.set_defaults(system_site_packages=False) + parser.add_option( + "--no-site-packages", + dest="system_site_packages", + action="store_false", + help="DEPRECATED. Retained only for backward compatibility. " + "Not having access to global site-packages is now the default behavior.", + ) parser.add_option( - '--clear', - dest='clear', - action='store_true', - help="Clear out the non-root install and start from scratch") + "--system-site-packages", + dest="system_site_packages", + action="store_true", + help="Give the virtual environment access to the global site-packages.", + ) parser.add_option( - '--no-site-packages', - dest='no_site_packages', - action='store_true', - help="Don't give access to the global site-packages dir to the " - "virtual environment") + "--always-copy", + dest="symlink", + action="store_false", + default=True, + help="Always copy files rather than symlinking.", + ) + + 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( - '--system-site-packages', - dest='system_site_packages', - action='store_true', - help="Give access to the global site-packages dir to the " - "virtual environment") + "--no-setuptools", + dest="no_setuptools", + action="store_true", + help="Do not install setuptools in the new virtualenv.", + ) + + parser.add_option("--no-pip", dest="no_pip", action="store_true", help="Do not install pip in the new virtualenv.") + + parser.add_option( + "--no-wheel", dest="no_wheel", action="store_true", help="Do not install wheel in the new virtualenv." + ) parser.add_option( - '--unzip-setuptools', - dest='unzip_setuptools', - action='store_true', - help="Unzip Setuptools or Distribute when installing it") + "--extra-search-dir", + dest="search_dirs", + action="append", + metavar="DIR", + default=[], + help="Directory to look for setuptools/pip distributions in. " "This option can be used multiple times.", + ) 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') + "--download", + dest="download", + default=True, + action="store_true", + help="Download pre-installed packages from PyPI.", + ) parser.add_option( - '--distribute', - dest='use_distribute', - action='store_true', - help='Use Distribute instead of Setuptools. Set environ variable ' - 'VIRTUALENV_DISTRIBUTE to make it the default ') + "--no-download", + "--never-download", + dest="download", + action="store_false", + help="Do not download pre-installed packages from PyPI.", + ) - default_search_dirs = file_search_dirs() - parser.add_option( - '--extra-search-dir', - dest="search_dirs", - action="append", - default=default_search_dirs, - help="Directory to look for setuptools/distribute/pip distributions in. " - "You can add any number of additional --extra-search-dir paths.") + parser.add_option("--prompt", dest="prompt", help="Provides an alternative prompt prefix for this environment.") parser.add_option( - '--never-download', - dest="never_download", + "--setuptools", + dest="setuptools", action="store_true", - help="Never download anything from the network. Instead, virtualenv will fail " - "if local distributions of setuptools/distribute/pip are not present.") + help="DEPRECATED. Retained only for backward compatibility. This option has no effect.", + ) parser.add_option( - '--prompt=', - dest='prompt', - help='Provides an alternative prompt prefix for this environment') + "--distribute", + dest="distribute", + action="store_true", + help="DEPRECATED. Retained only for backward compatibility. This option has no effect.", + ) - if 'extend_parser' in globals(): - extend_parser(parser) + 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(): + # noinspection PyUnresolvedReferences + extend_parser(parser) # noqa: F821 options, args = parser.parse_args() global logger - if 'adjust_options' in globals(): - adjust_options(options, args) + if "adjust_options" in globals(): + # noinspection PyUnresolvedReferences + adjust_options(options, args) # noqa: F821 verbosity = options.verbose - options.quiet - logger = Logger([(Logger.level_for_integer(2-verbosity), sys.stdout)]) + logger = Logger([(Logger.level_for_integer(2 - verbosity), sys.stdout)]) + + def should_reinvoke(options): + """Do we need to reinvoke ourself?""" + # Did the user specify the --python option? + if options.python and not os.environ.get("VIRTUALENV_INTERPRETER_RUNNING"): + interpreter = resolve_interpreter(options.python) + if interpreter != sys.executable: + # The user specified a different interpreter, so we have to reinvoke. + return interpreter - if options.python and not os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'): + # At this point, we know the user wants to use sys.executable to create the + # virtual environment. But on Windows, sys.executable may be a venv redirector, + # in which case we still need to locate the underlying actual interpreter, and + # reinvoke using that. + if IS_WIN: + # OK. Now things get really fun... + # + # If we are running from a venv, with a redirector, then what happens is as + # follows: + # + # 1. The redirector sets __PYVENV_LAUNCHER__ in the environment to point + # to the redirector executable. + # 2. The redirector launches the "base" Python (from the home value in + # pyvenv.cfg). + # 3. The base Python executable sees __PYVENV_LAUNCHER__ in the environment + # and sets sys.executable to that value. + # 4. If site.py gets run, it sees __PYVENV_LAUNCHER__, and sets + # sys._base_executable to _winapi.GetModuleFileName(0) and removes + # __PYVENV_LAUNCHER__. + # + # Unfortunately, that final step (site.py) may not happen. There are 2 key + # times when that is the case: + # + # 1. Python 3.7.2, which had the redirector but not the site.py code. + # 2. Running a venv from a virtualenv, which uses virtualenv's custom + # site.py. + # + # So, we check for sys._base_executable, but if it's not present and yet we + # have __PYVENV_LAUNCHER__, we do what site.py would have done and get our + # interpreter from GetModuleFileName(0). We also remove __PYVENV_LAUNCHER__ + # from the environment, to avoid loops (actually, mainly because site.py + # does so, and my head hurts enough buy now that I just want to be safe!) + + # In Python 3.7.4, the rules changed so that sys._base_executable is always + # set. So we now only return sys._base_executable if it's set *and does not + # match sys.executable* (we still have to check that it's set, as we need to + # support Python 3.7.3 and earlier). + + # Phew. + + if getattr(sys, "_base_executable", sys.executable) != sys.executable: + return sys._base_executable + + if "__PYVENV_LAUNCHER__" in os.environ: + import _winapi + + del os.environ["__PYVENV_LAUNCHER__"] + return _winapi.GetModuleFileName(0) + + # We don't need to reinvoke + return None + + interpreter = should_reinvoke(options) + if interpreter is None: + # We don't need to reinvoke - if the user asked us to, tell them why we + # aren't. + if options.python: + logger.warn("Already using interpreter {}".format(sys.executable)) + else: env = os.environ.copy() - interpreter = resolve_interpreter(options.python) - if interpreter == sys.executable: - logger.warn('Already using interpreter %s' % interpreter) - else: - logger.notify('Running virtualenv with interpreter %s' % interpreter) - env['VIRTUALENV_INTERPRETER_RUNNING'] = 'true' - file = __file__ - if file.endswith('.pyc'): - file = file[:-1] - popen = subprocess.Popen([interpreter, file] + sys.argv[1:], env=env) - raise SystemExit(popen.wait()) + logger.notify("Running virtualenv with interpreter {}".format(interpreter)) + env["VIRTUALENV_INTERPRETER_RUNNING"] = "true" + # Remove the variable __PYVENV_LAUNCHER__ if it's present, as it causes the + # interpreter to redirect back to the virtual environment. + if "__PYVENV_LAUNCHER__" in env: + del env["__PYVENV_LAUNCHER__"] + file = __file__ + if file.endswith(".pyc"): + file = file[:-1] + elif IS_ZIPAPP: + file = HERE + sub_process_call = subprocess.Popen([interpreter, file] + sys.argv[1:], env=env) + raise SystemExit(sub_process_call.wait()) - # Force --distribute on Python 3, since setuptools is not available. - if majver > 2: - options.use_distribute = True - - if os.environ.get('PYTHONDONTWRITEBYTECODE') and not options.use_distribute: - print( - "The PYTHONDONTWRITEBYTECODE environment variable is " - "not compatible with setuptools. Either use --distribute " - "or unset PYTHONDONTWRITEBYTECODE.") - sys.exit(2) if not args: - print('You must provide a DEST_DIR') + print("You must provide a DEST_DIR") parser.print_help() sys.exit(2) if len(args) > 1: - print('There must be only one argument: DEST_DIR (you gave %s)' % ( - ' '.join(args))) + print("There must be only one argument: DEST_DIR (you gave {})".format(" ".join(args))) parser.print_help() sys.exit(2) home_dir = args[0] - if os.environ.get('WORKING_ENV'): - logger.fatal('ERROR: you cannot run virtualenv while in a workingenv') - logger.fatal('Please deactivate your workingenv, then re-run this script') + if os.path.exists(home_dir) and os.path.isfile(home_dir): + logger.fatal("ERROR: File already exists and is not a directory.") + logger.fatal("Please provide a different path or delete the file.") sys.exit(3) - if 'PYTHONHOME' in os.environ: - logger.warn('PYTHONHOME is set. You *must* activate the virtualenv before using it') - del os.environ['PYTHONHOME'] + if os.pathsep in home_dir: + logger.fatal("ERROR: target path contains the operating system path separator '{}'".format(os.pathsep)) + logger.fatal("This is not allowed as would make the activation scripts unusable.".format(os.pathsep)) + sys.exit(3) + + if os.environ.get("WORKING_ENV"): + logger.fatal("ERROR: you cannot run virtualenv while in a working env") + logger.fatal("Please deactivate your working env, then re-run this script") + sys.exit(3) + + if "PYTHONHOME" in os.environ: + logger.warn("PYTHONHOME is set. You *must* activate the virtualenv before using it") + del os.environ["PYTHONHOME"] if options.relocatable: make_environment_relocatable(home_dir) return - if options.no_site_packages: - logger.warn('The --no-site-packages flag is deprecated; it is now ' - 'the default behavior.') + with virtualenv_support_dirs() as search_dirs: + create_environment( + home_dir, + site_packages=options.system_site_packages, + clear=options.clear, + prompt=options.prompt, + search_dirs=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, + ) + if "after_install" in globals(): + # noinspection PyUnresolvedReferences + after_install(options, home_dir) # noqa: F821 - create_environment(home_dir, - site_packages=options.system_site_packages, - clear=options.clear, - unzip_setuptools=options.unzip_setuptools, - use_distribute=options.use_distribute, - prompt=options.prompt, - search_dirs=options.search_dirs, - never_download=options.never_download) - 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): +def call_subprocess( + cmd, + show_stdout=True, + filter_stdout=None, + cwd=None, + raise_on_return_code=True, + extra_env=None, + remove_from_env=None, + stdin=None, +): cmd_parts = [] for part in cmd: if len(part) > 45: - part = part[:20]+"..."+part[-20:] - if ' ' in part or '\n' in part or '"' in part or "'" in part: - part = '"%s"' % part.replace('"', '\\"') - if hasattr(part, 'decode'): + part = part[:20] + "..." + part[-20:] + if " " in part or "\n" in part or '"' in part or "'" in part: + part = '"{}"'.format(part.replace('"', '\\"')) + if hasattr(part, "decode"): try: part = part.decode(sys.getdefaultencoding()) except UnicodeDecodeError: part = part.decode(sys.getfilesystemencoding()) cmd_parts.append(part) - cmd_desc = ' '.join(cmd_parts) + cmd_desc = " ".join(cmd_parts) if show_stdout: stdout = None else: stdout = subprocess.PIPE - logger.debug("Running command %s" % cmd_desc) + logger.debug("Running command {}".format(cmd_desc)) if extra_env or remove_from_env: env = os.environ.copy() if extra_env: env.update(extra_env) if remove_from_env: - for varname in remove_from_env: - env.pop(varname, None) + for var_name in remove_from_env: + env.pop(var_name, None) else: env = None try: proc = subprocess.Popen( - cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout, - cwd=cwd, env=env) + cmd, + stderr=subprocess.STDOUT, + stdin=None if stdin is None else subprocess.PIPE, + stdout=stdout, + cwd=cwd, + env=env, + ) except Exception: e = sys.exc_info()[1] - logger.fatal( - "Error %s while executing command %s" % (e, cmd_desc)) + logger.fatal("Error {} while executing command {}".format(e, cmd_desc)) raise all_output = [] if stdout is not None: - stdout = proc.stdout + if stdin is not None: + with proc.stdin: + proc.stdin.write(stdin) + encoding = sys.getdefaultencoding() fs_encoding = sys.getfilesystemencoding() - while 1: - line = stdout.readline() - try: - line = line.decode(encoding) - except UnicodeDecodeError: - line = line.decode(fs_encoding) - if not line: - break - line = line.rstrip() - all_output.append(line) - if filter_stdout: - level = filter_stdout(line) - if isinstance(level, tuple): - level, line = level - logger.log(level, line) - if not logger.stdout_level_matches(level): - logger.show_progress() - else: - logger.info(line) + with proc.stdout as stdout: + while 1: + line = stdout.readline() + try: + line = line.decode(encoding) + except UnicodeDecodeError: + line = line.decode(fs_encoding) + if not line: + break + line = line.rstrip() + all_output.append(line) + if filter_stdout: + level = filter_stdout(line) + if isinstance(level, tuple): + level, line = level + logger.log(level, line) + if not logger.stdout_level_matches(level): + logger.show_progress() + else: + logger.info(line) else: - proc.communicate() + proc.communicate(stdin) proc.wait() if proc.returncode: - if raise_on_returncode: + if raise_on_return_code: if all_output: - logger.notify('Complete output from command %s:' % cmd_desc) - logger.notify('\n'.join(all_output) + '\n----------------------------------------') - raise OSError( - "Command %s failed with error code %s" - % (cmd_desc, proc.returncode)) + logger.notify("Complete output from command {}:".format(cmd_desc)) + logger.notify("\n".join(all_output) + "\n----------------------------------------") + raise OSError("Command {} failed with error code {}".format(cmd_desc, proc.returncode)) else: - logger.warn( - "Command %s had error code %s" - % (cmd_desc, proc.returncode)) + logger.warn("Command {} had error code {}".format(cmd_desc, proc.returncode)) + return all_output + + +def filter_install_output(line): + if line.strip().startswith("running"): + return Logger.INFO + return Logger.DEBUG + + +def find_wheels(projects, search_dirs): + """Find wheels from which we can import PROJECTS. + + Scan through SEARCH_DIRS for a wheel for each PROJECT in turn. Return + a list of the first wheel found for each PROJECT + """ + + wheels = [] + + # Look through SEARCH_DIRS for the first suitable wheel. Don't bother + # about version checking here, as this is simply to get something we can + # then use to install the correct version. + for project in projects: + for dirname in search_dirs: + # This relies on only having "universal" wheels available. + # The pattern could be tightened to require -py2.py3-none-any.whl. + files = glob.glob(os.path.join(dirname, "{}-*.whl".format(project))) + if files: + versions = list( + reversed( + sorted( + [(tuple(int(i) for i in os.path.basename(f).split("-")[1].split(".")), f) for f in files] + ) + ) + ) + if project == "pip" and sys.version_info[0:2] == (3, 4): + wheel = next(p for v, p in versions if v <= (19, 1, 1)) + else: + wheel = versions[0][1] + wheels.append(wheel) + break + else: + # We're out of luck, so quit with a suitable error + logger.fatal("Cannot find a wheel for {}".format(project)) + + return wheels + + +def install_wheel(project_names, py_executable, search_dirs=None, download=False): + if search_dirs is None: + search_dirs_context = virtualenv_support_dirs + else: + + @contextlib.contextmanager + def search_dirs_context(): + yield search_dirs + + with search_dirs_context() as search_dirs: + _install_wheel_with_search_dir(download, project_names, py_executable, search_dirs) -def create_environment(home_dir, site_packages=False, clear=False, - unzip_setuptools=False, use_distribute=False, - prompt=None, search_dirs=None, never_download=False): +def _install_wheel_with_search_dir(download, project_names, py_executable, search_dirs): + wheels = find_wheels(["setuptools", "pip"], search_dirs) + python_path = os.pathsep.join(wheels) + + # PIP_FIND_LINKS uses space as the path separator and thus cannot have paths + # with spaces in them. Convert any of those to local file:// URL form. + try: + from urlparse import urljoin + from urllib import pathname2url + except ImportError: + from urllib.parse import urljoin + from urllib.request import pathname2url + + def space_path2url(p): + if " " not in p: + return p + return urljoin("file:", pathname2url(os.path.abspath(p))) + + find_links = " ".join(space_path2url(d) for d in search_dirs) + + extra_args = ["--ignore-installed", "-v"] + if DEBUG: + extra_args.append("-v") + + config = _pip_config(py_executable, python_path) + defined_cert = bool(config.get("install.cert") or config.get(":env:.cert") or config.get("global.cert")) + + script = textwrap.dedent( + """ + import sys + import pkgutil + import tempfile + import os + + defined_cert = {defined_cert} + + try: + from pip._internal import main as _main + if type(_main) is type(sys): # + _main = _main.main # nested starting in Pip 19.3 + 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") + except IOError: + cert_data = None + + if not defined_cert and 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"] + [{extra_args}] + if cert_file is not None: + args += ["--cert", cert_file.name] + args += sys.argv[1:] + + sys.exit(_main(args)) + finally: + if cert_file is not None: + os.remove(cert_file.name) + """.format( + defined_cert=defined_cert, extra_args=", ".join(repr(i) for i in extra_args) + ) + ).encode("utf8") + + if sys.version_info[0:2] == (3, 4) and "pip" in project_names: + at = project_names.index("pip") + project_names[at] = "pip<19.2" + + cmd = [py_executable, "-"] + project_names + logger.start_progress("Installing {}...".format(", ".join(project_names))) + logger.indent += 2 + + env = { + "PYTHONPATH": python_path, + "PIP_FIND_LINKS": find_links, + "PIP_USE_WHEEL": "1", + "PIP_ONLY_BINARY": ":all:", + "PIP_USER": "0", + "PIP_NO_INPUT": "1", + } + + 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 _pip_config(py_executable, python_path): + cmd = [py_executable, "-m", "pip", "config", "list"] + config = {} + for line in call_subprocess( + cmd, + show_stdout=False, + extra_env={"PYTHONPATH": python_path}, + remove_from_env=["PIP_VERBOSE", "PIP_QUIET"], + raise_on_return_code=False, + ): + key, _, value = line.partition("=") + if value: + config[key] = ast.literal_eval(value) + return config + + +def create_environment( + home_dir, + site_packages=False, + clear=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``. @@ -1024,464 +1158,642 @@ """ home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) - py_executable = os.path.abspath(install_python( - home_dir, lib_dir, inc_dir, bin_dir, - site_packages=site_packages, clear=clear)) + py_executable = os.path.abspath( + install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages=site_packages, clear=clear, symlink=symlink) + ) install_distutils(home_dir) - # use_distribute also is True if VIRTUALENV_DISTRIBUTE env var is set - # we also check VIRTUALENV_USE_DISTRIBUTE for backwards compatibility - if use_distribute or os.environ.get('VIRTUALENV_USE_DISTRIBUTE'): - install_distribute(py_executable, unzip=unzip_setuptools, - search_dirs=search_dirs, never_download=never_download) - else: - install_setuptools(py_executable, unzip=unzip_setuptools, - search_dirs=search_dirs, never_download=never_download) + to_install = [] + + if not no_setuptools: + to_install.append("setuptools") - install_pip(py_executable, search_dirs=search_dirs, never_download=never_download) + if not no_pip: + to_install.append("pip") + + if not no_wheel: + to_install.append("wheel") + + if to_install: + install_wheel(to_install, py_executable, search_dirs, download=download) install_activate(home_dir, bin_dir, prompt) -def path_locations(home_dir): + install_python_config(home_dir, bin_dir, prompt) + + +def is_executable_file(fpath): + return os.path.isfile(fpath) and is_executable(fpath) + + +def path_locations(home_dir, dry_run=False): """Return the path locations for the environment (where libraries are, where scripts go, etc)""" + home_dir = os.path.abspath(home_dir) + lib_dir, inc_dir, bin_dir = None, None, None # XXX: We'd use distutils.sysconfig.get_python_inc/lib but its # prefix arg is broken: http://bugs.python.org/issue3386 - if sys.platform == 'win32': + if IS_WIN: # Windows has lots of problems with executables with spaces in # the name; this function will remove them (using the ~1 # format): - mkdir(home_dir) - if ' ' in home_dir: + if not dry_run: + mkdir(home_dir) + if " " in home_dir: + import ctypes + + get_short_path_name = ctypes.windll.kernel32.GetShortPathNameW + size = max(len(home_dir) + 1, 256) + buf = ctypes.create_unicode_buffer(size) try: - import win32api - except ImportError: - print('Error: the path "%s" has a space in it' % home_dir) - print('To handle these kinds of paths, the win32api module must be installed:') - print(' http://sourceforge.net/projects/pywin32/') + # noinspection PyUnresolvedReferences + u = unicode + except NameError: + u = str + ret = get_short_path_name(u(home_dir), buf, size) + if not ret: + print('Error: the path "{}" has a space in it'.format(home_dir)) + print("We could not determine the short pathname for it.") + print("Exiting.") sys.exit(3) - home_dir = win32api.GetShortPathName(home_dir) - lib_dir = join(home_dir, 'Lib') - inc_dir = join(home_dir, 'Include') - bin_dir = join(home_dir, 'Scripts') - elif is_jython: - lib_dir = join(home_dir, 'Lib') - inc_dir = join(home_dir, 'Include') - bin_dir = join(home_dir, 'bin') - elif is_pypy: + home_dir = str(buf.value) + lib_dir = join(home_dir, "Lib") + inc_dir = join(home_dir, "Include") + bin_dir = join(home_dir, "Scripts") + if IS_PYPY: lib_dir = home_dir - inc_dir = join(home_dir, 'include') - bin_dir = join(home_dir, 'bin') - else: - lib_dir = join(home_dir, 'lib', py_version) - inc_dir = join(home_dir, 'include', py_version + abiflags) - bin_dir = join(home_dir, 'bin') + inc_dir = join(home_dir, "include") + bin_dir = join(home_dir, "bin") + elif not IS_WIN: + lib_dir = join(home_dir, "lib", PY_VERSION) + inc_dir = join(home_dir, "include", PY_VERSION + ABI_FLAGS) + bin_dir = join(home_dir, "bin") return home_dir, lib_dir, inc_dir, bin_dir def change_prefix(filename, dst_prefix): prefixes = [sys.prefix] - if sys.platform == "darwin": - prefixes.extend(( - os.path.join("/Library/Python", sys.version[:3], "site-packages"), - os.path.join(sys.prefix, "Extras", "lib", "python"), - os.path.join("~", "Library", "Python", sys.version[:3], "site-packages"))) + if IS_DARWIN: + prefixes.extend( + ( + os.path.join("/Library/Python", VERSION, "site-packages"), + os.path.join(sys.prefix, "Extras", "lib", "python"), + os.path.join("~", "Library", "Python", VERSION, "site-packages"), + # Python 2.6 no-frameworks + os.path.join("~", ".local", "lib", "python", VERSION, "site-packages"), + # System Python 2.7 on OSX Mountain Lion + os.path.join("~", "Library", "Python", VERSION, "lib", "python", "site-packages"), + ) + ) - if hasattr(sys, 'real_prefix'): + if hasattr(sys, "real_prefix"): prefixes.append(sys.real_prefix) + if hasattr(sys, "base_prefix"): + prefixes.append(sys.base_prefix) + prefixes = list(map(os.path.expanduser, prefixes)) prefixes = list(map(os.path.abspath, prefixes)) + # Check longer prefixes first so we don't split in the middle of a filename + prefixes = sorted(prefixes, key=len, reverse=True) filename = os.path.abspath(filename) + # On Windows, make sure drive letter is uppercase + if IS_WIN and filename[0] in "abcdefghijklmnopqrstuvwxyz": + filename = filename[0].upper() + filename[1:] + for i, prefix in enumerate(prefixes): + if IS_WIN and prefix[0] in "abcdefghijklmnopqrstuvwxyz": + prefixes[i] = prefix[0].upper() + prefix[1:] for src_prefix in prefixes: if filename.startswith(src_prefix): - _, relpath = filename.split(src_prefix, 1) - assert relpath[0] == os.sep - relpath = relpath[1:] - return join(dst_prefix, relpath) - assert False, "Filename %s does not start with any of these prefixes: %s" % \ - (filename, prefixes) + _, relative_path = filename.split(src_prefix, 1) + if src_prefix != os.sep: # sys.prefix == "/" + assert relative_path[0] == os.sep + relative_path = relative_path[1:] + return join(dst_prefix, relative_path) + raise AssertionError("Filename {} does not start with any of these prefixes: {}".format(filename, prefixes)) + + +def find_module_filename(modname): + + if sys.version_info < (3, 4): + # noinspection PyDeprecation + import imp + + try: + file_handler, filepath, _ = imp.find_module(modname) + except ImportError: + return None + else: + if file_handler is not None: + file_handler.close() + return filepath + else: + import importlib.util + + if sys.version_info < (3, 5): + + def find_spec(modname): + # noinspection PyDeprecation + loader = importlib.find_loader(modname) + if loader is None: + return None + else: + return importlib.util.spec_from_loader(modname, loader) + + else: + find_spec = importlib.util.find_spec + + spec = find_spec(modname) + if spec is None: + return None + if not os.path.exists(spec.origin): + # https://bitbucket.org/pypy/pypy/issues/2944/origin-for-several-builtin-modules + # on pypy3, some builtin modules have a bogus build-time file path, ignore them + return None + filepath = spec.origin + # https://www.python.org/dev/peps/pep-3147/#file guarantee to be non-cached + if os.path.basename(filepath) == "__init__.py": + filepath = os.path.dirname(filepath) + return filepath + -def copy_required_modules(dst_prefix): - import imp - # If we are running under -p, we need to remove the current - # directory from sys.path temporarily here, so that we - # definitely get the modules from the site directory of - # the interpreter we are running under, not the one - # virtualenv.py is installed under (which might lead to py2/py3 - # incompatibility issues) - _prev_sys_path = sys.path - if os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'): - sys.path = sys.path[1:] - try: - for modname in REQUIRED_MODULES: - if modname in sys.builtin_module_names: - logger.info("Ignoring built-in bootstrap module: %s" % modname) - continue - try: - f, filename, _ = imp.find_module(modname) - except ImportError: - logger.info("Cannot import bootstrap module: %s" % modname) +def copy_required_modules(dst_prefix, symlink): + for modname in REQUIRED_MODULES: + if modname in sys.builtin_module_names: + logger.info("Ignoring built-in bootstrap module: %s" % modname) + continue + filename = find_module_filename(modname) + if filename is None: + logger.info("Cannot import bootstrap module: %s" % modname) + else: + # special-case custom readline.so on OS X, but not for pypy: + if ( + modname == "readline" + and IS_DARWIN + and not (IS_PYPY or filename.endswith(join("lib-dynload", "readline.so"))) + ): + dst_filename = join(dst_prefix, "lib", PY_VERSION, "readline.so") + elif modname == "readline" and IS_WIN: + # special-case for Windows, where readline is not a standard module, though it may have been installed + # in site-packages by a third-party package + dst_filename = None else: - if f is not None: - f.close() dst_filename = change_prefix(filename, dst_prefix) - copyfile(filename, dst_filename) - if filename.endswith('.pyc'): - pyfile = filename[:-1] - if os.path.exists(pyfile): - copyfile(pyfile, dst_filename[:-1]) - finally: - sys.path = _prev_sys_path + if dst_filename is not None: + copyfile(filename, dst_filename, symlink) + if filename.endswith(".pyc"): + py_file = filename[:-1] + if os.path.exists(py_file): + copyfile(py_file, dst_filename[:-1], symlink) + + +def copy_required_files(src_dir, lib_dir, symlink): + if not os.path.isdir(src_dir): + return + for fn in os.listdir(src_dir): + bn = os.path.splitext(fn)[0] + if fn != "site-packages" and bn in REQUIRED_FILES: + copyfile(join(src_dir, fn), join(lib_dir, fn), symlink) + + +def copy_license(prefix, dst_prefix, lib_dir, symlink): + """Copy the license file so `license()` builtin works""" + lib64_dir = lib_dir.replace("lib", "lib64") + for license_path in ( + # posix cpython + os.path.join(prefix, os.path.relpath(lib_dir, dst_prefix), "LICENSE.txt"), + # posix cpython installed in /usr/lib64 + os.path.join(prefix, os.path.relpath(lib64_dir, dst_prefix), "LICENSE.txt"), + # windows cpython + os.path.join(prefix, "LICENSE.txt"), + # pypy + os.path.join(prefix, "LICENSE"), + ): + if os.path.exists(license_path): + dest = subst_path(license_path, prefix, dst_prefix) + copyfile(license_path, dest, symlink) + return + logger.warn("No LICENSE.txt / LICENSE found in source") -def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear): + +def copy_include_dir(include_src, include_dest, symlink): + """Copy headers from *include_src* to *include_dest* symlinking if required""" + if not os.path.isdir(include_src): + return + # PyPy headers are located in ``pypy-dir/include`` and following code + # avoids making ``venv-dir/include`` symlink to it + if IS_PYPY: + for fn in os.listdir(include_src): + copyfile(join(include_src, fn), join(include_dest, fn), symlink) + else: + copyfile(include_src, include_dest, symlink) + + +def copy_tcltk(src, dest, symlink): + """ copy tcl/tk libraries on Windows (issue #93) """ + for lib_version in "8.5", "8.6": + for libname in "tcl", "tk": + src_dir = join(src, "tcl", libname + lib_version) + dest_dir = join(dest, "tcl", libname + lib_version) + # Only copy the dirs from the above combinations that exist + if os.path.exists(src_dir) and not os.path.exists(dest_dir): + copy_file_or_folder(src_dir, dest_dir, 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 + return prefix_path.replace(prefix, home_dir, 1) + + +def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear, symlink=True): """Install just the base environment, no distutils patches etc""" if sys.executable.startswith(bin_dir): - print('Please use the *system* python to run this script') + print("Please use the *system* python to run this script") return if clear: - rmtree(lib_dir) - ## FIXME: why not delete it? - ## Maybe it should delete everything with #!/path/to/venv/python in it - logger.notify('Not deleting %s', bin_dir) + rm_tree(lib_dir) + # FIXME: why not delete it? + # Maybe it should delete everything with #!/path/to/venv/python in it + logger.notify("Not deleting %s", bin_dir) - if hasattr(sys, 'real_prefix'): - logger.notify('Using real prefix %r' % sys.real_prefix) + if hasattr(sys, "real_prefix"): + logger.notify("Using real prefix %r", sys.real_prefix) prefix = sys.real_prefix + elif hasattr(sys, "base_prefix"): + logger.notify("Using base prefix %r", sys.base_prefix) + prefix = sys.base_prefix else: prefix = sys.prefix + prefix = os.path.abspath(prefix) mkdir(lib_dir) - fix_lib64(lib_dir) + fix_lib64(lib_dir, symlink) stdlib_dirs = [os.path.dirname(os.__file__)] - if sys.platform == 'win32': - stdlib_dirs.append(join(os.path.dirname(stdlib_dirs[0]), 'DLLs')) - elif sys.platform == 'darwin': - stdlib_dirs.append(join(stdlib_dirs[0], 'site-packages')) - if hasattr(os, 'symlink'): - logger.info('Symlinking Python bootstrap modules') + if IS_WIN: + stdlib_dirs.append(join(os.path.dirname(stdlib_dirs[0]), "DLLs")) + elif IS_DARWIN: + stdlib_dirs.append(join(stdlib_dirs[0], "site-packages")) + if hasattr(os, "symlink"): + logger.info("Symlinking Python bootstrap modules") else: - logger.info('Copying Python bootstrap modules') + logger.info("Copying Python bootstrap modules") logger.indent += 2 try: # copy required files... for stdlib_dir in stdlib_dirs: - if not os.path.isdir(stdlib_dir): - continue - 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)) + copy_required_files(stdlib_dir, lib_dir, symlink) # ...and modules - copy_required_modules(home_dir) + copy_required_modules(home_dir, symlink) + copy_license(prefix, home_dir, lib_dir, symlink) finally: logger.indent -= 2 - mkdir(join(lib_dir, 'site-packages')) + # ...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'): + 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') + 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) 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') + 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, '') + writefile(site_packages_filename, "") + + if IS_PYPY or IS_WIN: + standard_lib_include_dir = join(prefix, "include") else: - if os.path.exists(site_packages_filename): - logger.info('Deleting %s' % site_packages_filename) - os.unlink(site_packages_filename) + standard_lib_include_dir = join(prefix, "include", PY_VERSION + ABI_FLAGS) + if os.path.exists(standard_lib_include_dir): + copy_include_dir(standard_lib_include_dir, inc_dir, symlink) + else: + logger.debug("No include dir %s", standard_lib_include_dir) - if is_pypy or is_win: - stdinc_dir = join(prefix, 'include') - else: - stdinc_dir = join(prefix, 'include', py_version + abiflags) - if os.path.exists(stdinc_dir): - copyfile(stdinc_dir, inc_dir) - else: - logger.debug('No include dir %s' % stdinc_dir) + platform_include_dir = distutils.sysconfig.get_python_inc(plat_specific=1) + if platform_include_dir != standard_lib_include_dir: + platform_include_dest = distutils.sysconfig.get_python_inc(plat_specific=1, prefix=home_dir) + if platform_include_dir == platform_include_dest: + # Do platinc_dest manually due to a CPython bug; + # not http://bugs.python.org/issue3386 but a close cousin + platform_include_dest = subst_path(platform_include_dir, prefix, home_dir) + if platform_include_dest: + # PyPy's stdinc_dir and prefix are relative to the original binary + # (traversing virtualenvs), whereas the platinc_dir is relative to + # the inner virtualenv and ignores the prefix argument. + # This seems more evolved than designed. + copy_include_dir(platform_include_dir, platform_include_dest, symlink) # pypy never uses exec_prefix, just ignore it - if sys.exec_prefix != prefix and not is_pypy: - if sys.platform == 'win32': - exec_dir = join(sys.exec_prefix, 'lib') - elif is_jython: - exec_dir = join(sys.exec_prefix, 'Lib') + if os.path.realpath(sys.exec_prefix) != os.path.realpath(prefix) and not IS_PYPY: + if IS_WIN: + exec_dir = join(sys.exec_prefix, "lib") else: - exec_dir = join(sys.exec_prefix, 'lib', py_version) - for fn in os.listdir(exec_dir): - copyfile(join(exec_dir, fn), join(lib_dir, fn)) - - if is_jython: - # Jython has either jython-dev.jar and javalib/ dir, or just - # jython.jar - for name in 'jython-dev.jar', 'javalib', 'jython.jar': - src = join(prefix, name) - if os.path.exists(src): - copyfile(src, join(home_dir, name)) - # XXX: registry should always exist after Jython 2.5rc1 - src = join(prefix, 'registry') - if os.path.exists(src): - copyfile(src, join(home_dir, 'registry'), symlink=False) - copyfile(join(prefix, 'cachedir'), join(home_dir, 'cachedir'), - symlink=False) + exec_dir = join(sys.exec_prefix, "lib", PY_VERSION) + copy_required_files(exec_dir, lib_dir, symlink) mkdir(bin_dir) py_executable = join(bin_dir, os.path.basename(sys.executable)) - if 'Python.framework' in prefix: - if re.search(r'/Python(?:-32|-64)*$', py_executable): + if "Python.framework" in prefix or "Python3.framework" in prefix: + # OS X framework builds cause validation to break + # https://github.com/pypa/virtualenv/issues/322 + if os.environ.get("__PYVENV_LAUNCHER__"): + del os.environ["__PYVENV_LAUNCHER__"] + if re.search(r"/Python(?:-32|-64)*$", py_executable): # The name of the python executable is not quite what # we want, rename it. - py_executable = os.path.join( - os.path.dirname(py_executable), 'python') + py_executable = os.path.join(os.path.dirname(py_executable), "python") - logger.notify('New %s executable in %s', expected_exe, py_executable) - pcbuild_dir = os.path.dirname(sys.executable) - pyd_pth = os.path.join(lib_dir, 'site-packages', 'virtualenv_builddir_pyd.pth') - if is_win and os.path.exists(os.path.join(pcbuild_dir, 'build.bat')): - logger.notify('Detected python running from build directory %s', pcbuild_dir) - logger.notify('Writing .pth file linking to build directory for *.pyd files') - writefile(pyd_pth, pcbuild_dir) + logger.notify("New %s executable in %s", EXPECTED_EXE, py_executable) + pc_build_dir = os.path.dirname(sys.executable) + pyd_pth = os.path.join(lib_dir, "site-packages", "virtualenv_builddir_pyd.pth") + if IS_WIN and os.path.exists(os.path.join(pc_build_dir, "build.bat")): + logger.notify("Detected python running from build directory %s", pc_build_dir) + logger.notify("Writing .pth file linking to build directory for *.pyd files") + writefile(pyd_pth, pc_build_dir) else: - pcbuild_dir = None if os.path.exists(pyd_pth): - logger.info('Deleting %s (not Windows env or not build directory python)' % pyd_pth) + logger.info("Deleting %s (not Windows env or not build directory python)", pyd_pth) os.unlink(pyd_pth) - + if sys.executable != py_executable: - ## FIXME: could I just hard link? + # FIXME: could I just hard link? executable = sys.executable - if sys.platform == 'cygwin' and os.path.exists(executable + '.exe'): - # Cygwin misreports sys.executable sometimes - executable += '.exe' - py_executable += '.exe' - logger.info('Executable actually exists in %s' % executable) shutil.copyfile(executable, py_executable) make_exe(py_executable) - if sys.platform == 'win32' or sys.platform == 'cygwin': - pythonw = os.path.join(os.path.dirname(sys.executable), 'pythonw.exe') - if os.path.exists(pythonw): - logger.info('Also created pythonw.exe') - shutil.copyfile(pythonw, os.path.join(os.path.dirname(py_executable), 'pythonw.exe')) - 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 IS_WIN or IS_CYGWIN: + python_w = os.path.join(os.path.dirname(sys.executable), "pythonw.exe") + if os.path.exists(python_w): + logger.info("Also created pythonw.exe") + shutil.copyfile(python_w, os.path.join(os.path.dirname(py_executable), "pythonw.exe")) + 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') + 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') + 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: + if IS_PYPY: + py_executable_dll_s = [("libpypy-c.dll", "libpypy_d-c.dll")] + else: + py_executable_dll_s = [ + ("python{}.dll".format(sys.version_info[0]), "python{}_d.dll".format(sys.version_info[0])), + ( + "python{}{}.dll".format(sys.version_info[0], sys.version_info[1]), + "python{}{}_d.dll".format(sys.version_info[0], sys.version_info[1]), + ), + ] + + for py_executable_dll, py_executable_dll_d in py_executable_dll_s: + python_dll = os.path.join(os.path.dirname(sys.executable), py_executable_dll) + python_dll_d = os.path.join(os.path.dirname(sys.executable), py_executable_dll_d) + python_dll_d_dest = os.path.join(os.path.dirname(py_executable), py_executable_dll_d) + if os.path.exists(python_dll): + logger.info("Also created %s", py_executable_dll) + shutil.copyfile(python_dll, os.path.join(os.path.dirname(py_executable), py_executable_dll)) + if os.path.exists(python_dll_d): + logger.info("Also created %s", py_executable_dll_d) + shutil.copyfile(python_dll_d, python_dll_d_dest) + elif os.path.exists(python_dll_d_dest): + logger.info("Removed %s as the source does not exist", python_dll_d_dest) + os.unlink(python_dll_d_dest) + if IS_PYPY: # make a symlink python --> pypy-c - python_executable = os.path.join(os.path.dirname(py_executable), 'python') - logger.info('Also created executable %s' % python_executable) - copyfile(py_executable, python_executable) + python_executable = os.path.join(os.path.dirname(py_executable), "python") + if IS_WIN or IS_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", "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) - if os.path.splitext(os.path.basename(py_executable))[0] != expected_exe: - secondary_exe = os.path.join(os.path.dirname(py_executable), - expected_exe) + for d in sys.path: + if d.endswith("lib_pypy"): + break + else: + logger.fatal("Could not find lib_pypy in sys.path") + raise SystemExit(3) + logger.info("Copying lib_pypy") + copyfile(d, os.path.join(home_dir, "lib_pypy"), symlink) + + if os.path.splitext(os.path.basename(py_executable))[0] != EXPECTED_EXE: + secondary_exe = os.path.join(os.path.dirname(py_executable), EXPECTED_EXE) py_executable_ext = os.path.splitext(py_executable)[1] - if py_executable_ext == '.exe': + if py_executable_ext.lower() == ".exe": # python2.4 gives an extension of '.4' :P secondary_exe += py_executable_ext if os.path.exists(secondary_exe): - logger.warn('Not overwriting existing %s script %s (you must use %s)' - % (expected_exe, secondary_exe, py_executable)) + logger.warn( + "Not overwriting existing {} script {} (you must use {})".format( + EXPECTED_EXE, secondary_exe, py_executable + ) + ) else: - logger.notify('Also creating executable in %s' % secondary_exe) + logger.notify("Also creating executable in %s", secondary_exe) shutil.copyfile(sys.executable, secondary_exe) make_exe(secondary_exe) - if '.framework' in prefix: - if 'Python.framework' in prefix: - logger.debug('MacOSX Python framework detected') - # Make sure we use the the embedded interpreter inside + if ".framework" in prefix: + original_python = None + if "Python.framework" in prefix or "Python3.framework" in prefix: + logger.debug("MacOSX Python framework detected") + # Make sure we use the embedded interpreter inside # the framework, even if sys.executable points to # the stub executable in ${sys.prefix}/bin # See http://groups.google.com/group/python-virtualenv/ # browse_thread/thread/17cab2f85da75951 - original_python = os.path.join( - prefix, 'Resources/Python.app/Contents/MacOS/Python') - if 'EPD' in prefix: - logger.debug('EPD framework detected') - original_python = os.path.join(prefix, 'bin/python') + original_python = os.path.join(prefix, "Resources/Python.app/Contents/MacOS/Python") + if "EPD" in prefix: + logger.debug("EPD framework detected") + original_python = os.path.join(prefix, "bin/python") shutil.copy(original_python, py_executable) # Copy the framework's dylib into the virtual # environment - virtual_lib = os.path.join(home_dir, '.Python') + virtual_lib = os.path.join(home_dir, ".Python") if os.path.exists(virtual_lib): os.unlink(virtual_lib) - copyfile( - os.path.join(prefix, 'Python'), - virtual_lib) + lib_name = "Python3" if "Python3.framework" in prefix else "Python" + copyfile(os.path.join(prefix, lib_name), virtual_lib, symlink) # And then change the install_name of the copied python executable + search = ( + "@executable_path/../../../../Python3" if "Python3.framework" in prefix else os.path.join(prefix, lib_name) + ) + # noinspection PyBroadException try: - call_subprocess( - ["install_name_tool", "-change", - os.path.join(prefix, 'Python'), - '@executable_path/../.Python', - py_executable]) - except: - logger.fatal( - "Could not call install_name_tool -- you must have Apple's development tools installed") - raise + mach_o_change(py_executable, search, "@executable_path/../.Python") + except Exception: + e = sys.exc_info()[1] + logger.warn("Could not call mach_o_change: %s. " "Trying to call install_name_tool instead.", e) + try: + call_subprocess(["install_name_tool", "-change", search, "@executable_path/../.Python", py_executable]) + except Exception: + logger.fatal("Could not call install_name_tool -- you must " "have Apple's development tools installed") + raise + + if not IS_WIN: + # Ensure that 'python', 'pythonX' and 'pythonX.Y' all exist + py_exe_version_major = "python{}".format(sys.version_info[0]) + py_exe_version_major_minor = "python{}.{}".format(sys.version_info[0], sys.version_info[1]) + py_exe_no_version = "python" + required_symlinks = [py_exe_no_version, py_exe_version_major, py_exe_version_major_minor] - # Some tools depend on pythonX.Y being present - py_executable_version = '%s.%s' % ( - sys.version_info[0], sys.version_info[1]) - if not py_executable.endswith(py_executable_version): - # symlinking pythonX.Y > python - pth = py_executable + '%s.%s' % ( - sys.version_info[0], sys.version_info[1]) - if os.path.exists(pth): - os.unlink(pth) - os.symlink('python', pth) - else: - # reverse symlinking python -> pythonX.Y (with --python) - pth = join(bin_dir, 'python') - if os.path.exists(pth): - os.unlink(pth) - os.symlink(os.path.basename(py_executable), pth) + py_executable_base = os.path.basename(py_executable) + + if py_executable_base in required_symlinks: + # Don't try to symlink to yourself. + required_symlinks.remove(py_executable_base) - if sys.platform == 'win32' 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 - cmd = [py_executable, '-c', """ -import sys -prefix = sys.prefix -if sys.version_info[0] == 3: - prefix = prefix.encode('utf8') -if hasattr(sys.stdout, 'detach'): - sys.stdout = sys.stdout.detach() -elif hasattr(sys.stdout, 'buffer'): - sys.stdout = sys.stdout.buffer -sys.stdout.write(prefix) -"""] - logger.info('Testing executable with %s %s "%s"' % tuple(cmd)) + for pth in required_symlinks: + 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) + + 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"', *cmd) try: - proc = subprocess.Popen(cmd, - stdout=subprocess.PIPE) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) proc_stdout, proc_stderr = proc.communicate() except OSError: e = sys.exc_info()[1] if e.errno == errno.EACCES: - logger.fatal('ERROR: The executable %s could not be run: %s' % (py_executable, e)) + logger.fatal("ERROR: The executable {} could not be run: {}".format(py_executable, e)) sys.exit(100) else: - raise e + raise e proc_stdout = proc_stdout.strip().decode("utf-8") - proc_stdout = os.path.normcase(os.path.abspath(proc_stdout)) - norm_home_dir = os.path.normcase(os.path.abspath(home_dir)) - if hasattr(norm_home_dir, 'decode'): + # normalize paths using realpath to ensure that a virtualenv correctly identifies itself even + # when addressed over a symlink + proc_stdout = os.path.normcase(os.path.realpath(proc_stdout)) + norm_home_dir = os.path.normcase(os.path.realpath(home_dir)) + if hasattr(norm_home_dir, "decode"): norm_home_dir = norm_home_dir.decode(sys.getfilesystemencoding()) if proc_stdout != norm_home_dir: - logger.fatal( - 'ERROR: The executable %s is not functioning' % py_executable) - logger.fatal( - 'ERROR: It thinks sys.prefix is %r (should be %r)' - % (proc_stdout, norm_home_dir)) - logger.fatal( - 'ERROR: virtualenv is not compatible with this system or executable') - if sys.platform == 'win32': + logger.fatal("ERROR: The executable %s is not functioning", py_executable) + logger.fatal("ERROR: It thinks sys.prefix is {!r} (should be {!r})".format(proc_stdout, norm_home_dir)) + logger.fatal("ERROR: virtualenv is not compatible with this system or executable") + if IS_WIN: logger.fatal( - 'Note: some Windows users have reported this error when they ' + "Note: some Windows users have reported this error when they " 'installed Python for "Only this user" or have multiple ' - 'versions of Python installed. Copying the appropriate ' - 'PythonXX.dll to the virtualenv Scripts/ directory may fix ' - 'this problem.') + "versions of Python installed. Copying the appropriate " + "PythonXX.dll to the virtualenv Scripts/ directory may fix " + "this problem." + ) sys.exit(100) else: - logger.info('Got sys.prefix result: %r' % proc_stdout) + logger.info("Got sys.prefix result: %r", proc_stdout) - pydistutils = os.path.expanduser('~/.pydistutils.cfg') + pydistutils = os.path.expanduser("~/.pydistutils.cfg") if os.path.exists(pydistutils): - logger.notify('Please make sure you remove any previous custom paths from ' - 'your %s file.' % pydistutils) - ## FIXME: really this should be calculated earlier + logger.notify("Please make sure you remove any previous custom paths from " "your %s file.", pydistutils) + # FIXME: really this should be calculated earlier + + fix_local_scheme(home_dir, symlink) - fix_local_scheme(home_dir) + if site_packages: + if os.path.exists(site_packages_filename): + logger.info("Deleting %s", site_packages_filename) + os.unlink(site_packages_filename) return py_executable + def install_activate(home_dir, bin_dir, prompt=None): - home_dir = os.path.abspath(home_dir) - if sys.platform == 'win32' or is_jython and os._name == 'nt': - files = { - 'activate.bat': ACTIVATE_BAT, - 'deactivate.bat': DEACTIVATE_BAT, - 'activate.ps1': ACTIVATE_PS, - } + if IS_WIN: + files = {"activate.bat": ACTIVATE_BAT, "deactivate.bat": DEACTIVATE_BAT, "activate.ps1": ACTIVATE_PS} # MSYS needs paths of the form /c/path/to/file - drive, tail = os.path.splitdrive(home_dir.replace(os.sep, '/')) - home_dir_msys = (drive and "/%s%s" or "%s%s") % (drive[:1], tail) + drive, tail = os.path.splitdrive(home_dir.replace(os.sep, "/")) + home_dir_msys = (drive and "/{}{}" or "{}{}").format(drive[:1], tail) # Run-time conditional enables (basic) Cygwin compatibility - home_dir_sh = ("""$(if [ "$OSTYPE" "==" "cygwin" ]; then cygpath -u '%s'; else echo '%s'; fi;)""" % - (home_dir, home_dir_msys)) - files['activate'] = ACTIVATE_SH.replace('__VIRTUAL_ENV__', home_dir_sh) + home_dir_sh = """$(if [ "$OSTYPE" "==" "cygwin" ]; then cygpath -u '{}'; else echo '{}'; fi;)""".format( + home_dir, home_dir_msys + ) + files["activate"] = ACTIVATE_SH.replace("__VIRTUAL_ENV__", home_dir_sh) else: - files = {'activate': ACTIVATE_SH} + files = { + "activate": ACTIVATE_SH, + "activate.fish": ACTIVATE_FISH, + "activate.csh": ACTIVATE_CSH, + "activate.ps1": ACTIVATE_PS, + } + files["activate_this.py"] = ACTIVATE_THIS - # suppling activate.fish in addition to, not instead of, the - # bash script support. - files['activate.fish'] = ACTIVATE_FISH + if sys.version_info >= (3, 4): + # Add xonsh support + files["activate.xsh"] = ACTIVATE_XSH - # same for csh/tcsh support... - files['activate.csh'] = ACTIVATE_CSH + install_files(home_dir, bin_dir, prompt, files) + - files['activate_this.py'] = ACTIVATE_THIS - if hasattr(home_dir, 'decode'): +def install_files(home_dir, bin_dir, prompt, files): + if hasattr(home_dir, "decode"): home_dir = home_dir.decode(sys.getfilesystemencoding()) - vname = os.path.basename(home_dir) + virtualenv_name = os.path.basename(home_dir) for name, content in files.items(): - content = content.replace('__VIRTUAL_PROMPT__', prompt or '') - content = content.replace('__VIRTUAL_WINPROMPT__', prompt or '(%s)' % vname) - content = content.replace('__VIRTUAL_ENV__', home_dir) - content = content.replace('__VIRTUAL_NAME__', vname) - content = content.replace('__BIN_NAME__', os.path.basename(bin_dir)) + content = content.replace("__VIRTUAL_PROMPT__", prompt or "") + content = content.replace("__VIRTUAL_WINPROMPT__", prompt or "({}) ".format(virtualenv_name)) + content = content.replace("__VIRTUAL_ENV__", home_dir) + content = content.replace("__VIRTUAL_NAME__", virtualenv_name) + content = content.replace("__BIN_NAME__", os.path.basename(bin_dir)) + content = content.replace("__PATH_SEP__", os.pathsep) writefile(os.path.join(bin_dir, name), content) + +def install_python_config(home_dir, bin_dir, prompt=None): + if IS_WIN: + files = {} + else: + files = {"python-config": PYTHON_CONFIG} + install_files(home_dir, bin_dir, prompt, files) + for name, _ in files.items(): + make_exe(os.path.join(bin_dir, name)) + + def install_distutils(home_dir): distutils_path = change_prefix(distutils.__path__[0], home_dir) mkdir(distutils_path) - ## FIXME: maybe this prefix setting should only be put in place if - ## there's a local distutils.cfg with a prefix setting? - home_dir = os.path.abspath(home_dir) - ## FIXME: this is breaking things, removing for now: - #distutils_cfg = DISTUTILS_CFG + "\n[install]\nprefix=%s\n" % home_dir - writefile(os.path.join(distutils_path, '__init__.py'), DISTUTILS_INIT) - writefile(os.path.join(distutils_path, 'distutils.cfg'), DISTUTILS_CFG, overwrite=False) + # FIXME: maybe this prefix setting should only be put in place if + # there's a local distutils.cfg with a prefix setting? + # FIXME: this is breaking things, removing for now: + # distutils_cfg = DISTUTILS_CFG + "\n[install]\nprefix=%s\n" home_dir + writefile(os.path.join(distutils_path, "__init__.py"), DISTUTILS_INIT) + writefile(os.path.join(distutils_path, "distutils.cfg"), DISTUTILS_CFG, overwrite=False) -def fix_local_scheme(home_dir): + +def fix_local_scheme(home_dir, symlink=True): """ Platforms that use the "posix_local" install scheme (like Ubuntu with Python 2.7) need to be given an additional "local" location, sigh. @@ -1491,183 +1803,240 @@ except ImportError: pass else: - if sysconfig._get_default_scheme() == 'posix_local': - local_path = os.path.join(home_dir, 'local') + # noinspection PyProtectedMember + if sysconfig._get_default_scheme() == "posix_local": + local_path = os.path.join(home_dir, "local") if not os.path.exists(local_path): os.mkdir(local_path) for subdir_name in os.listdir(home_dir): - if subdir_name == 'local': + if subdir_name == "local": continue - os.symlink(os.path.abspath(os.path.join(home_dir, subdir_name)), \ - os.path.join(local_path, subdir_name)) + copyfile( + os.path.abspath(os.path.join(home_dir, subdir_name)), + os.path.join(local_path, subdir_name), + symlink, + ) -def fix_lib64(lib_dir): + +def fix_lib64(lib_dir, symlink=True): """ Some platforms (particularly Gentoo on x64) put things in lib64/pythonX.Y instead of lib/pythonX.Y. If this is such a platform we'll just create a symlink so lib64 points to lib """ - if [p for p in distutils.sysconfig.get_config_vars().values() - if isinstance(p, basestring) and 'lib64' in p]: - logger.debug('This system uses lib64; symlinking lib64 to lib') - assert os.path.basename(lib_dir) == 'python%s' % sys.version[:3], ( - "Unexpected python lib dir: %r" % lib_dir) - lib_parent = os.path.dirname(lib_dir) - assert os.path.basename(lib_parent) == 'lib', ( - "Unexpected parent dir: %r" % lib_parent) - copyfile(lib_parent, os.path.join(os.path.dirname(lib_parent), 'lib64')) + # PyPy's library path scheme is not affected by this. + # Return early or we will die on the following assert. + if IS_PYPY: + logger.debug("PyPy detected, skipping lib64 symlinking") + return + # Check we have a lib64 library path + if not [p for p in distutils.sysconfig.get_config_vars().values() if isinstance(p, basestring) and "lib64" in p]: + return + + logger.debug("This system uses lib64; symlinking lib64 to lib") + + assert os.path.basename(lib_dir) == PY_VERSION, "Unexpected python lib dir: {!r}".format(lib_dir) + lib_parent = os.path.dirname(lib_dir) + top_level = os.path.dirname(lib_parent) + lib_dir = os.path.join(top_level, "lib") + lib64_link = os.path.join(top_level, "lib64") + assert os.path.basename(lib_parent) == "lib", "Unexpected parent dir: {!r}".format(lib_parent) + if os.path.lexists(lib64_link): + return + if symlink: + os.symlink("lib", lib64_link) + else: + copyfile(lib_dir, lib64_link, symlink=False) + 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(os.path.join(path, exe)): - exe = os.path.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 {} (from --python={}) does not exist".format(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 {} (from --python={}) is not an executable file".format(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: +# 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. """ home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) - activate_this = os.path.join(bin_dir, 'activate_this.py') + activate_this = os.path.join(bin_dir, "activate_this.py") if not os.path.exists(activate_this): logger.fatal( - 'The environment doesn\'t have a file %s -- please re-run virtualenv ' - 'on this environment to update it' % activate_this) - fixup_scripts(home_dir) + "The environment doesn't have a file %s -- please re-run virtualenv " "on this environment to update it", + activate_this, + ) + fixup_scripts(home_dir, bin_dir) fixup_pth_and_egg_link(home_dir) - ## FIXME: need to fix up distutils.cfg + # FIXME: need to fix up distutils.cfg + -OK_ABS_SCRIPTS = ['python', 'python%s' % sys.version[:3], - 'activate', 'activate.bat', 'activate_this.py'] +OK_ABS_SCRIPTS = [ + "python", + PY_VERSION, + "activate", + "activate.bat", + "activate_this.py", + "activate.fish", + "activate.csh", + "activate.xsh", +] -def fixup_scripts(home_dir): + +def fixup_scripts(_, bin_dir): + if IS_WIN: + new_shebang_args = ("{} /c".format(os.path.normcase(os.environ.get("COMSPEC", "cmd.exe"))), "", ".exe") + else: + new_shebang_args = ("/usr/bin/env", VERSION, "") + # This is what we expect at the top of scripts: - shebang = '#!%s/bin/python' % os.path.normcase(os.path.abspath(home_dir)) + shebang = "#!{}".format( + os.path.normcase(os.path.join(os.path.abspath(bin_dir), "python{}".format(new_shebang_args[2]))) + ) # This is what we'll put: - new_shebang = '#!/usr/bin/env python%s' % sys.version[:3] - activate = "import os; activate_this=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'activate_this.py'); execfile(activate_this, dict(__file__=activate_this)); del os, activate_this" - if sys.platform == 'win32': - bin_suffix = 'Scripts' - else: - bin_suffix = 'bin' - bin_dir = os.path.join(home_dir, bin_suffix) - home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) + new_shebang = "#!{} python{}{}".format(*new_shebang_args) + for filename in os.listdir(bin_dir): filename = os.path.join(bin_dir, filename) if not os.path.isfile(filename): - # ignore subdirs, e.g. .svn ones. + # ignore child directories, e.g. .svn ones. continue - f = open(filename, 'rb') - try: + with open(filename, "rb") as f: try: - lines = f.read().decode('utf-8').splitlines() + lines = f.read().decode("utf-8").splitlines() except UnicodeDecodeError: # This is probably a binary program instead # of a script, so just ignore it. continue - finally: - f.close() if not lines: - logger.warn('Script %s is an empty file' % filename) + logger.warn("Script %s is an empty file", filename) continue - if not lines[0].strip().startswith(shebang): + + old_shebang = lines[0].strip() + old_shebang = old_shebang[0:2] + os.path.normcase(old_shebang[2:]) + + if not old_shebang.startswith(shebang): if os.path.basename(filename) in OK_ABS_SCRIPTS: - logger.debug('Cannot make script %s relative' % filename) + logger.debug("Cannot make script %s relative", filename) elif lines[0].strip() == new_shebang: - logger.info('Script %s has already been made relative' % filename) + logger.info("Script %s has already been made relative", filename) else: - logger.warn('Script %s cannot be made relative (it\'s not a normal script that starts with %s)' - % (filename, shebang)) + logger.warn( + "Script %s cannot be made relative (it's not a normal script that starts with %s)", + filename, + shebang, + ) continue - logger.notify('Making script %s relative' % filename) - lines = [new_shebang+'\n', activate+'\n'] + lines[1:] - f = open(filename, 'wb') - f.write('\n'.join(lines).encode('utf-8')) - f.close() + logger.notify("Making script %s relative", filename) + script = relative_script([new_shebang] + lines[1:]) + with open(filename, "wb") as f: + f.write("\n".join(script).encode("utf-8")) + + +def relative_script(lines): + """Return a script that'll work in a relocatable environment.""" + activate = ( + "import os; " + "activate_this=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'activate_this.py'); " + "exec(compile(open(activate_this).read(), activate_this, 'exec'), { '__file__': activate_this}); " + "del os, activate_this" + ) + # Find the last future statement in the script. If we insert the activation + # line before a future statement, Python will raise a SyntaxError. + activate_at = None + for idx, line in reversed(list(enumerate(lines))): + if line.split()[:3] == ["from", "__future__", "import"]: + activate_at = idx + 1 + break + if activate_at is None: + # Activate after the shebang. + activate_at = 1 + return lines[:activate_at] + ["", activate, ""] + lines[activate_at:] + def fixup_pth_and_egg_link(home_dir, sys_path=None): """Makes .pth and .egg-link files use relative paths""" home_dir = os.path.normcase(os.path.abspath(home_dir)) if sys_path is None: sys_path = sys.path - for path in sys_path: - if not path: - path = '.' - if not os.path.isdir(path): + for a_path in sys_path: + if not a_path: + a_path = "." + if not os.path.isdir(a_path): continue - path = os.path.normcase(os.path.abspath(path)) - if not path.startswith(home_dir): - logger.debug('Skipping system (non-environment) directory %s' % path) + a_path = os.path.normcase(os.path.abspath(a_path)) + if not a_path.startswith(home_dir): + logger.debug("Skipping system (non-environment) directory %s", a_path) continue - for filename in os.listdir(path): - filename = os.path.join(path, filename) - if filename.endswith('.pth'): + for filename in os.listdir(a_path): + filename = os.path.join(a_path, filename) + if filename.endswith(".pth"): if not os.access(filename, os.W_OK): - logger.warn('Cannot write .pth file %s, skipping' % filename) + logger.warn("Cannot write .pth file %s, skipping", filename) else: fixup_pth_file(filename) - if filename.endswith('.egg-link'): + if filename.endswith(".egg-link"): if not os.access(filename, os.W_OK): - logger.warn('Cannot write .egg-link file %s, skipping' % filename) + logger.warn("Cannot write .egg-link file %s, skipping", filename) else: fixup_egg_link(filename) + def fixup_pth_file(filename): lines = [] - prev_lines = [] - f = open(filename) - prev_lines = f.readlines() - f.close() + with open(filename) as f: + prev_lines = f.readlines() for line in prev_lines: line = line.strip() - if (not line or line.startswith('#') or line.startswith('import ') - or os.path.abspath(line) != line): + if not line or line.startswith("#") or line.startswith("import ") or os.path.abspath(line) != line: lines.append(line) else: new_value = make_relative_path(filename, line) if line != new_value: - logger.debug('Rewriting path %s as %s (in %s)' % (line, new_value, filename)) + logger.debug("Rewriting path {} as {} (in {})".format(line, new_value, filename)) lines.append(new_value) if lines == prev_lines: - logger.info('No changes to .pth file %s' % filename) + logger.info("No changes to .pth file %s", filename) return - logger.notify('Making paths in .pth file %s relative' % filename) - f = open(filename, 'w') - f.write('\n'.join(lines) + '\n') - f.close() + logger.notify("Making paths in .pth file %s relative", filename) + with open(filename, "w") as f: + f.write("\n".join(lines) + "\n") + def fixup_egg_link(filename): - f = open(filename) - link = f.read().strip() - f.close() + with open(filename) as f: + link = f.readline().strip() if os.path.abspath(link) != link: - logger.debug('Link in %s already relative' % filename) + logger.debug("Link in %s already relative", filename) return new_link = make_relative_path(filename, link) - logger.notify('Rewriting link %s in %s as %s' % (link, filename, new_link)) - f = open(filename, 'w') - f.write(new_link) - f.close() + logger.notify("Rewriting link {} in {} as {}".format(link, filename, new_link)) + with open(filename, "w") as f: + f.write(new_link) + def make_relative_path(source, dest, dest_is_directory=True): """ @@ -1687,6 +2056,8 @@ if not dest_is_directory: dest_filename = os.path.basename(dest) dest = os.path.dirname(dest) + else: + dest_filename = None dest = os.path.normpath(os.path.abspath(dest)) source = os.path.normpath(os.path.abspath(source)) dest_parts = dest.strip(os.path.sep).split(os.path.sep) @@ -1694,20 +2065,20 @@ while dest_parts and source_parts and dest_parts[0] == source_parts[0]: dest_parts.pop(0) source_parts.pop(0) - full_parts = ['..']*len(source_parts) + dest_parts - if not dest_is_directory: + full_parts = [".."] * len(source_parts) + dest_parts + if not dest_is_directory and dest_filename is not None: full_parts.append(dest_filename) if not full_parts: # Special case for the current directory (otherwise it'd be '') - return './' + return "./" return os.path.sep.join(full_parts) +FILE_PATH = __file__ if os.path.isabs(__file__) else os.path.join(os.getcwd(), __file__) -############################################################ -## Bootstrap script creation: -def create_bootstrap_script(extra_text, python_version=''): +# Bootstrap script creation: +def create_bootstrap_script(extra_text, python_version=""): """ Creates a bootstrap script, which is like this script but with extend_parser, adjust_options, and after_install hooks. @@ -1742,533 +2113,522 @@ This example immediately installs a package, and runs a setup script from that package. - If you provide something like ``python_version='2.4'`` then the - script will start with ``#!/usr/bin/env python2.4`` instead of + If you provide something like ``python_version='2.5'`` then the + script will start with ``#!/usr/bin/env python2.5`` instead of ``#!/usr/bin/env python``. You can use this when the script must be run with a particular Python version. """ - filename = __file__ - if filename.endswith('.pyc'): + filename = FILE_PATH + if filename.endswith(".pyc"): filename = filename[:-1] - f = open(filename, 'rb') - content = f.read() - f.close() - py_exe = 'python%s' % python_version - content = (('#!/usr/bin/env %s\n' % py_exe) - + '## WARNING: This file is generated\n' - + content) - return content.replace('##EXT' 'END##', extra_text) + with codecs.open(filename, "r", encoding="utf-8") as f: + content = f.read() + py_exe = "python{}".format(python_version) + content = "#!/usr/bin/env {}\n# WARNING: This file is generated\n{}".format(py_exe, content) + # we build the string as two, to avoid replacing here, but yes further done + return content.replace("# EXTEND - " "bootstrap here", extra_text) -##EXTEND## + +# EXTEND - bootstrap here + def convert(s): - b = base64.b64decode(s.encode('ascii')) - return zlib.decompress(b).decode('utf-8') + b = base64.b64decode(s.encode("ascii")) + return zlib.decompress(b).decode("utf-8") -##file site.py -SITE_PY = convert(""" -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 -Q7wQmAIMcman2gOwRiH4P2ts6wE= -""") - -##file ez_setup.py -EZ_SETUP_PY = convert(""" -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 -BDaonX65d/fwEjNqlDjLVIvM9X+XVxF7 -""") -##file distribute_setup.py -DISTRIBUTE_SETUP_PY = convert(""" -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 -""") - -##file activate.sh -ACTIVATE_SH = convert(""" -eJytVVFvokAQfudXTLEP2pw1fW3jg01NNGm1KV4vd22zrDDIJrhrYJHay/33m0VEKGpyufIg7s63 -M9/OfDO0YBaKBAIRISzTRMMcIU3Qh0zoEOxEpbGHMBeyxz0t1lyjDRdBrJYw50l4YbVgo1LwuJRK -Q5xKEBp8EaOno41l+bg7Be0O/LaAnhbEmKAGFfmAci1iJZcoNax5LPg8wiRHiQBeoCvBPmfT+zv2 -PH6afR/cs8fBbGTDG9yADlHmSPOY7f4haInA95WKdQ4s91JpeDQO5fZAnKTxczaaTkbTh+EhMqWx -QWl/rEGsNJ2kV0cRySKleRGTUKWUVB81pT+vD3Dpw0cSfoMsFF4IIV8jcHqRyVPLpTHrkOu89IUr -EoDHo4gkoBUsiAFVlP4FKjaLFSeNFEeTS4AfJBOV6sKshVwUbmpAkyA4N8kFL+RygQlkpDfum58N -GO1QWNLFipij/yn1twOHit5V29UvZ8Seh0/OeDo5kPz8at24lp5jRXSuDlXPuWqUjYCNejlXJwtV -mHcUtpCddTh53hM7I15EpA+2VNLHRMep6Rn8xK0FDkYB7ABnn6J3jWnXbLvQfyzqz61dxDFGVP1a -o1Xasx7bsipU+zZjlSVjtlUkoXofq9FHlMZtDxaLCrrH2O14wiaDhyFj1wWs2qIl773iTbZohyza -iD0TUQQBF5HZr6ISgzKKNZrD5UpvgO5FwoT2tgkIMec+tcYm45sO+fPytqGpBy75aufpTG/gmhRb -+u3AjQtC5l1l7QV1dBAcadt+7UhFGpXONprZRviAWtbY3dgZ3N4P2ePT9OFxdjJiruJSuLk7+31f -x60HKiWc9eH9SBc04XuPGCVYce1SXlDyJcJrjfKr7ebSNpEaQVpg+l3wiAYOJZ9GCAxoR9JMWAiv -+IyoWBSfhOIIIoRar657vSzLLj9Q0xRZX9Kk6SUq0BmPsceNl179Mi8Vii65Pkj21XXf4MAlSy/t -Exft7A8WX4/iVRkZprZfNK2/YFL/55T+9wm9m86Uhr8A0Hwt -""") - -##file activate.fish -ACTIVATE_FISH = convert(""" -eJydVm1v4jgQ/s6vmA1wBxUE7X2stJVYlVWR2lK13d6d9laRk0yIr8HmbIe0++tvnIQQB9pbXT5A -Ys/LM55nZtyHx5RrSHiGsMm1gRAh1xhDwU0Kng8hFzMWGb5jBv2E69SDs0TJDdj3MxilxmzPZzP7 -pVPMMl+q9bjXh1eZQ8SEkAZULoAbiLnCyGSvvV6SC7IoBcS4Nw0wjcFbvJDcjiuTswzFDpiIQaHJ -lQAjQUi1YRmUboC2uZJig8J4PaCnT5IaDcgsbm/CjinOwgx1KcUTMEhhTgV4g2B1fRk8Le8fv86v -g7v545UHpZB9rKnp+gXsMhxLunIIpwVQxP/l9c/Hq9Xt1epm4R27bva6AJqN92G4YhbMG2i+LB+u -grv71c3dY7B6WtzfLy9bePbp0taDTXSwJQJszUnnp0y57mvpPcrF7ZODyhswtd59+/jdgw+fwBNS -xLSscksUPIDqwwNmCez3PpxGeyBYg6HE0YdcWBxcKczYzuVJi5Wu915vn5oWePCCoPUZBN5B7IgV -MCi54ZDLG7TUZ0HweXkb3M5vFmSpFm/gthhBx0UrveoPpv9AJ9unIbQYdUoe21bKg2q48sPFGVwu -H+afrxd1qvclaNlRFyh1EQ2sSccEuNAGWQwysfVpz1tPajUqbqJUnEcIJkWo6OXDaodK8ZiLdbmM -L1wb+9H0D+pcyPSrX5u5kgWSygRYXCnJUi/KKcuU4cqsAyTKZBiissLc7NFwizvjxtieKBVCIdWz -fzilzPaYyljZN0cGN1v7NnaIPNCGmVy3GKuJaQ6iVjE1Qfm+36hglErwmnAD8hu0dDy4uICBA8ZV -pQr/q/+O0KFW2kjelu9Dgb9SDBsWV4F4x5CswgS0zBVlk5tDMP5bVtUGpslbm81Lu2sdKq7uNMGh -MVQ4fy9xhogC1lS5guhISa0DlBWv0O8odT6/LP+4WZzDV6FzIkEqC0uolGZSZoMnlpxplmD2euaT -O4hkTpPnbztDccey0bhjDaBIqaWQa0uwEtQEwtyU56i4fq54F9IE3ORR6mKriODM4XOYZwaVYLYz -7SPbKkz4i7VkB6/Ot1upDE3znNqYKpM8raa0Bx8vfvntJ32UENsM4aI6gJL+jJwhxhh3jVIDOcpi -m0r2hmEtS8XXXNBk71QCDXTBNhhPiHX2LtHkrVIlhoEshH/EZgdq53Eirqs5iFKMnkOmqZTtr3Xq -djvPTWZT4S3NT5aVLgurMPUWI07BRVYqkQrmtCKohNY8qu9EdACoT6ki0a66XxVF4f9AQ3W38yO5 -mWmZmIIpnDFrbXakvKWeZhLwhvrbUH8fahhqD0YUcBDJjEBMQwiznE4y5QbHrbhHBOnUAYzb2tVN -jJa65e+eE2Ya30E2GurxUP8ssA6e/wOnvo3V78d3vTcvMB3n7l3iX1JXWqk= -""") +# file site.py +SITE_PY = convert( + """ +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 +""" +) -##file activate.csh -ACTIVATE_CSH = convert(""" -eJx9U11vmzAUffevOCVRu+UB9pws29Kl0iq1aVWllaZlcgxciiViItsQdb9+xiQp+dh4QOB7Pu49 -XHqY59IgkwVhVRmLmFAZSrGRNkdgykonhFiqSCRW1sJSmJg8wCDT5QrucRCyHn6WFRKhVGmhKwVp -kUpNiS3emup3TY6XIn7DVNQyJUwlrgthJD6n/iCNv72uhCzCpFx9CRkThRQGKe08cWXJ9db/yh/u -pvzl9mn+PLnjj5P5D1yM8QmXlzBkSdXwZ0H/BBc0mEo5FE5qI2jKhclHOOvy9HD/OO/6YO1mX9vx -sY0H/tPIV0dtqel0V7iZvWyNg8XFcBA0ToEqVeqOdNUEQFvN41SumAv32VtJrakQNSmLWmgp4oJM -yDoBHgoydtoEAs47r5wHHnUal5vbJ8oOI+9wI86vb2d8Nrm/4Xy4RZ8R85E4uTZPB5EZPnTaaAGu -E59J8BE2J8XgrkbLeXMlVoQxznEYFYY8uFFdxsKQRx90Giwx9vSueHP1YNaUSFG4vTaErNSYuBOF -lXiVyXa9Sy3JdClEyK1dD6Nos9mEf8iKlOpmqSNTZnYjNEWiUYn2pKNB3ttcLJ3HmYYXy6Un76f7 -r8rRsC1TpTJj7f19m5sUf/V3Ir+x/yjtLu8KjLX/CmN/AcVGUUo= -""") +# file activate.sh +ACTIVATE_SH = convert( + """ +eJytVW1P2zAQ/u5fcaQVG9s6VvjGVLQyKoEELWo6JjZQMMmVWEudynYK5eW/75w3kgbYh9EPbe27 +s5977p5zCyah0DAVEcIs0QauEBKNAdwIE4Kj40T5CFdCbnLfiAU36MCHqYpncMV1+IG1YBkn4HMp +YwMqkSAMBEKhb6IlY0xM4Tc47fu9vnvguaMf4++DzqMDPdr74sDFVzAhSgb0QT+MwTmjw1IY+cXG +gtO+EnOzA+ftYtsG765vZYG3dOX2NpsKxgIsUML7DbhP7YnUaKAzhfkyiH3Y3QxwsSmTKIKt3fUu +S31aoNB6xVEAKBdCxXKG0sCCK8GvItS51xpl07mD9v1pf/zRe4QLijOJkhqMShAoWzIAQQ7Qj7gi +GrkBHkVpOFnzeCLEGx3te6eH48mP/pF30p8c7NB5xAhUKLEfa+o57Ya7U3rg7TxWJnUs97KcG0Gp +nXj6B5qzycFoeDA6HrwAqbQ3gJWWJrzS9CrIupctaUZ82qQ6jBMqUICG2ivtP+AygDsdfoKbUPgh +hHyBwOmHTH48m1mzCakGtqfyo6jBfSoJ1cbEcE0IqH3o3zRWdjHn1Hx5qP4M8JNkECcmNxshr/Nj +ao6WIGhbisEPubxGDTekJx7YryVYbdC11GNzQo5BUQCiXxbq6KRUPzyUm79IMaeDsXs4GnaeK0Oa +ZEdRF5cdXSPtlQK73Rcq63YbJXW7zVq63VeLmJsLIJlLYR0MT5/SX7PYuvlEkLEMUJOQrIRxBV4L +XIymUDisrQDoWFOh/eL2R0bjKbMLpTDCBa9pujIt6nczVkHbczyvsvQ8h+U8VFNiDbERk5lQ80XF +e9Pz9g6H3rB/PPC8ndytquMS95MgLGG0w2plfU2qLwjLwlqR6epV6SjN2jO9pZr9/qHb3zsaeCfj +0fHJpNGYq43QsyBdW+Gnoju3T4RmxxCnsNaD2xc6suleOxQjjfWA95c0HFDyGcJ5jfhz53IDasH5 +NKx0tk2+Bcf8D4JOFNrZkEgeCa7zF4SSEOadprmukAdLC1ghq3pUJFl9b9bXV04itdt3g7FsWT5Z +8yUNHQmdWe7ntL85WTe/yRx8gxn4n/Pvf2bfc3OPavYXWw6XFQ== +""" +) -##file activate.bat -ACTIVATE_BAT = convert(""" -eJyFUkEKgzAQvAfyhz0YaL9QEWpRqlSjWGspFPZQTevFHOr/adQaU1GaUzI7Mzu7ZF89XhKkEJS8 -qxaKMMsvboQ+LxxE44VICSW1gEa2UFaibqoS0iyJ0xw2lIA6nX5AHCu1jpRsv5KRjknkac9VLVug -sX9mtzxIeJDE/mg4OGp47qoLo3NHX2jsMB3AiDht5hryAUOEifoTdCXbSh7V0My2NMq/Xbh5MEjU -ZT63gpgNT9lKOJ/CtHsvT99re3pX303kydn4HeyOeAg5cjf2EW1D6HOPkg9NGKhu -""") +# file activate.fish +ACTIVATE_FISH = convert( + """ +eJytVm1v2zYQ/q5fcZUdyClqGVuHfQgwDGnjIQYSO3DcAMM6yLREWxxo0iMpty7243ekLImy5RQY +lg+xTd7rc3cPrweLnGlYM05hW2gDKwqFphn+Y2IDSy0LlVJYMTEiqWF7Ymi8ZjpfwtsvzORMAAFV +CGGF7TkMIDdmdzMa2V86p5zHqdzCNWiqNZPibRz04E6CkMYqAjOQMUVTww9xEKwLgV6kgGRFdM7W +h2RHTA7DDMKPUuypMhodOkfuwkjQckttIBuwKpASAWhObgT7RsMA8E9T41SOxvpEbfb1hVWqLhqh +P37400mspXKO8FAZwGx9mR/jeHiU67AW9psfN/3aSBkSFVn5meYSPMHAXngoWG+XUHDpnqPgwOlA +oXRlc4d/wCiIbiKIPovoxGVGqzpbf9H4KxZoz5QpCKdiD1uZUSAiQ+umUMK6NjnFaqot4ZgWikqx +pcLEkfPaQ0ELjOSZfwt7ohhZcaqdFFuDodh8Q4GwJbOHu+RlMl98un1Inm4X92ENcc81l8bu2mDz +FSvbWq7Rhq7T/K9M64Lq0U/vfwbCDVXY0tYW5Bg8R5xqm5XvQQnQb5Pn++RlPH+ezKYlUGEcQvhZ +hNfYFDDkBt7XulXZh5uvpfVBu2LnuVzXupRretnQuWajeOydWodCt7Ar7Hfg/X1xP5vezx7HYXAW +R313Gk198XocbbFXonGYP81nj0+LZIbYzyd3TTy22aru1DD8GxLsJQdzslNyuzNedzxjGNj5FE8P +wGWKLbl0E5tUFlxdlrZtCefyi2teRbdyj6JyDUvP7rLiQM87XcbtnDmcmw+8iMaKaOoNUKRPfJSz +pI1U1AUjFdswQXjjx3cPXXl7AukZOt/ToJfx9IvaVaJ2WY/SVfXH05d2uUPHPThDIbz5BSIhRYbH +qrBsQ6NWEfl6WN296Y55d8hk2n3VENiFdP2X5YKIP8R1li7THnwSNlOmFOV0T3wqiwOPPNv5BUE1 +VR4+ECaJ9zNJQmv//2O4/8hsVaRnpILs1nqV+yWh1UR2WdFJOgBbJBf2vfRHSfJhMk2mt49jROKo +UuO97Dd0srQ9hYdxUH5aUjghm+5QPELrkqe+lfar2PTb7mByPBhuy7PjNuGka2L71s4suZs83354 +GB/nJzw+jB/l7uBGPi2wmbCR2sRQ+yZIGaczeqSh1uT7Q3820y3xTk7AwSN72gqorw0xhX7n1iBP +R6MUsXub3nFywBXujBSt+1K5MuKT4lMZpMRNRjHcJ9DqHj+zXz2ZydquiO/gL7uU7hTdIcQuOH+L +EGRLG9/+w9JM1pG0kuaBc2VUTJg1RFf6Sked4jDAXJJUcsy9XG9eebsbc4MrfQ1RhzIMcHiojbjd +HaFntuLSEoJ5x7NQw1nr2NkOqV3T+g3qIQ54ubrXgp0032bvamC6yP4kaNfx/wIsXsYv +""" +) -##file deactivate.bat -DEACTIVATE_BAT = convert(""" -eJxzSE3OyFfIT0vj4spMU0hJTcvMS01RiPf3cYkP8wwKCXX0iQ8I8vcNCFHQ4FIAguLUEgWIgK0q -FlWqXJpcICVYpGzx2BAZ4uHv5+Hv6wq1BWINXBTdKriEKkI1DhW2QAfhttcxxANiFZCBbglQSJUL -i2dASrm4rFz9XLgAwJNbyQ== -""") +# file activate.csh +ACTIVATE_CSH = convert( + """ +eJx9VNtO4zAQffdXDKEiUEFhX8t22bJFWqRyEVuQVkKy3Hi6sZQ44Dit+sK379hJittG5KGqPZdz +fOZyCLNUlbBQGUJelRbmCFWJElbKphCVRWUShLnS5yKxaiksDpIyjaC/MEUO9Lc/YIfwt6ggEVoX +FkylQVmQymBis7Wz/jJIcRLma5iIpZIIEwXXmSgVfJf+Qs5//suFygZJkf8YMFaiBY2rTGkcxa8s +ZkxkSpQgsWUBsUVi27viD9MJf7l9mj2Pp/xxPPsNByO4gKMjoCSol+Dvot6e3/A9cl6VdmB71ksw +mIoyvYROnKeHu8dZiARvpMebHe0CeccvoLz9sjY5tq3h5v6lgY5eD4b9yGFFutCSrkzlRMAm554y +we3bWhYJqXcIzx5bGYMZLoW2sBRGiXmG5YAFsdsIvhA7rCDiPDhyHtXl2lOQpGhkZtuVCKKH7+ec +X9/e8/vx3Q3nw00EfWoBxwFWrRTBeSWiE7Apagb0OXRKz7XIEUbQFcMwK7HLOT6OtwlZQo9PIGao +pVrULKj64Ysnt3/G19ObtgkCJrXzF74jRz2MaCnJgtcN5B7wLfK2DedOp4vGydPcet5urq2XBEZv +DcnQpBZVJt0KUBqEa4YzpS0a3x7odFOm0Dlqe9oEkN8qVUlK01/iKfSa3LRRKmqkBc2vBKFpmyCs +XG4d2yYyEQZBzIvKOgLN+JDveiVoaXyqedVYOkTrmCRqutrfNVHr6xMFBhh9QD/qNQuGLvq72d03 +3Jy2CtGCf0rca/tp+N4BXqsflKquRr0L2sjmuClOu+/8/NKvTQsNZ3l9ZqxeTew//1a6EA== +""" +) -##file activate.ps1 -ACTIVATE_PS = convert(""" -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 -QastYw== -""") +# file activate.xsh +ACTIVATE_XSH = convert( + """ +eJyNU11PwjAUfe+vuNY9sIj7ASQ+YCSBRD6i02gIaSq7gyWjXdqyaIz/3XYwVmB+9GFZ78c57T2n +lNIXKfQa+NJkJTcIeqmywkAqFZSZMlueoygppSRVcgPvrjgyUuYask0hlYEVGqaxAK6B7f8JSTAF +lmCN2uFqpcMeAbuyFGjxkcglhUwAzzOuUe9SbiWY18H5vm5B6sbgM4qir8jSdCib3t+x59FD/NS/ +Z7N+PKRdoDRskAIXhBsIziqPyFrSf9O9xsPpZDgdD85JD6lz6kPqtwM0RYdx1bnB5Lka2u5cxzML +vKLWTjZ7mI5n8b8A9rUNjpAiQW3U1gmKFIQ0lXpW1gblEh4xT6EuvGjXtHGFE5ZcwlZotGhKYY4l +FwZKrjL+lqMmvoXmp4dYhKQV1M7d6yPEv5jNKcqYf1VGbcmZB5x4lRcCfzfvLXaBiCdJ5wj46uD+ +Tmg3luR2NGGT/nhgGbpgX48wN7HaYhcUFjlfYrULCTkxWru36jF59rJ9NlJlf7JQde5j11VS+yZr +0d22eUPaxdycLKMTvqWjR3610emDtgTu36ylcJe83rhv/di/AYN1UZY= +""" +) + +# file activate.bat +ACTIVATE_BAT = convert( + """ +eJyVk1FLhEAUhd8X/A8XWSkf28dCyMUpBR3FzAiCS+WYwq4TOdXfb0Z3dTJdyCfveO85n8frNXut +OPCyNFbGqmUCzDxIs3s3REJzB1GrEE3VVJdQsLJuWAEYh97QkaRxlGRwbqxAXp1Uf+RYM32W1LKB +7Vp2nJC6DReD9m+5qeQ6Wd+a/SN7dlzn9oI7dxsSXJCcoXOskfLgYXdv/j8LnXiM8iGg/RmiZmOr +bFMSgcebMwGfKhgbBIfnL14X8P7BX3Zs38J3LSoQFdtD3YCVuJlvnfgmj5kfUz+OCLxxqUWoF9zk +qtYAFyZkBsO9ArzUh/td0ZqP9IskElTFMsnwb4/GqeoLPUlZT5dJvf8Id5hQIONynUSa2G0Wc+m8 +Z+w2w4/Tt2hbYT0hbgOK1I0I4tUw/QOTZfLE +""" +) + +# file deactivate.bat +DEACTIVATE_BAT = convert( + """ +eJyFkN0KgkAUhO8F32EQpHqFQEjQUPAPMaErqVxzId3IrV6/3ST/UDp3c86c4WN25FIysKJQFVVp +CEfqxsnB9DI7SA25i20fFqtXHM+GYL0BZzi9GM1xf7DzjVQN3pSX4CWpQGvokZk4uqrQAjXjyElB +a5IjCz0r+2VHcehHCa5MZNmB5e7TdqMqECMptHZh6DN/utb7Zs6CejsO/QNzTJwwcELfHgJJPcTp +TFOk7rCM1f92aG38HzBR5KgjoYdIQk5hZPWLGNLfd/MN+wANyJE5 +""" +) -##file distutils-init.py -DISTUTILS_INIT = convert(""" -eJytV92L4zYQf/dfMU0ottuse7RvC6FQrg8Lxz2Ugz4si9HacqKuIxlJ2ST313dG8odkO9d7aGBB -luZLv/nNjFacOqUtKJMIvzK3cXlhWgp5MDBsqK5SNYftsBAGpLLA4F1oe2Ytl+9wUvW55TswCi4c -KibhbFDSglXQCFmDPXIwtm7FawLRbwtPzg2T9gf4gupKv4GS0N262w7V0NvpbCy8cvTo3eAus6C5 -ETU3ICQZX1hFTw/dzR6V/AW1RCN4/XAtbsVXqIXmlVX6liS4lOzEYY9QFB2zx6LfoSNjz1a0pqT9 -QOIfJWQ2E888NEVZNqLlZZnvIB0NpHkimlFdKn2iRRY7yGG/CCJb6Iz280d34SFXBS2yEYPNF0Q7 -yM7oCjpWvbEDQmnhRwOs6zjThpKE8HogwRAgraqYFZgGZvzmzVh+mgz9vskT3hruwyjdFcqyENJw -bbMPO5jdzonxK68QKT7B57CMRRG5shRSWDTX3dI8LzRndZbnSWL1zfvriUmK4TcGWSnZiEPCrxXv -bM+sP7VW2is2WgWXCO3sAu3Rzysz3FiNCA8WPyM4gb1JAAmCiyTZbhFjWx3h9SzauuRXC9MFoVbc -yNTCm1QXOOIfIn/g1kGMhDUBN72hI5XCBQtIXQw8UEEdma6Jaz4vJIJ51Orc15hzzmu6TdFp3ogr -Aof0c98tsw1SiaiWotHffk3XYCkqdToxWRfTFXqgpg2khcLluOHMVC0zZhLKIomesfSreUNNgbXi -Ky9VRzwzkBneNoGQyyvGjbsFQqOZvpWIjqH281lJ/jireFgR3cPzSyTGWzQpDNIU+03Fs4XKLkhp -/n0uFnuF6VphB44b3uWRneSbBoMSioqE8oeF0JY+qTvYfEK+bPLYdoR4McfYQ7wMZj39q0kfP8q+ -FfsymO0GzNlPh644Jje06ulqHpOEQqdJUfoidI2O4CWx4qOglLye6RrFQirpCRXvhoRqXH3sYdVJ -AItvc+VUsLO2v2hVAWrNIfVGtkG351cUMNncbh/WdowtSPtCdkzYFv6mwYc9o2Jt68ud6wectBr8 -hYAulPSlgzH44YbV3ikjrulEaNJxt+/H3wZ7bXSXje/YY4tfVVrVmUstaDwwOBLMg6iduDB0lMVC -UyzYx7Ab4kjCqdViEJmDcdk/SKbgsjYXgfMznUWcrtS4z4fmJ/XOM1LPk/iIpqass5XwNbdnLb1Y -8h3ERXSWZI6rZJxKs1LBqVH65w0Oy4ra0CBYxEeuOMbDmV5GI6E0Ha/wgVTtkX0+OXvqsD02CKLf -XHbeft85D7tTCMYy2Njp4DJP7gWJr6paVWXZ1+/6YXLv/iE0M90FktiI7yFJD9e7SOLhEkkaMTUO -azq9i2woBNR0/0eoF1HFMf0H8ChxH/jgcB34GZIz3Qn4/vid+VEamQrOVqAPTrOfmD4MPdVh09tb -8dLLjvh/61lEP4yW5vJaH4vHcevG8agXvzPGoOhhXNncpTr99PTHx6e/UvffFLaxUSjuSeP286Dw -gtEMcW1xKr/he4/6IQ6FUXP+0gkioHY5iwC9Eyx3HKO7af0zPPe+XyLn7fAY78k4aiR387bCr5XT -5C4rFgwLGfMvJuAMew== -""") +# file activate.ps1 +ACTIVATE_PS = convert( + """ +eJytVU1v4jAQvftXTNNoF9QNvSP1kG6RikQpIiyX3ZXlJkOx5NiR46RFK/77Oh+QkEBVrXYOSPZ8 ++L2ZN8FNQ80TM149TgO68FePcAduvOMyVyEzXMlRvAtVHDMZjRJmtsStE+79YEIfpksbHySCG29h +vTBYYqpEjtXJcY9lb0cjZwj2WqM0hGwyGRbV4VWoFybGETJ7zpnBwc/0jZtw+xvcuZIPmBqdFS4c +wh8C1vgGBit7XT2RM83Zi8AxfZ490PV0ufrhz8oXD/GFuSjz8YHd5ZRj/BJjZUms60hweqEOeEGo +EqwJlJl7cgbggemYKhHRnGuTMUETreLEnEA8Bla+AulHuV2sU4Nx89ivSxktjGVTDpwm83UbTbto +Jwy8idZK+9X8Ai7sQMXuu5KGywy7j1xdmGJh1xCg2EBUe68+ptRI5OO4ZBepsIax7yutdNcgkp3Z +Wo8XQ3XrMv2aFknXkMkUDXCtUWDOpDkKLSUNEPCkklFDjhC33Sg7wcOWkG6zC2frSMgc3xq9nWgL +vDmLEXoSBBsvMmzETUhb5073yVtK76dzOvefJtRaEUaDyYJSB25aRaqpdXIth8C/n03oYvn8tFgd +ptht7hnVtebtOPVYTvV+Lumutw+NpBzatKFEUzDwpN1Sp62u3uC7cCoJ+lEEZosQZqlRVggaN/wd +jCov8Z2nVtav0Nm5koANzbnK0hozzctp3MGXTy5uYefJ3FwoPjzm7ludRJHiv/FmHbphpovPc832 +G7xkBiIlv9pfnoZMR8DN6P83wZX41s13Bu6g/cfS2x9vhmwDwyE4pw3tF/t8N/fkLzQ3ME8= +""" +) -##file distutils.cfg -DISTUTILS_CFG = convert(""" +# file distutils-init.py +DISTUTILS_INIT = convert( + """ +eJytV21v5DQQ/p5fMaRCJLANcAcSqlghuBdUcRzo6BdUnSI3cXZNs3bO9m679+uZsbOJnWR7fKBS +u65nPC/PvK7YdUpbUCYR/mSOw/GBaSnkxiTJBaiuUjUHYUAqCwwOQts9a7k8wE7V+5avwCh44FAx +CXuDnBasgkbIGuyWg7F1K+5Q0LWTzaT9DG7wgdL3oCR0x+64QkaUv9sbC3ccdXjBeMssaG5EzQ0I +SeJQDkq77I52q+TXyCcawevLx+JYfIRaaF5ZpY8nP7ztSYIEyXYc1uhu0TG7LfobIhm7t6I1Jd0H +HP8oIbMJe+YFFmXZiJaXZb6CdBCQ5olohudS6R0dslhBDuuZEdnszSA/v0oAf07xKOiQpTcIaxCG +QQN0rLpnG0TQwucGWNdxpg1FA1H1+IEhHFpVMSsQfWb85dFYvhsF/YS+8NZwr710lpdlIaTh2mbf +rGDqFFxgdnxgV/D6h2ffukcIBUotDlwbVFQK2Sj4EbLnK/iud8px+TjhRzLcac7acvRpTdSiVawu +fVpkaTk6PzKmK3irJJ/atoIsRRL9kpw/f/u1fHn97tWLmz/e/Z3nTunoaWwSfmCuFTtWbYXkmFUD +z9NJMzUgLdF9YRHA7pjmgxByiWvv31RV8Zfa64q/xix449jOOz0JxejH2QB8HwQg8NgeO26SiDIL +heMpfndxuMFz5p0oKI1H1TGgi6CSwFiX6XgVgUEsBd2WjVa70msKFa56CPOnbZ5I9EnkZZL0jP5M +o1LwR9Tb51ssMfdmX8AL1R1d9Wje8gP2NSw7q8Xd3iKMxGL1cUShLDU/CBeKEo2KZRYh1efkY8U7 +Cz+fJL7SWulRWseM6WvzFOBFqQMxScjhoFX0EaGLFSVKpWQjNuSXMEi4MvcCa3Jw4Y4ZbtAWuUl6 +095iBAKrRga0Aw80OjAhqy3c7UVbl/zRwlgZUCtu5BcW7qV6gC3+YpPacOvwxFCZoJc7OVuaFQ84 +U9SDgUuaMVuma2rGvoMRC3Y8rfb92HG6ee1qoNO8EY8YuL4mupbZBnst9eIUhT5/lnonYoyKSu12 +TNbF6EGP2niBDVThcbjwyVG1GJ+RK4tYguqreUODkrXiIy9VRy3ZZIa3zbRC0W68LRAZzfQRQ4xt +HScmNbyY01XSjHUNt+8jNt6iSMw3aXAgVzybPVkFAc3/m4rZHRZvK+xpuhne5ZOKnz0YB0zUUClm +LrV9ILGjvsEUSfO48COQi2VYkyfCvBjc4Z++GXgB09sgQ9YQ5MJFoIVOfVaaqyQha2lHKn3huYFP +KBJb8VIYX/doeTHjSnBr8YkT34eZ07hCWMOimh6LPrMQar8cYTF0yojHdIw37nPavenXpxRHWABc +s0kXJujs0eKbKdcs4qdgR4yh1Y5dGCJlMdNoC5Y5NgvcbXD9adGIzAEzLy/iKbiszYPA/Wtm8UIJ +OEGYljt14Bk9z5OYROuXrLMF8zW3ey09W+JX0E+EHPFZSIMwvcYWHucYNtXSb8u4AtCAHRiLmNRn +1UCevMyoabqBiRt3tcYS9fFZUw/q4UEc/eW8N/X3Tn1YyyEec3NjpSeVWMXJOTNx5tWqcsNwLu5E +TM5hEMJTTuGZyMPGdQ5N+r7zBJpInqNJjbjGkUbUs+iGTEAt63+Ee2ZVbNMnwacF6yz4AXEZ/Ama +5RTNk7yefGB+5ESiAtoi/AE9+5LpjemBdfj0Ehf09Lzht5qzCwT9oL00zZZaWjzEWjfEwoU9mMiD +UbThVzZ34U7fXP+C315S91UcO9rAFLen4fr29OA9WnOyC1c8Zu5xNaLeyNo2WNvPmkCtc2ICqidc +zmg+LaPu/BXc9srfx9pJbJiSw5NZkgXxWMiyBWpyNjdmeRbmzb+31cHS +""" +) + +# file distutils.cfg +DISTUTILS_CFG = convert( + """ eJxNj00KwkAMhfc9xYNuxe4Ft57AjYiUtDO1wXSmNJnK3N5pdSEEAu8nH6lxHVlRhtDHMPATA4uH xJ4EFmGbvfJiicSHFRzUSISMY6hq3GLCRLnIvSTnEefN0FIjw5tF0Hkk9Q5dRunBsVoyFi24aaLg 9FDOlL0FPGluf4QjcInLlxd6f6rqkgPu/5nHLg0cXCscXoozRrP51DRT3j9QNl99AP53T2Q= -""") +""" +) + +# file activate_this.py +ACTIVATE_THIS = convert( + """ +eJylVE1v2zAMvetXENqh9pZ5wHYL0EMOBdqh64It7RAEhaE4TKzOlgxJ+ULR/15Sdhr341BsOcSW +9fj4SD5JSjkqgt6ogLDRLqxVhWYDS+ugWDuHJoA2AV3jkP6HQlx7BNxhkdgGTRJK7fOlrjDNHKpF +kg7g/iSPX/L8ZAhP+w9pJsSEVlAoA3OEtccFbEs0sLdrqNc+8CegTdxpH7RZwXgfSmv6+QdgbCDS +Z1rn2nxpIjQTUkqh68a6ANYf3rwO+PS+90IEtx8KoN9BqcBdgU2AK1XjmXPWtdtOaZI08h5d0NbE +nURO+3r/qRWpTIX4AFQTBS64AAgWxqPJOUQaYBjQUxuvFxgLZtBCOyyCdftU0DKnJZxSnVmjQpnR +ypD85LBWc8/P5CAhTQVtUcO0s2YmOZu8PcZ7bLI7q00y66hv4RMcA7IVhqQNGoCUaeabSofkGEz0 +Yq6oI68VdYSx5m5uwIOjAp1elQHU3G5eVPhM683Fr8n16DI/u7qJkjkPk6nFom8G6AJqcq2PU//c +qOKvWiG3l4GlpbE1na1aQ9RYlMpoX4uL3/l4Op4Sf6m8CsElZBYqttk3+3yDzpMFcm2WlqZH2O/T +yfnPK0ITKmsqFejM1JkPygW/1dR4eac2irB6CU/w1lcsLe+k+V7DYv+5OMp6qefc6X4VnsiwaulY +6fvJXrN4bKOJ6s/Fyyrg9BTkVptvX2UEtSkJ18b8ZwkcfhTwXrKqJWuHd/+Q3T/IjLWqkHxk7f0B +pW9kFXTaNlwn+TjWSglSwaiMbMRP8l7yTAltE5AOc5VT8FLvDm2KC3F87VnyBzuZrUakdMERXe0P +7luSN+leWsYF5x/QAQdqeoHC4JZYKrr5+uq6t9mQXT/T8VrWHMRwmoqO9yGTUHF8YN/EHPbFI/bz +/no= +""" +) + +# file python-config +PYTHON_CONFIG = convert( + """ +eJyNVV1P2zAUfc+v8ODBiSABxlulTipbO6p1LWqBgVhlhcZpPYUkctzSivHfd6+dpGloGH2Ja/ue +e+65Hz78xNhtf3x90xmw7vCWsRPGLvpDNuz87MKfdKMWSWxZ4ilNpCLZJiuWc66SVFUOZkkcirll +rfxIBAzOMtImDzSVPBRrekwoX/OZu/0r4lm0DHiG60g86u8sjPw5rCyy86NRkB8QuuBRSqfAKESn +3orLTCQxE3GYkC9tYp8fk89OSwNsmXgizrhUtnumeSgeo5GbLUMk49Rv+2nK48Cm/qMwfp333J2/ +dVcAGE0CIQHBsgIeEr4Wij0LtWDLzJ9ze5YEvH2WI6CHTAVcSu9ZCsXtgxu81CIvp6/k4eXsdfo7 +PvDCRD75yi41QitfzlcPp1OI7i/1/iQitqnr0iMgQ+A6wa+IKwwdxyk9IiXNAzgquTFU8NIxAVjM +osm1Zz526e+shQ4hKRVci69nPC3Kw4NQEmkQ65E7OodxorSvxjvpBjQHDmWFIQ1mlmzlS5vedseT +/mgIEsMJ7Lxz2bLAF9M5xeLEhdbHxpWOw0GdkJApMVBRF1y+a0z3c9WZPAXGFcFrJgCIB+024uad +0CrzmEoRa3Ub4swNIHPGf7QDV+2uj2OiFWsChgCwjKqN6rp5izpbH6Wc1O1TclQTP/XVwi6anTr1 +1sbubjZLI1+VptPSdCfwnFBrB1jvebrTA9uUhU2/9gad7xPqeFkaQcnnLbCViZK8d7R1kxzFrIJV +8EaLYmKYpvGVkig+3C5HCXbM1jGCGekiM2pRCVPyRyXYdPf6kcbWEQ36F5V4Gq9N7icNNw+JHwRE +LTgxRXACpvnQv/PuT0xCCAywY/K4hE6Now2qDwaSE5FB+1agsoUveYDepS83qFcF1NufvULD3fTl +g6Hgf7WBt6lzMeiyyWVn3P1WVbwaczHmTzE9A5SyItTVgFYyvs/L/fXlaNgbw8v3azT+0eikVlWD +/vBHbzQumP23uBCjsYdrL9OWARwxs/nuLOzeXbPJTa/Xv6sUmQir5pC1YRLz3eA+CD8Z0XpcW8v9 +MZWF36ryyXXf3yBIz6nzqz8Muyz0m5Qj7OexfYo/Ph3LqvkHUg7AuA== +""" +) + +MH_MAGIC = 0xFEEDFACE +MH_CIGAM = 0xCEFAEDFE +MH_MAGIC_64 = 0xFEEDFACF +MH_CIGAM_64 = 0xCFFAEDFE +FAT_MAGIC = 0xCAFEBABE +BIG_ENDIAN = ">" +LITTLE_ENDIAN = "<" +LC_LOAD_DYLIB = 0xC +maxint = getattr(sys, "maxsize", getattr(sys, "maxint", None)) + + +class FileView(object): + """ + A proxy for file-like objects that exposes a given view of a file. + Modified from macholib. + """ + + def __init__(self, file_obj, start=0, size=maxint): + if isinstance(file_obj, FileView): + self._file_obj = file_obj._file_obj + else: + self._file_obj = file_obj + self._start = start + self._end = start + size + self._pos = 0 + + def __repr__(self): + return "".format(self._start, self._end, self._file_obj) + + def tell(self): + return self._pos + + def _checkwindow(self, seek_to, op): + if not (self._start <= seek_to <= self._end): + raise IOError( + "{} to offset {:d} is outside window [{:d}, {:d}]".format(op, seek_to, self._start, self._end) + ) -##file activate_this.py -ACTIVATE_THIS = convert(""" -eJyNU01v2zAMvetXEB4K21jmDOstQA4dMGCHbeihlyEIDMWmG62yJEiKE//7kXKdpN2KzYBt8euR -fKSyLPs8wiEo8wh4wqZTGou4V6Hm0wJa1cSiTkJdr8+GsoTRHuCotBayiWqQEYGtMCgfD1KjGYBe -5a3p0cRKiAe2NtLADikftnDco0ko/SFEVgEZ8aRC5GLux7i3BpSJ6J1H+i7A2CjiHq9z7JRZuuQq -siwTIvpxJYCeuWaBpwZdhB+yxy/eWz+ZvVSU8C4E9FFZkyxFsvCT/ZzL8gcz9aXVE14Yyp2M+2W0 -y7n5mp0qN+avKXvbsyyzUqjeWR8hjGE+2iCE1W1tQ82hsCZN9UzlJr+/e/iab8WfqsmPI6pWeUPd -FrMsd4H/55poeO9n54COhUs+sZNEzNtg/wanpjpuqHJaxs76HtZryI/K3H7KJ/KDIhqcbJ7kI4ar -XL+sMgXnX0D+Te2Iy5xdP8yueSlQB/x/ED2BTAtyE3K4SYUN6AMNfbO63f4lBW3bUJPbTL+mjSxS -PyRfJkZRgj+VbFv+EzHFi5pKwUEepa4JslMnwkowSRCXI+m5XvEOvtuBrxHdhLalG0JofYBok6qj -YdN2dEngUlbC4PG60M1WEN0piu7Nq7on0mgyyUw3iV1etLo6r/81biWdQ9MWHFaePWZYaq+nmp+t -s3az+sj7eA0jfgPfeoN1 -""") + def seek(self, offset, whence=0): + seek_to = offset + if whence == os.SEEK_SET: + seek_to += self._start + elif whence == os.SEEK_CUR: + seek_to += self._start + self._pos + elif whence == os.SEEK_END: + seek_to += self._end + else: + raise IOError("Invalid whence argument to seek: {!r}".format(whence)) + self._checkwindow(seek_to, "seek") + self._file_obj.seek(seek_to) + self._pos = seek_to - self._start + + def write(self, content): + here = self._start + self._pos + self._checkwindow(here, "write") + self._checkwindow(here + len(content), "write") + self._file_obj.seek(here, os.SEEK_SET) + self._file_obj.write(content) + self._pos += len(content) + + def read(self, size=maxint): + assert size >= 0 + here = self._start + self._pos + self._checkwindow(here, "read") + size = min(size, self._end - here) + self._file_obj.seek(here, os.SEEK_SET) + read_bytes = self._file_obj.read(size) + self._pos += len(read_bytes) + return read_bytes + + +def read_data(file, endian, num=1): + """ + Read a given number of 32-bits unsigned integers from the given file + with the given endianness. + """ + res = struct.unpack(endian + "L" * num, file.read(num * 4)) + if len(res) == 1: + return res[0] + return res + + +def mach_o_change(at_path, what, value): + """ + Replace a given name (what) in any LC_LOAD_DYLIB command found in + the given binary with a new name (value), provided it's shorter. + """ -if __name__ == '__main__': - main() + def do_macho(file, bits, endian): + # Read Mach-O header (the magic number is assumed read by the caller) + cpu_type, cpu_sub_type, file_type, n_commands, size_of_commands, flags = read_data(file, endian, 6) + # 64-bits header has one more field. + if bits == 64: + read_data(file, endian) + # The header is followed by n commands + for _ in range(n_commands): + where = file.tell() + # Read command header + cmd, cmd_size = read_data(file, endian, 2) + if cmd == LC_LOAD_DYLIB: + # The first data field in LC_LOAD_DYLIB commands is the + # offset of the name, starting from the beginning of the + # command. + name_offset = read_data(file, endian) + file.seek(where + name_offset, os.SEEK_SET) + # Read the NUL terminated string + load = file.read(cmd_size - name_offset).decode() + load = load[: load.index("\0")] + # If the string is what is being replaced, overwrite it. + if load == what: + file.seek(where + name_offset, os.SEEK_SET) + file.write(value.encode() + b"\0") + # Seek to the next command + file.seek(where + cmd_size, os.SEEK_SET) -## TODO: -## Copy python.exe.manifest -## Monkeypatch distutils.sysconfig + def do_file(file, offset=0, size=maxint): + file = FileView(file, offset, size) + # Read magic number + magic = read_data(file, BIG_ENDIAN) + if magic == FAT_MAGIC: + # Fat binaries contain nfat_arch Mach-O binaries + n_fat_arch = read_data(file, BIG_ENDIAN) + for _ in range(n_fat_arch): + # Read arch header + cpu_type, cpu_sub_type, offset, size, align = read_data(file, BIG_ENDIAN, 5) + do_file(file, offset, size) + elif magic == MH_MAGIC: + do_macho(file, 32, BIG_ENDIAN) + elif magic == MH_CIGAM: + do_macho(file, 32, LITTLE_ENDIAN) + elif magic == MH_MAGIC_64: + do_macho(file, 64, BIG_ENDIAN) + elif magic == MH_CIGAM_64: + do_macho(file, 64, LITTLE_ENDIAN) + + assert len(what) >= len(value) + + with open(at_path, "r+b") as f: + do_file(f) + + +if __name__ == "__main__": + main() diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/web/virtualenv_support/distribute-0.6.24.tar.gz Binary file virtualenv/web/virtualenv_support/distribute-0.6.24.tar.gz has changed diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/web/virtualenv_support/pip-1.1.tar.gz Binary file virtualenv/web/virtualenv_support/pip-1.1.tar.gz has changed diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/web/virtualenv_support/pip-19.1.1-py2.py3-none-any.whl Binary file virtualenv/web/virtualenv_support/pip-19.1.1-py2.py3-none-any.whl has changed diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/web/virtualenv_support/pip-19.3.1-py2.py3-none-any.whl Binary file virtualenv/web/virtualenv_support/pip-19.3.1-py2.py3-none-any.whl has changed diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/web/virtualenv_support/setuptools-0.6c11-py2.4.egg Binary file virtualenv/web/virtualenv_support/setuptools-0.6c11-py2.4.egg has changed diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/web/virtualenv_support/setuptools-0.6c11-py2.5.egg Binary file virtualenv/web/virtualenv_support/setuptools-0.6c11-py2.5.egg has changed diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/web/virtualenv_support/setuptools-0.6c11-py2.6.egg Binary file virtualenv/web/virtualenv_support/setuptools-0.6c11-py2.6.egg has changed diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/web/virtualenv_support/setuptools-0.6c11-py2.7.egg Binary file virtualenv/web/virtualenv_support/setuptools-0.6c11-py2.7.egg has changed diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/web/virtualenv_support/setuptools-42.0.2-py2.py3-none-any.whl Binary file virtualenv/web/virtualenv_support/setuptools-42.0.2-py2.py3-none-any.whl has changed diff -r a5b9bb4dd63d -r a14feef87fd7 virtualenv/web/virtualenv_support/wheel-0.33.6-py2.py3-none-any.whl Binary file virtualenv/web/virtualenv_support/wheel-0.33.6-py2.py3-none-any.whl has changed diff -r a5b9bb4dd63d -r a14feef87fd7 web/tralalere/__init__.py --- a/web/tralalere/__init__.py Tue Jan 21 22:11:51 2020 +0100 +++ b/web/tralalere/__init__.py Sat Jan 25 02:22:57 2020 +0100 @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -VERSION = (0, 4, 2, "final", 0) +VERSION = (0, 4, 3, "final", 0) def get_version():