Python library: optionally support numpy types without depending on numpy

Question:

Context

We develop a Python library that contains a function expecting a numlike parameter. We specify this in our signature and make use of python type hints:

def cool(value: float | int | List[float | int]) 

Problem & Goal

During runtime, we noticed it’s fine to pass in numpy number types as well, e.g. np.float16(1.2345). So we thought: why not incorporate "numpy number types" into our signature as this would be beneficial for the community that will use our library.

However, we don’t want numpy as dependency in our project. We’d like to only signify in the method signature that we can take a float, int, a list of them OR any "numpy number type". If the user hasn’t installed numpy on their system, they should still be able to use our library and just ignore that they could possibly pass in a "numpy number type" as well.

We don’t want to depend on numpy as we don’t use it in our library (except for allowing their types in our method signature). So why include it in our dependency graph? There’s no reason to do so. One dependency less is better.

Additional requirements/context

  • We search for an answer that is compatible with all Python versions >=3.8.
  • (The answer should work with setuptools>=69.0.)
  • The answer should be such that we get proper IntelliSense (Ctrl + Space) when typing cool( in an IDE, e.g. VSCode.
  • This is what our pyproject.toml looks like.

Efforts

  • We’ve noticed the option [project.optional-dependencies] for the pyproject.toml file, see here. However, it remains unclear how this optional dependencies declaration helps us in providing optional numpy datatypes in our method signatures.
  • numpy provides the numpy.typing type annotations. Is it somehow possible to only depend on this subpackage?
  • We did search on search engines and found this SO question, however our question is more specific with regards to how we can only use types from another module. We also found this SO question, but despite having "optional" in its title, it’s not about optional numpy types.
Asked By: Splines

||

Answers:

Defer evaluation of annotations, and only import numpy conditionally.

from __future__ import annotations
import typing as t

if t.TYPE_CHECKING:
    import numpy as np

def cool(value: int | np.floating | etc ...):
    ...

Now the numpy dependency is only necessary when type-checking.

See PEP 563 – Postponed Evaluation of Annotations

Side-questions..

numpy provides the numpy.typing type annotations. Is it somehow possible to only depend on this subpackage?

No, this is not possible.

We’ve noticed the option [project.optional-dependencies] for the pyproject.toml file …

It doesn’t really help you much here. It could still be useful if you wanted "extra" dependencies which the user can opt-in for, e.g.:

pip install mypkg          # install with required dependencies
pip install mypkg[typing]  # install with extra dependencies such as numpy

Then you could use this to easily install the package along with the soft-deps in your CI, for example.

Answered By: wim
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.