Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allowing a python package requirement to fail in setup

Is there a standard way to allow a requirement of a python package to fail in a packages setup script?

I am creating a package similar to Python Social Auth in that it has a number of providers.

Is there a standard way to allow the installation requirements of some of the providers to fail, but still have the package install just fine?

like image 412
dkroy Avatar asked Oct 01 '22 11:10

dkroy


1 Answers

Optional dependencies specified by user at installation time:

You can use the extras_require parameter to setup(): http://pythonhosted.org/setuptools/setuptools.html#declaring-extras-optional-features-with-their-own-dependencies

setup(
    name="MyProject",
    ...
    extras_require = {
        'ProviderX':  ["DependencyX1", "DependencyX2"],
        'ProviderY': ["DependencyY"],
    }
)

With this method, the user can ask to install specific extensions pip install Myproject[ProviderX].

Optional dependencies based on existing packages:

In order to automatically detect installed packages, you can build the list of requirements dynamically. For example, you can look at how matplotlib does this (they have many optional backends for plotting amongst others): https://github.com/matplotlib/matplotlib.

Basically, setup.py is just regular python code, so you can run a function which checks for the optional dependencies, and adjust requirements and your package list to be installed accordingly.

The way matplotlib does this is by defining a class for dependencies which it extends for each dependency (in setupExt.py).

class SetupPackage(object):
    optional = False

    def check(self):
        """
        Checks whether the dependencies are met. [...]
        """
        pass

    def get_packages(self):
        """
        Get a list of package names to add to the configuration.
        These are added to the `packages` list passed to
        `distutils.setup`.
        """
        return []

    def get_namespace_packages(self):
        """
        Get a list of namespace package names to add to the configuration.
        These are added to the `namespace_packages` list passed to
        `distutils.setup`.
        """
        return []


    def get_py_modules(self):
        """
        Get a list of top-level modules to add to the configuration.
        These are added to the `py_modules` list passed to
        `distutils.setup`.
        """
        return []

    ...

class Numpy(SetupPackage):
    ...

It then iterates over each dependency in setup.py, checks whether it should be installed, and accordingly extends each list to be passed to setup()

mpl_packages = [
    'Building Matplotlib',
    setupext.Six(),
    setupext.Dateutil(),
    ...

good_packages = []
for package in mpl_packages:
    [...]
    # check and append
    if ...
        good_packages.append(package)

[...]
for package in good_packages:
    if isinstance(package, str):
        continue
    packages.extend(package.get_packages())
    namespace_packages.extend(package.get_namespace_packages())
    py_modules.extend(package.get_py_modules())
    ext = package.get_extension()
like image 143
Jonathan Villemaire-Krajden Avatar answered Oct 04 '22 20:10

Jonathan Villemaire-Krajden