Skip to content

Instantly share code, notes, and snippets.

@vsajip
Created March 29, 2011 22:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vsajip/893507 to your computer and use it in GitHub Desktop.
Save vsajip/893507 to your computer and use it in GitHub Desktop.
Poor man's virtualize.py
# 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