Declare the optional dependency and then import it unconditionally if the optional is installed. You can conditionally import a file you control based on asking whether an optional specifier was installed, either by probing the filesystem, a CLI argument, an environment variable, or whatever folks come up with for https://github.com/pypa/packaging-problems/issues/215. The "if: ... import" laziness-defeating clause only applies to your stub module, and the imports in that module are unconditional and thus will work with analyzers/laziness/etc.
I think this import flow tends to be the canonical one.
I might recommend doing something like importing from something like `numpy.version` (or some other "random" very small utils package) so that the work done on file load is still fairly small.
Common? Yes. Canonical/ideal? No. Optional dependency specifiers in your package metadata is a much better way to go. You can use those even if you don't publish your package as an artifact; `pip install -e .[optional-thing]` should work, or can be made to, easily.
In my preference order: an explicit CLI option/envvar which requires the optional dep, a flag file on the filesystem, or an ImportError attempt if you absolutely must. Use a stub/trampoline file in the conditionals so that static analyzers and laziness analysis on that file itself still work.
Yep it's not clear, and a bit of a case-by-case thing. Fortunately most codebases only need to do this in a handful of spots so you can really "just" look at a package and find a file that probably is safe.
For example, numba._version only imports standard library stuff so is probably good enough here[0]
It would be nice to have general querying capabilities here, of course. I just think that in practice there's at least a halfway-decent workaround in almost any real situation
Comments
What is the right way to handle an optional dependency?
https://packaging.python.org/en/latest/guides/writing-pyproj...
I don't understand, sorry. How does declaring an optional dependency in a toml file solve the problem of accessing the symbol in your code?
Also, you need something that works for simple one-file scripts, not just a whole package or project.
Declare the optional dependency and then import it unconditionally if the optional is installed. You can conditionally import a file you control based on asking whether an optional specifier was installed, either by probing the filesystem, a CLI argument, an environment variable, or whatever folks come up with for https://github.com/pypa/packaging-problems/issues/215. The "if: ... import" laziness-defeating clause only applies to your stub module, and the imports in that module are unconditional and thus will work with analyzers/laziness/etc.
If you need something for one-off scripts, use https://docs.astral.sh/uv/guides/scripts/.
I think this import flow tends to be the canonical one.
I might recommend doing something like importing from something like `numpy.version` (or some other "random" very small utils package) so that the work done on file load is still fairly small.
Common? Yes. Canonical/ideal? No. Optional dependency specifiers in your package metadata is a much better way to go. You can use those even if you don't publish your package as an artifact; `pip install -e .[optional-thing]` should work, or can be made to, easily.
That's only part of the puzzle. Your program needs to branch on whether the dependency is installed or not!
"Oh you don't have optional dependency? Then do X" needs to happen at runtime right?
I explained further here: https://news.ycombinator.com/item?id=49604462
In my preference order: an explicit CLI option/envvar which requires the optional dep, a flag file on the filesystem, or an ImportError attempt if you absolutely must. Use a stub/trampoline file in the conditionals so that static analyzers and laziness analysis on that file itself still work.
It's not clear such a thing even exists in general though. It's certainly not guaranteed by packages.
What would that be for numba?
Yep it's not clear, and a bit of a case-by-case thing. Fortunately most codebases only need to do this in a handful of spots so you can really "just" look at a package and find a file that probably is safe.
For example, numba._version only imports standard library stuff so is probably good enough here[0]
It would be nice to have general querying capabilities here, of course. I just think that in practice there's at least a halfway-decent workaround in almost any real situation
[0] https://github.com/numba/numba/blob/main/numba/_version.py