Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

distutils: How to pass a user defined parameter to setup.py?

People also ask

Is distutils deprecated?

In Python 3.10 and 3.11, distutils will be formally marked as deprecated. All known issues will be closed at this time. import distutils will raise a deprecation warning. New issues that would be considered release blocking may still be fixed, but support for new tools or platforms will not be added.

What is Package_data in setup py?

package_data. By default, include_package_data considers all non .py files found inside the package directory ( src/mypkg in this case) as data files, and includes those that satisfy (at least) one of the above two conditions into the source distribution, and consequently in the installation of your package.


As Setuptools/Distuils are horribly documented, I had problems finding the answer to this myself. But eventually I stumbled across this example. Also, this similar question was helpful. Basically, a custom command with an option would look like:

from distutils.core import setup, Command

class InstallCommand(Command):
    description = "Installs the foo."
    user_options = [
        ('foo=', None, 'Specify the foo to bar.'),
    ]
    def initialize_options(self):
        self.foo = None
    def finalize_options(self):
        assert self.foo in (None, 'myFoo', 'myFoo2'), 'Invalid foo!'
    def run(self):
        install_all_the_things()

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

Here is a very simple solution, all you have to do is filter out sys.argv and handle it yourself before you call to distutils setup(..). Something like this:

if "--foo" in sys.argv:
    do_foo_stuff()
    sys.argv.remove("--foo")
...
setup(..)

The documentation on how to do this with distutils is terrible, eventually I came across this one: the hitchhikers guide to packaging, which uses sdist and its user_options. I find the extending distutils reference not particularly helpful.

Although this looks like the "proper" way of doing it with distutils (at least the only one that I could find that is vaguely documented). I could not find anything on --with and --without switches mentioned in the other answer.

The problem with this distutils solution is that it is just way too involved for what I am looking for (which may also be the case for you). Adding dozens of lines and subclassing sdist is just wrong for me.


Yes, it's 2015 and the documentation for adding commands and options in both setuptools and distutils is still largely missing.

After a few frustrating hours I figured out the following code for adding a custom option to the install command of setup.py:

from setuptools.command.install import install


class InstallCommand(install):
    user_options = install.user_options + [
        ('custom_option=', None, 'Path to something')
    ]

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

    def finalize_options(self):
        #print('The custom option for install is ', self.custom_option)
        install.finalize_options(self)

    def run(self):
        global my_custom_option
        my_custom_option = self.custom_option
        install.run(self)  # OR: install.do_egg_install(self)

It's worth to mention that install.run() checks if it's called "natively" or had been patched:

if not self._called_from_setup(inspect.currentframe()):
    orig.install.run(self)
else:
    self.do_egg_install()

At this point you register your command with setup:

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

You can't really pass custom parameters to the script. However the following things are possible and could solve your problem:

  • optional features can be enabled using --with-featurename, standard features can be disabled using --without-featurename. [AFAIR this requires setuptools]
  • you can use environment variables, these however require to be set on windows whereas prefixing them works on linux/ OS X (FOO=bar python setup.py).
  • you can extend distutils with your own cmd_classes which can implement new features. They are also chainable, so you can use that to change variables in your script. (python setup.py foo install) will execute the foo command before it executes install.

Hope that helps somehow. Generally speaking I would suggest providing a bit more information what exactly your extra parameter should do, maybe there is a better solution available.


I successfully used a workaround to use a solution similar to totaam's suggestion. I ended up popping my extra arguments from the sys.argv list:

import sys
from distutils.core import setup
foo = 0
if '--foo' in sys.argv:
    index = sys.argv.index('--foo')
    sys.argv.pop(index)  # Removes the '--foo'
    foo = sys.argv.pop(index)  # Returns the element after the '--foo'
# The foo is now ready to use for the setup
setup(...)

Some extra validation could be added to ensure the inputs are good, but this is how I did it