xenv / xenv /

Full commit
import os
from paver.easy import path
from subprocess import Popen, call, check_output
from urllib2 import urlopen
from urlparse import urljoin

class XenvFS(object):
    '''The xenv filesystem object provides the specification for the
    xenv filesystem. This will allow finding directories, establishing
    the PATH when calling a command along with locations for specific
    def __init__(self, root=None):
        self.root = path(root or '.')

    def create_base_env(self):
        (self.root / 'local').mkdir()
    def create_venv(self):
        venv = self.root.abspath() / 'usr'
        cmd = 'virtualenv --no-site-packages %s' % venv

    def path(self):
        '''Return the path hiearchy'''
        return ':'.join([
                (self.root / 'usr' / 'bin').abspath(),
                (self.root / 'local' / 'bin').abspath(),
                '/bin', '/usr/bin', '/usr/local/bin',

class Xenv(object):
    def __init__(self, root=None):
        self.xenv_fs = XenvFS(root)
        self.root = self.xenv_fs.root.abspath()

    def run(self, cmd):
        env = {
            'PATH': self.xenv_fs.path()
            print cmd
            call(cmd, env=env)
        except KeyboardInterrupt:

    def create(self):

    def install(self, pkg):
        pkgfile = pkg.fetch(self)
        call(['tar', 'xf', pkgfile], cwd=self.root)
        install_hook = self.root / 'packages' / 'install'

        history = self.root / 'packages' / / pkg.version

        if install_hook.isfile():
            call(install_hook.abspath(), cwd=self.root)
        (self.root/ pkg.fname).move(history)

    def uninstall(self, pkg):
        filelist = check_output(['tar', 'tf', self.root/'packages'/])
        for line in filelist.split('\n'):
            name = path(line)
            if name.isfile():

class Package(object):
    A package is a single file that contains a directory structure to
    untar on top a xenv. It may place files anywhere in the xenv and
    is responsible for not corrupting the environment.

    The only restriction is that it may not add any hidden (.) files
    to the root directory.

    Package Name

    The package must be a tarball (.tar) or gzipped tar (.tar.gz). It
    must be named using the following pattern: ::

    The version must comply with the specification.

    File Hiearchy

    The file hierarchy can be whatever the package creator likes. The
    only restrictions are the following.

    The /usr Directory

    The usr directory is where an xenv installs its core utilities and
    executables. For example, we install a virtualenv in /usr for
    using Python.

    The /local Directory

    If you create a /local/bin, it will be added to the PATH when
    running something via xe.

    Installation Hooks

    A xenv package does not aim to be a "real" package format and as
    such, it may wrap a more complex package such as an RPM. For this
    reason we provide a package hook in order to allow installing
    these sorts of packages.

    When a package is installed (untarred) in the xenv, an install
    script can be placed at /packages/install. This file must be
    executable. It may use any language that is available on the host
    system via a shebang.

    After it is installed, the install script will be moved to: ::

    def __init__(self, name, version, base='file:'): = name
        self.version = version
        self.base = base
        self.pkg_tmpl = '{}-{self.version}.tar'

    def fname(self):
        return path(self.pkg_tmpl.format(**vars()))

    def fetch(self, xenv):
        if self.base.startswith('file:'):
            fname = self.fname.abspath()
        url = urljoin(self.base, fname)
        pkgfile = urlopen(url)
        return, xenv)

    def save(self, fh, xenv):
        fname = self.pkg_tmpl.format(**vars())
        pkgfile = xenv.root.abspath() / fname
        with open(pkgfile, 'w+') as pkg:
            for line in fh:
        return pkgfile