Created
March 29, 2011 22:38
-
-
Save vsajip/893507 to your computer and use it in GitHub Desktop.
Poor man's virtualize.py
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) 2011 Vinay Sajip. | |
# | |
# Poor man's virtualize.py. | |
# | |
# Use with a Python executable built from the Python fork at | |
# | |
# https://bitbucket.org/vinay.sajip/pythonv/ as follows: | |
# | |
# python pmv.py env_dir | |
# | |
# You'll need an Internet connection (needed to download distribute_setup.py). | |
# | |
# The script will change to the environment's binary directory and run | |
# | |
# ./python distribute_setup.py | |
# | |
# after which you can change to the environment's binaries folder and do some | |
# easy_installs, e.g. | |
# | |
# ./easy_install setuptools-git | |
# ./easy_install ply | |
# ./easy_install Pygments | |
# ./easy_install Jinja2 | |
# ./easy_install SQLAlchemy | |
# ./easy_install coverage | |
# | |
# Note that on Windows, distributions which include C extensions (e.g. coverage) | |
# may fail due to lack of a suitable C compiler. | |
# | |
import os | |
import os.path | |
import shutil | |
import sys | |
def reader(stream, progress, context): | |
while True: | |
s = stream.readline() | |
if not s: | |
break | |
if progress is not None: | |
progress(s, context) | |
else: | |
sys.stderr.write('.') | |
sys.stderr.flush() | |
def virtualize(env_dir, nosite=False, clear=False, nodist=False, | |
progress=None): | |
""" | |
Create a virtualized environment in a directory. | |
By default, installs Distribute so that you can pip or easy_install | |
other packages into the created environment. | |
By default, makes the system (global) site-packages dir available to | |
the created environment. | |
:param env_dir: The target directory to create an environment in. | |
:param nosite: If True, the system (global) site-packages dir is | |
not available to the environment. | |
:param nodist: If True, Distribute is not installed into the created | |
environment. | |
:param clear: If True and the target directory exists, it is deleted. | |
Otherwise, if the target directory exists, an error is | |
raised. | |
:param progress: If Distribute is 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 Distribute. | |
If a callable is not specified, default progress | |
information is output to sys.stderr. | |
""" | |
if os.path.exists(env_dir) and not clear: | |
raise ValueError('Directory exists: %s' % env_dir) | |
else: | |
if os.path.exists(env_dir) and clear: | |
shutil.rmtree(env_dir) | |
os.makedirs(env_dir) | |
path = os.path.join(env_dir, 'env.cfg') | |
dirname, exename = os.path.split(os.path.abspath(sys.executable)) | |
with open(path, 'w', encoding='utf-8') as f: | |
f.write('home = %s\n' % dirname) | |
if nosite: | |
f.write('include-system-site = false\n') | |
if sys.platform == 'win32': | |
binname = 'Scripts' | |
incpath = 'Include' | |
libpath = os.path.join(env_dir, 'Lib', 'site-packages') | |
else: | |
binname = 'bin' | |
incpath = 'include' | |
libpath = os.path.join(env_dir, 'lib', 'python%d.%d' % sys.version_info[:2], 'site-packages') | |
path = os.path.join(env_dir, incpath) | |
os.mkdir(path) | |
os.makedirs(libpath) | |
binpath = os.path.join(env_dir, binname) | |
os.mkdir(binpath) | |
path = os.path.join(binpath, exename) | |
shutil.copyfile(sys.executable, path) | |
if sys.platform == 'win32': | |
files = [f for f in os.listdir(dirname) if f.endswith(('.pyd', '.dll'))] | |
for f in files: | |
src = os.path.join(dirname, f) | |
dst = os.path.join(binpath, f) | |
shutil.copyfile(src, dst) | |
else: | |
os.chmod(path, 0o755) | |
dst = os.path.join(binpath, 'python') | |
os.symlink(exename, dst) | |
if not nodist: | |
from subprocess import Popen, PIPE | |
from threading import Thread | |
from urllib.request import urlretrieve | |
url = 'http://python-distribute.org/distribute_setup.py' | |
distpath = os.path.join(binpath, 'distribute_setup.py') | |
# Download Distribute in the env | |
urlretrieve(url, distpath) | |
if progress is not None: | |
progress('Installing distribute', 'main') | |
else: | |
sys.stderr.write('Installing distribute ') | |
sys.stderr.flush() | |
# Install Distribute in the env | |
os.chdir(binpath) | |
if sys.platform == 'win32': | |
args = [exename, 'distribute_setup.py'] | |
else: | |
args = [os.path.join('.', exename), 'distribute_setup.py'] | |
p = Popen(args, stdout=PIPE, stderr=PIPE) | |
t1 = Thread(target=reader, args=(p.stdout, progress, 'stdout')) | |
t1.start() | |
t2 = Thread(target=reader, args=(p.stderr, progress, '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 main(): | |
compatible = True | |
if sys.version_info < (3, 3): | |
compatible = False | |
elif not hasattr(sys, 'virtual_prefix'): | |
compatible = False | |
if not compatible: | |
raise ValueError('This script is only for use with ' | |
'Python 3.3 (pythonv variant)') | |
else: | |
import argparse | |
parser = argparse.ArgumentParser() | |
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-site-packages', default=False, | |
action='store_true', dest='nosite', | |
help="Don't give access to the global " | |
"site-packages dir to the virtual " | |
"environment.") | |
parser.add_argument('--clear', default=False, action='store_true', | |
dest='clear', help='Delete the environment ' | |
'directory if it already ' | |
'exists. If not specified and ' | |
'the directory exists, an error' | |
' is raised.') | |
options = parser.parse_args() | |
for d in options.dirs: | |
virtualize(d, nosite=options.nosite, clear=options.clear, nodist=options.nodist) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment