34

I am using pip 1.4.1, attempting to install a package from a local path, for example:

pip install /path/to/my/local/package

This does what I want, which is more or less the equivalent of running python /path/to/my/local/package/setup.py install, but I would like to pass some additional options/arguments to my package's setup.py install.

I understand from the pip documentation that this is possible with the --install-option option, for example:

pip install --install-option="--some-option" /path/to/my/local/package

This post from the python-virtualenv Google Group suggests this is possible.

What I do not understand is how to obtain the passed-in "--some-option" from within setup.py. I tried looking at sys.argv, but no matter what I put for "--install-option=", sys.argv is always this:

['-c', 'egg_info', '--egg-base', 'pip-egg-info']

How can I get the values of things passed in as "--install-option" from pip install?

djangodude
  • 5,362
  • 3
  • 27
  • 39
  • 1
    Related: [distutils: How to pass a user defined parameter to setup.py?](https://stackoverflow.com/questions/677577/distutils-how-to-pass-a-user-defined-parameter-to-setup-py) – Delgan Sep 13 '17 at 16:37

5 Answers5

24

You need to extend the install command with a custom command of your own. In the run method you can expose the value of the option to setup.py (in my example I use a global variable).

from setuptools.command.install import install


class InstallCommand(install):
    user_options = install.user_options + [
        ('someopt', None, None), # a 'flag' option
        #('someval=', None, None) # an option that takes a value
    ]

    def initialize_options(self):
        install.initialize_options(self)
        self.someopt = None
        #self.someval = None

    def finalize_options(self):
        #print("value of someopt is", self.someopt)
        install.finalize_options(self)

    def run(self):
        global someopt
        someopt = self.someopt # will be 1 or None
        install.run(self)

Register the custom install command with the setup function.

setup(
    cmdclass={
        'install': InstallCommand,
    },
    :

It seems that the order of your arguments is off

pip install /path/to/my/local/package --install-option="--someopt"

Ronen Botzer
  • 6,951
  • 22
  • 41
  • By the way, once you subclass the install command you should be able to see those on `sys.argv`. – Ronen Botzer Oct 18 '15 at 16:53
  • Just to clarify, within setup(...) how you would access the variable you've passed in using the cmdclass technique? – Mani Jan 28 '16 at 15:56
  • The cmdclass sets up having an option (at all). Without it pip will complain and not execute the action. It will pass it along to setup.py, and in there you can inspect your sys.argv for the value of the option. – Ronen Botzer Jan 28 '16 at 17:02
  • By the way, using underscores in the `user_options` seems forbidden: `distutils.errors.DistutilsGetoptError: invalid long option name 'some_option' (must be letters, numbers, hyphens only` Right, I'm trying to use the commented out 'some_val' version of this code and end up with `error: option --someval not recognised`. This is despite the fact that `python setup.py install -h` shows that the new flag being recognised. – Mani Jan 29 '16 at 08:43
  • Odd, this wasn't a problem with the version of setuptools I worked with. Ugh, the documentation is horrendous. Anyway, thanks for the tip Mani. I fixed the example. – Ronen Botzer Jan 29 '16 at 18:57
  • 1
    this simply does not work, pip ingests the options and sends them to /dev/null :( – Tommaso Barbugli Apr 07 '16 at 14:26
  • For reference: https://github.com/aerospike/aerospike-client-python/blob/master/setup.py#L226-L229 – Ronen Botzer Apr 08 '16 at 01:11
  • `--install-option` was deprecated in [pip 22.3](https://pip.pypa.io/en/stable/news/#v22-3) and removed in [pip 23.1](https://pip.pypa.io/en/stable/news/#v23-1). – wim Jul 28 '23 at 19:20
11

For consistency, you can add an option to both setup.py install and setup.py develop (aka pip install -e): (building off Ronen Botzer's answer)

from setuptools import setup
from setuptools.command.install import install
from setuptools.command.develop import develop


class CommandMixin(object):
    user_options = [
        ('someopt', None, 'a flag option'),
        ('someval=', None, 'an option that takes a value')
    ]

    def initialize_options(self):
        super().initialize_options()
        # Initialize options
        self.someopt = None
        self.someval = 0

    def finalize_options(self):
        # Validate options
        if self.someval < 0:
            raise ValueError("Illegal someval!")
        super().finalize_options()

    def run(self):
        # Use options
        global someopt
        someopt = self.someopt # will be 1 or None

        super().run()

class InstallCommand(CommandMixin, install):
    user_options = getattr(install, 'user_options', []) + CommandMixin.user_options

class DevelopCommand(CommandMixin, develop):
    user_options = getattr(develop, 'user_options', []) + CommandMixin.user_options

setup(
    ...,
    cmdclass={
        'install': InstallCommand,
        'develop': DevelopCommand,
    }

Then you can pass options to pip like:

pip install --install-option="--someval=1" --install-option="--someopt" .

Or in develop mode:

pip install -e --install-option="--someval=1" .
user2561747
  • 1,333
  • 2
  • 16
  • 39
Quantum7
  • 3,165
  • 3
  • 34
  • 45
  • I want to send include_dirs and library_dirs to build my module, previously I'd populate the include_dirs and library_dirs in setup(), how should I be going about this now? – Hossein Sep 28 '20 at 10:34
  • 1
    @Rika Don't do this via the install command. Instead look at https://stackoverflow.com/q/51522248/81658 – Quantum7 Nov 11 '20 at 10:16
6

It works well and also documented.

from setuptools.command.install import install

class InstallCommand(install):             
    user_options = install.user_options + [
        ('engine=', None, '<description for this custom option>'),
    ]                                      

    def initialize_options(self):          
        install.initialize_options(self)   
        self.engine = None  

    def finalize_options(self):                   
        print("value of engine is", self.engine)
        install.finalize_options(self)            

    def run(self):                                
        print(self.engine)                       
        install.run(self)                         

setup(
...
cmdclass={'install': InstallCommand}
...
)

One of common mistakes is to pass setup options to pip like you pass it to setup directly. Use options from pip like that:

pip install . --install-option="--engine=rabbitmq"

But this way is a wrong way:

pip install . --install-option="--engine rabbitmq"

Absence of equal sign causes well known error:

error: option --engines rabbitmq not recognized

scrutari
  • 1,378
  • 2
  • 17
  • 33
I159
  • 29,741
  • 31
  • 97
  • 132
1

I was having this problem installing pyside.

I needed to specify the --qmake option.

This is the form you need:

pip install --install-option="--qmake=/usr/lib64/qt4/bin/qmake" PySide
cuthb3rt
  • 19
  • 3
  • Thanks for answering. I had set this aside as I found other ways around the problem a while ago, but I will give that a try and see. As I recall, I don't think it worked; there is some difference between installing a local package versus one from PyPI but I may be mistaken. – djangodude Nov 03 '14 at 19:29
  • This option `--qmake` is related to project `PySide`. To add a custom option you need to register derived class of `setuptools.command.install` (and/or `.develop`) to manage it (as written in the answers above). – Pascal H. Aug 20 '20 at 16:26
-1

On top of this great anwser. One more thing to notice is that --install-options doesn't work with wheel

Since version 7.0 pip supports controlling the command line options given to setup.py via requirements files. This disables the use of wheels (cached or otherwise) for that package, as setup.py does not exist for wheels.

However, when you build the wheel with setup.py, you can use

python setup.py bdist_wheel install -your-options

To customize the install phase and this will affect the .dist-info of the wheel package.

Izana
  • 2,537
  • 27
  • 33