[Distutils] setup.py install using pip

Erik Bray erik.m.bray at gmail.com
Thu Dec 3 17:01:21 EST 2015


On Thu, Dec 3, 2015 at 3:37 PM, Ronny Pfannschmidt
<opensource at ronnypfannschmidt.de> wrote:
> Lets avoid getting setuptools even more complex in that way

It's not deeply complex--it's just bypassing the normal behavior of
using easy_install.  In my example code I subclassed
the existing command, but an easier approach would be to build it into
setuptools.

The way the 'install' command works in setuptools is to hand
installation off to the easy_install command unless
--single-version-externally-managed is specified (as well as
--record).  Otherwise it hands installation off to the default
base 'install' command of distutils.

This proposal just adds a third option for what actual installer the
'install' command should
use.  But it's opt-in instead of forced on any package by default (as
easy_install is forced on us by setuptools).

> Putting pip-ish-ness on top of easy install is a maintenance horror and I don't think setuptools does has enough consistent developer resources to handle something like that

It doesn't put anything "on top of" easy_install; it ignores easy_install.

> Instead let's just give options to destroy the normal install command from setup.py so projects can phase out easy install forcefully making downstream require patches or pip usage

That's the goal, yes.  This is just offering a transitional tool.


Erik

>
> Am 3. Dezember 2015 21:06:06 MEZ, schrieb Erik Bray <erik.m.bray at gmail.com>:
>>Hi all,
>>
>>I've been on vacation for a bit in general, and on vacation from this
>>mailing list even longer.  I'm not entirely caught up yet on the
>>latest developments so apologies if something like this is entirely
>>moot by now.
>>
>>But I have seen some discussions here and in other lists related to
>>using pip for all installations, and phasing out the old distutils
>>`./setup.py install` (eg. [1]).  This is not a new discussion, and
>>there are many related discussions, for example, about changing
>>setuptools not to default to egg installs anymore (see [2]).
>>
>>I'm definitely all for this in general.  Nowadays whenever I install a
>>package from source I run `pip install .`  But of course there are a
>>lot of existing tools, not to mention folk wisdom, assuming
>>`./setup.py install`.  We also don't want to change the long-existing
>>behavior in setuptools.
>>
>>I have a modest proposal for a small addition to setuptools that might
>>be helpful in a transition away from using setuptools+distutils for
>>installation.  This would be to add a `--pip` flag to setuptools'
>>install command (or possibly straight in distutils too, but might as
>>well start with setuptools).
>>
>>Therefore, running
>>
>>$ ./setup.py install --pip
>>
>>would be equivalent to running
>>
>>$ pip install .
>>
>>By extension, running
>>
>>$ ./setup.py install --pip --arg1 --arg2=foo
>>
>>would be equivalent to
>>
>>$ pip install --install-option="--arg1" --install-option="--arg2=foo" .
>>
>>and so on.
>>
>>By making `--pip` opt-in, it does not automatically break backward
>>compatibility for users expecting `./setup.py install` to use
>>easy_install.  However, individual users may opt into it globally by
>>adding
>>
>>[install]
>>pip = True
>>
>>to their .pydistutils.cfg.  Similarly, package authors who are
>>confident that none of their users are ever going to care about egg
>>installs (e.g. me) can add the same to their project's setup.cfg.
>>
>>Does something like this have any merit?  I hacked together a
>>prototype which follows the sig line.  It's just a proof of concept,
>>but seems to work in the most basic cases.  I'd like to add it to my
>>own projects too, but would appreciate some peer review.
>>
>>Thanks,
>>Erik
>>
>>
>>
>>[1]
>>https://mail.scipy.org/pipermail/numpy-discussion/2015-November/074142.html
>>[2]
>>https://bitbucket.org/pypa/setuptools/issues/371/setuptools-and-state-of-pep-376
>>
>>
>>$ cat pipinstall.py
>>from distutils.errors import DistutilsArgError
>>from setuptools.command.install import install as SetuptoolsInstall
>>
>>
>>class PipInstall(SetuptoolsInstall):
>>    command_name = 'install'
>>    user_options = SetuptoolsInstall.user_options + [
>>        ('pip', None, 'install using pip; ignored when also using '
>>                      '--single-version-externally-managed')
>>    ]
>>    boolean_options = SetuptoolsInstall.boolean_options + ['pip']
>>
>>    def initialize_options(self):
>>        SetuptoolsInstall.initialize_options(self)
>>        self.pip = False
>>
>>    def finalize_options(self):
>>        SetuptoolsInstall.finalize_options(self)
>>
>>        if self.single_version_externally_managed:
>>            self.pip = False
>>
>>        if self.pip:
>>            try:
>>                import pip
>>            except ImportError:
>>                raise DistutilsArgError(
>>                  'pip must be installed in order to install with the '
>>                    '--pip option')
>>
>>    def run(self):
>>        if self.pip:
>>            import pip
>>            opts = (['install', '--ignore-installed'] +
>>                    ['--install-option="{0}"'.format(opt)
>>                     for opt in self._get_command_line_opts()])
>>            pip.main(opts + ['.'])
>>        else:
>>            SetuptoolsInstall.run(self)
>>
>>    def _get_command_line_opts(self):
>>        # Generate a mapping from the attribute name associated with a
>># command-line option to the name of the command-line option (including
>>        # an = if the option takes an argument)
>>      attr_to_opt = dict((opt[0].rstrip('=').replace('-', '_'), opt[0])
>>                           for opt in self.user_options)
>>
>>  opt_dict = self.distribution.get_option_dict(self.get_command_name())
>>        opts = []
>>
>>        for attr, value in opt_dict.items():
>>            if value[0] != 'command line' or attr == 'pip':
>>         # Only look at options passed in on the command line (ignoring
>>                # the pip option itself)
>>                continue
>>
>>            opt = attr_to_opt[attr]
>>
>>            if opt in self.boolean_options:
>>                opts.append('--' + opt)
>>            else:
>>                opts.append('--{0}{1}'.format(opt, value[1]))
>>
>>        return opts
>>
>>    @staticmethod
>>    def _called_from_setup(run_frame):
>>        # A hack to work around a setuptools hack
>>        return SetuptoolsInstall._called_from_setup(run_frame.f_back)
>>_______________________________________________
>>Distutils-SIG maillist  -  Distutils-SIG at python.org
>>https://mail.python.org/mailman/listinfo/distutils-sig
>
> MFG Ronny


More information about the Distutils-SIG mailing list