[Distutils] PEX at Twitter (re: PEX - Twitter's multi-platform executable archive format for Python)

Vinay Sajip vinay_sajip at yahoo.co.uk
Sat Feb 1 13:03:34 CET 2014

On Sat, 1/2/14, Paul Moore <p.f.moore at gmail.com> wrote:

> I would like it if pip worked the same way (even though the
> tide of installing pip into each virtualenv may be unstoppable
> by now). Would you be interested in writing a patch to do this?
> I don't have the time to commit to it and nobody else seems
> particularly interested in doing it, which is one reason it's not
> happened, I suspect. It's certainly not a fundamental limitation
> of pip.

It's not, and virtualenv does it with -p, right? Here's the distil code
that does it:

ARG_RE = re.compile(r'-[ep](=?.*)?')
# Use encode rather than br'' to avoid syntax error in 2.5, so we can
# print a more helpful message.
PYTHON_RE = re.compile(r'Python \d(\.\d+)+'.encode('utf-8'))

def find_python(env_or_python, is_env):
    result = None
    if not is_env:
        result = env_or_python
        env = os.path.abspath(env_or_python)
        if os.path.isdir(env):
            from distutils import sysconfig
            exe = 'python%s' % sysconfig.get_config_var('EXE')
            for subdir in ('bin', 'Scripts'):
                p = os.path.join(env, subdir, exe)
                if os.path.isfile(p) and os.access(p, os.X_OK):
                    result = p
    if result:
        import subprocess

            cmd = [result, '--version']
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
            out, _ = p.communicate()
            if p.returncode:
                logger.error('Command %r returned non-zero status: %d',
                             ' '.join(cmd), p.returncode)
                result = None
            elif not PYTHON_RE.match(out):
                logger.error('Command %r - unexpected output: %r',
                             ' '.join(cmd), out)
                result = None
        except Exception as e:
            logger.exception('Command %r - unexpected exception: %r',
                         ' '.join(cmd), e)
            result = None
    return result

def find_env_or_python(args=None):
    result = None
    args = (args or sys.argv)[1:]
    nargs = len(args)
    for i, arg in enumerate(args):
        m = ARG_RE.match(arg)
        if m:
            value = m.groups()[0]
            start = i + 1
            if value:
                if value[0] in '=:':
                    argval = value[1:]
                    argval = value
                end = i + 2
            elif i < (nargs - 1):
                argval = args[i + 1]
                end = i + 3
            # if result is set, then start and end indicate the slice to lose
            # from sys.argv. First, validate the env.
            result = find_python(argval, arg[1] =='e')
            if result:
                del sys.argv[start:end]
                if arg[1] == 'e':
                    msg = 'No such environment: %r' % argval
                    msg = 'No such Python interpreter: %r' % argval
                raise ValueError(msg)
    return result

def main(args=None):
    python = find_env_or_python()
    if python:
        cmd = [python] + sys.argv
        import subprocess
        return subprocess.call(cmd)
    # the rest of the main() logic goes here as normal

> If you'd rather have this as a selling point for distil, then that's
> fair. But the vast weight of existing pip users and
> documentation, plus the core support for pip as the preferred
> default installer encapsulated in PEP 453, is going to make
> this much less useful to people in practice.

I'm not trying to "sell" distil versus pip, as I've said before, though I do
want it to be better than pip, by trying out different ideas on how to
do things. In my admittedly biased opinion it already is better in numerous
respects, but if people won't see for lack of looking, that's not my loss :-)

The reasons I wouldn't volunteer to add this to pip are:

1. I don't believe I really need to, since the code above is pretty self-
    contained and shouldn't take too long to adapt. So, any obstacles to
    adding it would surely not be technical. Anyone can feel free to add
    the above to pip.

2. I find working on pip painful because of how it works. I have no
    animus against pip; I have overcome this pain in the past and
    did a fair chunk of the work in getting it to work on 2.x and 3.x in
    a single code-base. But that was when I thought it was important
    for 3.x adoption to get pip and virtualenv working under 3.x in a
    low-maintenance fashion (i.e. no 2to3). Of course, I now have the
    use of distil, so I seldom need to use pip :-)

3. I try to avoid "politics" and "religion" in software development when
    I can, and I feel their presence more in the pip project than I'm
    comfortable with. I don't really want to fight non-technical battles.

Re. PEP 453, I voiced my reservations at the time, but if "that ship
has sailed", then so be it - "nobody ever got fired for recommending
pip". The Python community will get the packaging infrastructure that
it deserves, just like a populace does with elections :-)


Vinay Sajip

More information about the Distutils-SIG mailing list