How to obtain arguments passed to setup.py from pip with '--install-option'?

Solution 1:

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"

Solution 2:

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

Solution 3:

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