pythonsetuptoolssetup.py

How to reference a variable defined in setup.py?


I've got the following setup.py:

import itertools


dependencies = {
  'minimal': ['numpy', 'requests'],
  'option-a': ['scipy'],
  'option-b': ['matplotlib']
}

setup(
  install_requires=dependencies['minimal'],                           # minimal dependencies
  extras_require={
      'all': list(itertools.chain(*dependencies.values())),           # all dependencies included
      **{k: v for k, v in dependencies.items() if k != 'minimal'},    # each extra dependency group
  },
  # other setup parameters omitted
)

I used a variable dependencies to avoid duplicating lists of dependencies and to make it easy to maintain. It seems like a good approach.

I'd like to write a function verify_optional_extras('option-a') which will check if the packages for the option-a extras were installed.

If I could access the dependencies variable defined in setup.py I could easily write a verification function for those packages.

Possible answers to this question:

  1. Show me how to access dependencies in setup.py.
  2. Tell me a better way to organize optional dependencies.

Solution

  • If you want to access the information from within the installed package, then you should not attempt to access the setup.py source directly because it will not actually be there. Once your code is installed into site-packages, the installer i.e. the setup.py file itself is gone. You may be able to access it from within a development source tree, like a git checkout, but the same won't be true when your package gets installed and used by someone else.

    For a wheel installation, there was never even a setup.py inside the .whl file in the first place. It's just a zip file, unzip it and see for yourself! But the missing setup.py is true even in the case of a source distribution installation (mypkg-1.0.tar.gz), or anything installed from pip (which first builds a wheel file and then installs the wheel).

    Instead you will need something like this, to access the dependency data from the package metadata:

    from importlib.metadata import requires
    reqs = requires("mypkg")
    

    The structure is a little different, so you may need to parse it back to a dict, but all the information is there:

    >>> requires("mypkg")
    ['numpy',
     'requests',
     'numpy; extra == "all"',
     'requests; extra == "all"',
     'scipy; extra == "all"',
     'matplotlib; extra == "all"',
     'scipy; extra == "option-a"',
     'matplotlib; extra == "option-b"']
    

    These are requirement lines, if you need a tool for parsing them when you go to verify the optional deps installation, then you can use the packaging module for that:

    >>> from packaging.requirements import Requirement
    >>> req = Requirement('scipy >= 1.6; extra == "option-a"')
    >>> req.name
    'scipy'
    >>> req.specifier
    <SpecifierSet('>=1.6')>
    >>> "1.7.3" in req.specifier
    True
    >>> "1.5" in req.specifier
    False
    >>> req.marker
    <Marker('extra == "option-a"')>