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:)

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!