Posts in category setuptools

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