[Distutils] setup.py install using pip

Ronny Pfannschmidt opensource at ronnypfannschmidt.de
Thu Dec 3 15:37:42 EST 2015

Lets avoid getting setuptools even more complex in that way 

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

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

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
>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.
>$ 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

MFG Ronny

More information about the Distutils-SIG mailing list