2

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.
sinoroc
  • 18,409
  • 2
  • 39
  • 70
David Parks
  • 30,789
  • 47
  • 185
  • 328
  • 1
    Do you mean you want to use the variable in your main script? Not during build process? – Alexander Apr 20 '22 at 00:25
  • 1
    Correct, my goal is to write a function in main code to assert if the appropriate optional packages have been installed. Those optional packages are defined here and I was hoping to not need to keep two sets of identical lists in different places (else someone will almost certainly forget to update one of them someday in the future) – David Parks Apr 20 '22 at 22:39
  • what does your folder structure look like? The script from which you want to access setup.py and your setup.py – Bijay Regmi Apr 22 '22 at 18:30

1 Answers1

3

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 within your my_module:
from importlib.metadata import requires
reqs = requires(__package__)

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"')>
wim
  • 338,267
  • 99
  • 616
  • 750