Skip to content

Instantly share code, notes, and snippets.

@pfmoore
Last active December 15, 2015 10:49
Show Gist options
  • Save pfmoore/5248053 to your computer and use it in GitHub Desktop.
Save pfmoore/5248053 to your computer and use it in GitHub Desktop.
Virtualenv wrapper maker that (among other things) lets you specify things like -p 3.2 on Windows.
#!/usr/bin/env python
import os
import sys
import shutil
import tempfile
import subprocess
from glob import glob
from argparse import ArgumentParser
ADDITIONAL_CODE = """
import json
try:
import winreg
except ImportError:
import _winreg as winreg
try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse
try:
from urllib.request import url2pathname, pathname2url
except ImportError:
from urllib2 import url2pathname, pathname2url
def get_installed_pythons():
python_core = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE,
"Software\\Python\\PythonCore")
i = 0
versions = []
while True:
try:
versions.append(winreg.EnumKey(python_core, i))
i = i + 1
except WindowsError:
break
exes = dict()
for ver in versions:
path = winreg.QueryValue(python_core, "%s\\InstallPath" % ver)
exes[ver] = join(path, "python.exe")
winreg.CloseKey(python_core)
# Add the major versions
# Sort the keys, then repeatedly update the major version entry
# Last executable (i.e., highest version) wins with this approach
for ver in sorted(exes):
exes[ver[0]] = exes[ver]
return exes
def import_distlib(options):
try:
from distlib.locators import DirectoryLocator, AggregatingLocator
from distlib.wheel import Wheel
from distlib.compat import urlunparse
except ImportError:
for d in options.search_dirs:
candidates = glob.glob(join(d, 'distlib-*.whl'))
if candidates:
sys.path.append(candidates[0])
break
try:
from distlib.locators import DirectoryLocator, AggregatingLocator
from distlib.wheel import Wheel
from distlib.compat import urlunparse
except ImportError:
return False, None, None, None
class WheelLocator(DirectoryLocator):
downloadable_extensions = ('.whl',)
return True, WheelLocator, AggregatingLocator, Wheel
def get_paths(python, name):
SCRIPT='''
import sys
import json
from distutils.dist import Distribution
from distutils.command.install import install
d = Distribution({'name': sys.argv[1]})
i = install(d)
i.initialize_options()
i.finalize_options()
paths = {
'prefix': i.prefix,
'purelib': i.install_purelib,
'platlib': i.install_platlib,
'scripts': i.install_scripts,
'headers': i.install_headers,
'data': i.install_data,
}
if hasattr(sys, 'real_prefix'): # virtualenv
paths['headers'] = os.path.join(sys.prefix,
'include',
'site',
'python' + sys.version[:3],
sys.argv[1])
print(json.dumps(paths))
'''
args = [python, '-c', SCRIPT, name]
popen = subprocess.Popen(args, stdout=subprocess.PIPE)
out, err = popen.communicate()
popen.wait()
return json.loads(out.decode('ascii'))
def wheelname(dist):
url = dist.download_url
return url2pathname(urlparse(url).path)
def extend_parser(parser):
parser.add_option('--extras', action='append', default=[],
help="Extra distributions to install")
wheels = []
def adjust_options(options, args):
# Adjust the --python option to allow a version number which selects the
# installed Python of that version.
python_versions = get_installed_pythons()
if options.python in python_versions:
options.python = python_versions[options.python]
has_distlib, WheelLocator, AggregatingLocator, Wheel = import_distlib(options)
if has_distlib:
locator = AggregatingLocator(*[
WheelLocator(d, recursive=False) for d in options.search_dirs
])
if not options.no_pip:
pip = locator.locate('pip')
if pip:
wheels.append(Wheel(wheelname(pip)))
options.no_pip = True
if not options.no_setuptools:
setuptools = 'setuptools'
# If Python version > 2, always use distribute
# (use_distribute is set after adjust_options :-()
if majver > 2 or options.use_distribute:
setuptools = 'distribute'
setuptools = locator.locate(setuptools)
if setuptools:
wheels.append(Wheel(wheelname(setuptools)))
options.no_setuptools = True
for req in options.extras:
dist = locators.locate(req)
if dist:
wheels.append(Wheel(wheelname(dist)))
else:
logger.warn("Could not find requirement: %s" % (req,))
else:
if options.extras:
logger.warn("Cannot install extras without distlib")
def after_install(options, home_dir):
if sys.platform == 'win32':
bin = 'Scripts'
python = 'python.exe'
else:
bin = 'bin'
python = 'python'
python = join(home_dir, bin, python)
paths = get_paths(python, '{}')
headers = paths['headers']
for wheel in wheels:
paths['headers'] = headers.format(wheel.name)
logger.notify("Installing %s (%s)" % (wheel.name, wheel.version))
wheel.install(paths, executable=python)
"""
def main():
parser = ArgumentParser(description="Build a custom virtualenv installation")
parser.add_argument('--virtualenv', help="Location of virtualenv")
parser.add_argument('dest', help="The directory to contain the generated script")
args = parser.parse_args()
if args.virtualenv:
sys.path.insert(0, args.virtualenv)
import virtualenv
support = os.path.join(os.path.dirname(virtualenv.__file__), 'virtualenv_support')
script = virtualenv.create_bootstrap_script(ADDITIONAL_CODE)
# Fix up CRLF nastiness :-(
script = script.replace('\r\n', '\n')
script_name = os.path.join(args.dest, 'virtualenv.py')
support_dest = os.path.join(args.dest, 'virtualenv_support')
os.makedirs(args.dest)
with open(script_name, 'w') as f:
f.write(script)
shutil.copytree(support, support_dest)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment