-
-
Save delicb/5871935 to your computer and use it in GitHub Desktop.
Creates python 3.3 virtual environment and can install distribute and pip in it.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Copyright (C) 2013 Vinay Sajip. New BSD License. | |
# | |
import os | |
import os.path | |
from subprocess import Popen, PIPE | |
import sys | |
from threading import Thread | |
from urllib.parse import urlparse | |
from urllib.request import urlretrieve | |
import venv | |
class DistributeEnvBuilder(venv.EnvBuilder): | |
""" | |
This builder installs Distribute and pip so that you can pip or | |
easy_install other packages into the created environment. | |
:param nodist: If True, Distribute is not installed into the created | |
environment. | |
:param nopip: If True, pip is not installed into the created | |
environment. | |
:param progress: If Distribute or pip are installed, the progress of the | |
installation can be monitored by passing a progress | |
callable. If specified, it is called with two | |
arguments: a string indicating some progress, and a | |
context indicating where the string is coming from. | |
The context argument can have one of three values: | |
'main', indicating that it is called from virtualize() | |
itself, and 'stdout' and 'stderr', which are obtained | |
by reading lines from the output streams of a subprocess | |
which is used to install the app. | |
If a callable is not specified, default progress | |
information is output to sys.stderr. | |
""" | |
def __init__(self, *args, **kwargs): | |
self.nodist = kwargs.pop('nodist', False) | |
self.nopip = kwargs.pop('nopip', False) | |
self.progress = kwargs.pop('progress', None) | |
self.verbose = kwargs.pop('verbose', False) | |
super().__init__(*args, **kwargs) | |
def post_setup(self, context): | |
""" | |
Set up any packages which need to be pre-installed into the | |
environment being created. | |
:param context: The information for the environment creation request | |
being processed. | |
""" | |
if not self.nodist: | |
self.install_distribute(context) | |
if not self.nopip: | |
self.install_pip(context) | |
def reader(self, stream, context): | |
""" | |
Read lines from a subprocess' output stream and either pass to a progress | |
callable (if specified) or write progress information to sys.stderr. | |
""" | |
progress = self.progress | |
while True: | |
s = stream.readline() | |
if not s: | |
break | |
if progress is not None: | |
progress(s, context) | |
else: | |
if not self.verbose: | |
sys.stderr.write('.') | |
else: | |
sys.stderr.write(s.decode('utf-8')) | |
sys.stderr.flush() | |
def install_script(self, context, name, url): | |
_, _, path, _, _, _ = urlparse(url) | |
fn = os.path.split(path)[-1] | |
binpath = context.bin_path | |
distpath = os.path.join(binpath, fn) | |
# Download script into the env's binaries folder | |
urlretrieve(url, distpath) | |
progress = self.progress | |
if progress is not None: | |
progress('Installing %s' %name, 'main') | |
else: | |
sys.stderr.write('Installing %s ' % name) | |
sys.stderr.flush() | |
# Install in the env | |
args = [context.env_exe, fn] | |
p = Popen(args, stdout=PIPE, stderr=PIPE, cwd=binpath) | |
t1 = Thread(target=self.reader, args=(p.stdout, 'stdout')) | |
t1.start() | |
t2 = Thread(target=self.reader, args=(p.stderr, 'stderr')) | |
t2.start() | |
p.wait() | |
t1.join() | |
t2.join() | |
if progress is not None: | |
progress('done.', 'main') | |
else: | |
sys.stderr.write('done.\n') | |
# Clean up - no longer needed | |
os.unlink(distpath) | |
def install_distribute(self, context): | |
""" | |
Install Distribute in the environment. | |
:param context: The information for the environment creation request | |
being processed. | |
""" | |
url = 'http://python-distribute.org/distribute_setup.py' | |
self.install_script(context, 'distribute', url) | |
# clear up the distribute archive which gets downloaded | |
pred = lambda o: o.startswith('distribute-') and o.endswith('.tar.gz') | |
files = filter(pred, os.listdir(context.bin_path)) | |
for f in files: | |
f = os.path.join(context.bin_path, f) | |
os.unlink(f) | |
def install_pip(self, context): | |
""" | |
Install pip in the environment. | |
:param context: The information for the environment creation request | |
being processed. | |
""" | |
url = 'https://raw.github.com/pypa/pip/master/contrib/get-pip.py' | |
self.install_script(context, 'pip', url) | |
def main(args=None): | |
compatible = True | |
if sys.version_info < (3, 3): | |
compatible = False | |
elif not hasattr(sys, 'base_prefix'): | |
compatible = False | |
if not compatible: | |
raise ValueError('This script is only for use with ' | |
'Python 3.3 or later') | |
else: | |
import argparse | |
parser = argparse.ArgumentParser(prog=__name__, | |
description='Creates virtual Python ' | |
'environments in one or ' | |
'more target ' | |
'directories.') | |
parser.add_argument('dirs', metavar='ENV_DIR', nargs='+', | |
help='A directory to create the environment in.') | |
parser.add_argument('--no-distribute', default=False, | |
action='store_true', dest='nodist', | |
help="Don't install Distribute in the virtual " | |
"environment.") | |
parser.add_argument('--no-pip', default=False, | |
action='store_true', dest='nopip', | |
help="Don't install pip in the virtual " | |
"environment.") | |
parser.add_argument('--system-site-packages', default=False, | |
action='store_true', dest='system_site', | |
help='Give the virtual environment access to the ' | |
'system site-packages dir.') | |
if os.name == 'nt': | |
use_symlinks = False | |
else: | |
use_symlinks = True | |
parser.add_argument('--symlinks', default=use_symlinks, | |
action='store_true', dest='symlinks', | |
help='Try to use symlinks rather than copies, ' | |
'when symlinks are not the default for ' | |
'the platform.') | |
parser.add_argument('--clear', default=False, action='store_true', | |
dest='clear', help='Delete the contents of the ' | |
'environment directory if it ' | |
'already exists, before ' | |
'environment creation.') | |
parser.add_argument('--upgrade', default=False, action='store_true', | |
dest='upgrade', help='Upgrade the environment ' | |
'directory to use this version ' | |
'of Python, assuming Python ' | |
'has been upgraded in-place.') | |
parser.add_argument('--verbose', default=False, action='store_true', | |
dest='verbose', help='Display the output ' | |
'from the scripts which ' | |
'install Distribute and pip.') | |
options = parser.parse_args(args) | |
if options.upgrade and options.clear: | |
raise ValueError('you cannot supply --upgrade and --clear together.') | |
builder = DistributeEnvBuilder(system_site_packages=options.system_site, | |
clear=options.clear, | |
symlinks=options.symlinks, | |
upgrade=options.upgrade, | |
nodist=options.nodist, | |
nopip=options.nopip, | |
verbose=options.verbose) | |
for d in options.dirs: | |
builder.create(d) | |
if __name__ == '__main__': | |
rc = 1 | |
try: | |
main() | |
rc = 0 | |
except Exception as e: | |
print('Error: %s' % e, file=sys.stderr) | |
sys.exit(rc) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment