Adding support for a workingenv sandbox to setuptools/distutils
It seems that all I blog about is workingenv.
This time it's a snippet of code that adds a "sandbox" command to distutils, which automatically creates a workingenv from the setuptools extras_require, install_requires and dependency_links options listed in your setup() call. It only supports *nix systems for now, but could easily be extended to support Windows.
Here's the code:
import os from setuptools import setup, find_packages from distutils.cmd import Command class sandbox(Command): description = 'Create a development sandbox using workingenv' user_options = [ ('path=', None, 'workingenv path'), ('extras', None, 'also include "extras" requirements'), ] def initialize_options(self): self.path = 'wenv' self.extras = False def finalize_options(self): pass def run(self): requires = open('requirements.txt', 'w') try: requirements = self.distribution.dependency_links + \ self.distribution.install_requires if self.extras: extras = self.distribution.extras_require or {} requirements += extras.values() requires.write('\n'.join(requirements)) finally: requires.close() cwd = os.getcwd() import workingenv workingenv.main(['--always-unzip', '--requirements=requirements.txt', '--site-packages', '--verbose', self.path]) os.chdir(cwd) os.symlink(self.path + '/bin/activate', 'sandbox') print print 'XXX: Use ". sandbox" to activate the development sandbox' setup( name='MyCoolPackage', version='0.0.0.1', packages=find_packages(), # Add the sandbox command cmdclass={'sandbox': sandbox}, # Search some extra locations for dependencies dependency_links=[ 'http://svn.edgewall.org/repos/genshi/trunk#egg=Genshi-dev', 'http://trac.pocoo.org/repos/werkzeug/trunk#egg=Werkzeug-dev', 'http://svn.sqlalchemy.org/sqlalchemy/trunk#egg=SQLAlchemy-dev', ], install_requires=[ 'setuptools >= 0.6b1', 'Genshi >= 0.5.dev-r698,==dev', 'Werkzeug >= 0.1.dev-r3831,==dev', 'SQLAlchemy >= 0.4.0.dev-r3203,==dev', 'AuthKit >= 0.3.0pre5', ], )
And here's an example of how to use it:
$ python setup.py sandbox --help Common commands: (see '--help-commands' for more) ... Options for 'sandbox' command: --path workingenv path --extras also include "extras" requirements ... $ python setup.py sandbox --path=mysandbox --extras running sandbox Reading requirement requirements.txt Making working environment in /home/athomas/p/test/mysandbox Creating lib/python2.5 ... ...Installing http://svn.edgewall.org/repos/genshi/trunk#egg=Genshi-dev, http://trac.pocoo.org/repos/werkzeug/trunk#egg=Werkzeug-dev, http://svn.sqlalchemy.org/sqlalchemy/trunk#egg=SQLAlchemy-dev, setuptools >= 0.6b1, Genshi >= 0.5.dev-r698,==dev, Werkzeug >= 0.1.dev-r3831,==dev, SQLAlchemy >= 0.4.0.dev-r3203,==dev, AuthKit >= 0.3.0pre5 ...done. XXX: Use ". sandbox" to activate the development sandbox
Activating a `workingenv` from Python
It can, under some circumstances, be useful to be able to activate a workingenv from Python. Here's a quick function to achieve that:
import sys import os def activate_workingenv(root): """Make modules in a self-contained workingenv available.""" # Add ./bin directory to path. bin_dir = os.path.join(root, './bin') try: os.environ['PATH'] = os.path.pathsep.join([bin_dir, os.environ['PATH']]) except KeyError: os.environ['PATH'] = bin_dir # Add ./lib to linker path lib_dir = os.path.join(root, './lib') try: os.environ['LD_LIBRARY_PATH'] = \ os.path.pathsep.join([lib_dir, os.environ['LD_LIBRARY_PATH']]) except KeyError: os.environ['LD_LIBRARY_PATH'] = lib_dir # Find the workingenv Python package root python_version = '.'.join(map(str, sys.version_info[:2])) package_root = os.path.join(root, './lib/python' + python_version) # Find and insert setuptools into sys.path sys.path.insert(0, package_root) real_setuptools = open(os.path.join(package_root, 'setuptools.pth')).read().strip() sys.path.insert(0, os.path.join(package_root, real_setuptools)) # Load all distributions into the working set. from pkg_resources import working_set, Environment env = Environment(root) env.scan() distributions, errors = working_set.find_plugins(env) for dist in distributions: working_set.add(dist) return distributions, errors
It's UNIX-centric due to the use of LD_LIBRARY_PATH, but if you're not using shared libraries it's not really necessary anyway.
Use it like so:
from activate_workingenv import activate_workingenv activate_workingenv('./wenv') import some_module_from_the_workingenv
Automatically activating `workingenv.py` environments on directory change
workingenv.py is a very useful tool for Python development. Quoting from its home page:
This tool creates an environment that is isolated from the rest of the Python installation, eliminating site-packages and any other source of modules, so that only the modules (and versions) you install into the environment will be available. This allows for isolated and controlled environments, as well as reproduceability.
To create, activate and deactivate an environment:
$ workingenv foo $ . foo/bin/activate (foo)$ deactivate $
This is great, but what's even more so is using On Dir with it.
I need to work on multiple versions of Trac (stable, trunk, branches, etc.) at the same time, I have each version in its own directory beneath ~/projects/trac. Each Trac instance is completely self contained, so installing plugins in one will not affect the others.
So I use the following On Dir config to activate the workingenvs as I cd into each Trac directory.
enter ~/projects/trac/([^/]*)
declare -F deactivate > /dev/null && deactivate
activate=../env/$1/bin/activate
test -r $activate && . $activate
leave ~/projects/trac
declare -F deactivate > /dev/null && deactivate
Here's an example of me switching between environments. The last environment remains active until I leave the main Trac directory.
[aat@stalactite:~]cd projects/trac/trunk (trunk)[aat@stalactite:~/projects/trac/trunk]cd .. (trunk)[aat@stalactite:~/projects/trac]workingenv --site-packages ../env/stable Updating working environment in /home/aat/projects/trac/env/stable Installing local setuptools.................done. (trunk)[aat@stalactite:~/projects/trac]cd stable/ (stable)[aat@stalactite:~/projects/trac/stable]cd .. (stable)[aat@stalactite:~/projects/trac]cd trunk (trunk)[aat@stalactite:~/projects/trac/trunk]cd ../.. [aat@stalactite:~/projects]
Of course, this can be extended to any project that needs its own independent Python environment, not just Trac.

rss