How to package multiple optional drivers in a single Python wheel and discover only installed ones via entry points

I’m creating a Python package that contains multiple hardware drivers (e.g., Serial, DMM, BLE, Power Supply).

Each driver may have optional dependencies, and I want to:
Package all drivers in a single wheel.
Allow users to install only the extras they need, e.g.:pip install mydrivers[serial,dmm]

Dynamically discover only installed drivers using entry points. For example, if only serial is installed, then only SerialDriver should be discoverable.
I have a basic setup like this in pyproject.toml:

[project.optional-dependencies]
serial = [“pyserial”]
dmm =

[project.entry-points.“mydrivers.plugins”]
serial = “mydrivers.serial_driver:SerialDriver”
dmm = “mydrivers.dmm_driver:DMMDriver”

My question:

Is this the correct way to handle optional dependencies with a single wheel?
Is there a cleaner or more “official” way to ensure only installed drivers are discovered?

It’s rather suboptimal to package all drivers into one wheel when you know that users are only going to need a limited set and the user still has to be aware of this to pick the right driver dependencies.

If each driver has its own wheel with its own entrypoint, it can remove the awkward optionality of its dependencies and the ensure only installed drivers are discovered will work out organically.

I should have specified we have 30 drivers already and we see it going to 60. Will be a lot of wheels.

That’s why I thought extras would be perfect for this. distribute only 4-5 wheels with drivers subsets. Like Measurement, connectivity…. and even from those subsesets users could install only what they need.

But it looks like ‘extras’ functionality is limited in this regard? I was expecting that if it lets me install a subset of a wheel, to also keep track for me on what’s installed from that wheel, and optionally to uninstall that subset.

Is is possible to do it another way? Than having one wheel for each driver?

There isn’t any tooling for installing subsets of a wheel. From an installers perspective wheels are an atomic unit of installation so if you want things to be installable separately you put them in separate wheels.

The extras function is not for installing subsets of a wheel but rather for optionally installing additional wheels. When you write pip install foo[bar] it just means to install the foo wheel and then install other wheels that are associated with the bar extra that is described in the foo wheel. It still requires each separately installable piece to be a separate wheel.

1 Like

Turn that on its head and the one-wheel policy gives you up to 59 drivers being redundantly and unconfigurability installed. IMO, extras are almost always a false friend since you can’t exclude the now dead weight code that depends on the extras.

If you were really determined to avoid having 60 distinct projects, you could have a single project that dynamically sets its name and file includes/excludes in a setup.py or hatchling local plugin based on an environment variable. I did something similar for target platform specific file inclusion with a noddy bash script to build them all in one step.