Conditional package install depending on other packages in environment

Apologies if this has been answered elsewhere… was unable to find this specific use case.

We have a package that uses qtpy to support either PySide2 or PyQt5 backends, but one of the two must be installed. If possible, we’d like to make our package installable simply with pip install my-package and have PySide2 installed if and only if PyQt5 does not already exist in the environment. I am aware that we could use extras and ask our users to pip install my-package[pyside2] … but if at all possible, we’d like to have the bare install work without requiring the extras syntax, while also preventing both qt backends from being installed (if a user already has one of them in their environment).

(we’d also prefer to avoid having a seperate my-package-core package with no dependencies, with my-package depending on both my-package-core and PySide2)

We initially attempted to achieve this by putting try: import PyQt5 logic in setup.py, but that strategy seems to fail (unless the end-user uses the --no-cache-dir) when pip builds a wheel for caching during the install step. PEP 508 markers also do not appear to allow this sort of environmental inquiry.

Any input greatly appreciated (even if it’s just “yeah you can’t do that” :sweat_smile:)

1 Like

Currently the dependency system is very simple, and as far as I know there’s no way to do this

There is technically Provides-Dist metadata, and theoretically I think you could solve this by getting PyQt5 and PySide2 to all add Provides-Dist: qt_backend or something to their metadata, but it’s not exactly a perfect fit for what you want, plus none of the PyPA tools like pip and PyPI do anything with this information anyway, so it wouldn’t help in any practical sense.

I am fairly certain I’ve seen this type of request before, but I can’t find anything relevant. Some similar discussions that may give you a handle on the state of things:

  1. Add support for opportunistic dependencies
  2. Add ability to specify “default” extras_require

I can see how this sort of thing would be useful (and also how it would complicate the lives of resolvers…). I think if we were to add support for this, we might need to know more about other peoples’ use cases and whether there are other packaging ecosystems that support this sort of thing that we could rely on.

I could imagine adding a syntax where a given requirement is actually an ordered list of requirements, like so:

(PySide2, PyQt5)

Which would be satisfied by anything in the group, and if nothing in the group is installed, it attempts to satisfy it from left to right.

In the meantime, I think your best option is an extra.

2 Likes

Thank you very much. Super helpful. Yeah, looks like our use case is pretty much exactly like the 2nd discussion you linked above. It would also be solved by opportunistic dependencies in the first example, as well the ordered list of requirements that you propose. (Provides-Dist also looks good, but seems like it would take agreement among too many players to be practical for us :slight_smile:).

In any case, these links are excellent and provide me with a much clearer picture of the state of things. Thanks for your time!

Here’s another use case for supporting Provides-Dist (and Obsoletes-Dist):

The opencv-python packages come in multiple flavors and only one of them should be installed as explicitly stated in their documentation. So if an install already has opencv-contrib-python installed, a request to install opencv-python (not an uncommon dependency for other packages) should be ignored as its functionality (and more) is already provided by opencv-contrib-python. Currently, users can end up in an environment with both opencv-python and opencv-contrib-python installed, and depending on order of install all kinds of breakage.

Since opencv-contrib-python provides a superset of opencv-python, being able to specify Obsoletes-Dist and having it honored by the package manager would also be of great use. Then a user or package requesting installation of opencv-contrib-python would lead to opencv-python being removed. That doesn’t cause a loss of functionality and prevents having more than one of the packages installed at a time, as should really be avoided.

For future reference, see also this stackoverflow post hinting at how the tensorflow ecosystem could benefit from this.

I wonder if using the package metadata for this is a good idea. I feel like package metadata ends up too often being incorrect or out of date, and then it is hard to fix (maintainers are not active anymore for example).

I wonder if we could improve on the constraints.txt idea taken from pip instead. What if it were possible to specify the following in a “constraints” file (on top of what is already feasible)?

  • if library is required, install instead:
    • library-contrib
    • library[gpu-enhanced]
    • library and third-party-library-plugin
  • if library is required, install it from:
    • this alternative index server
    • this git repository branch
  • and potentially more advanced fixes, typically overriding metadata of a poorly packaged dependency

The one doing the installation (the one building the final application) is the one more likely to know what should be installed. The one packaging the library only has partial knowledge, they can not possibly know in which application and in which environment their library will be integrated.

Related:

1 Like