Skip to content

PR: Add scipy-stubs as development dependency.#1342

Merged
KelSolaar merged 12 commits into
colour-science:developfrom
jorenham:scipy-stubs
Apr 20, 2025
Merged

PR: Add scipy-stubs as development dependency.#1342
KelSolaar merged 12 commits into
colour-science:developfrom
jorenham:scipy-stubs

Conversation

@jorenham
Copy link
Copy Markdown
Contributor

@jorenham jorenham commented Apr 6, 2025

Summary

This adds scipy-stubs as a development dependency. By doing so, 58 new pyright errors popped up. There were 18 that were caused by two bugs and two other issues in scipy-stubs, and I've fixed them. See scipy/scipy-stubs#495 for details.

The other 40 errors turned out to be typing issues in colour itself. I have included their fixes in this PR.

I also noticed that an additional error pops up when the latest numpy==2.2.4 release is installed (currently pinned at 2.2.0). I've addressed this in numpy/numpy#28660, and the fix will be included in the upcoming 2.2.5 (and 2.3.0) NumPy release(s). (resolved in numpy 2.2.5)

This additionally improves the static-typing compatibility for different versions of pyright, numpy, scipy, and scipy-stubs.

Preflight

Code Style and Quality

  • Pyright static checking has been run and passed.
  • Pre-commit hooks have been run and passed.

@jorenham
Copy link
Copy Markdown
Contributor Author

jorenham commented Apr 6, 2025

I have analyzed the typing errors in scipy/scipy-stubs#495 (comment). Through this, I found three bugs in scipy-stubs that I'll soon fix and publish a release for.

But the majority of the typing errors are true positives, and should probably be fixed in within colour itself. Unless anyone objects, I'd be willing to do include those fixes in this PR.

@KelSolaar
Copy link
Copy Markdown
Member

Thanks @jorenham, sounds good with me!

@jorenham
Copy link
Copy Markdown
Contributor Author

jorenham commented Apr 6, 2025

All errors are fixed when the master branch of scipy-stubs is installed. I could either (subtly) work around the remaining errors for now, or publish a scipy-stubs release and upgrade scipy and scipy-stubs to 1.15.2 and 1.15.2.2 here in requirements.txt, respectively (that will take me a bit more time though). What would have your preference @KelSolaar?


edit

I didn't want to force you to upgrade to the latest scipy, and chose to patch the remaining issues in colour itself.


def __init__(self, x: ArrayLike, y: ArrayLike, *args: Any, **kwargs: Any) -> None:
super().__init__(x, y, *args, **kwargs)
super().__init__(as_float_array(x), as_float_array(y), *args, **kwargs)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typing.cast would also be an option, but strictly speaking I'd have to cast it to Any for it to be theoretically allowed. Downcasting this correctly would require recreating the ToFloatND or ToFloat1D types from optype.numpy (docs) here, which would be rather messy.

Comment thread colour/appearance/rlab.py
M = np.matmul(np.matmul(MATRIX_R, row_as_diagonal(LMS_a_L)), MATRIX_XYZ_TO_HPE)
XYZ_ref = vecmul(M, XYZ)

Y_ref: NDArrayFloat
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ensures that the right @overload of spow is selected, so that LR will be inferred as NDArrayFloat

) = optimisation_factory()
optimisation_settings = {

optimisation_settings: dict[str, Any] = {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without this, it'd be inferred as dict[str, str], which due to its invariant type-parameters, would lead to it being rejected when passed as **kwargs to minimize below

**settings_BT2246,
)
),
).item(),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fmin objective function must return a scalar type, but this returns an array of shape (1,). This sneaky workaround was was the least intrusive I could find.

Comment thread colour/hints/__init__.py
def __init__(self, *args: Any, **kwargs: Any) -> None: ... # pragma: no cover

def __call__(self, x: ArrayLike) -> NDArray: # noqa: D102
def __call__(self, x: NDArrayFloat) -> NDArray: # noqa: D102
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

input positions are contravariant, so assigning to this protocol, requires that the parameter types of that type are less strict than that of this Protocol. Put differently; narrowing the ArrayLike parameter type to NDArrayFloat results in a broader Protocol type.

target_o: ArrayLike, coefficients_0_o: ArrayLike
) -> Tuple[NDArrayFloat, float]:
target_o: NDArrayFloat, coefficients_0_o: NDArrayFloat
) -> Tuple[NDArrayFloat, float | np.float64]:
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both result.fun: np.float64 and errors.error: float are assignable to float | np.float64, and on numpy >= 2.2 it's even equivalent to float64 (float64 has been made a proper subtype of builtins.float)

coefficients = dimensionalise_coefficients(coefficients, cmfs.shape)

return coefficients, error
return coefficients, float(error)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because error: float | np.float64 at this point

"""Define the objective function."""

return cast(NDArrayFloat, np.sum(np.diff(a) ** 2))
return np.sum(np.square(np.diff(a)))
Copy link
Copy Markdown
Contributor Author

@jorenham jorenham Apr 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In numpy==2.2.0 there was a bug that caused np.diff(a) ** 2 to be inferred as np.signedinteger. This has been resolved in 2.2.4, but I figured that square would be a more stable solution.

@jorenham
Copy link
Copy Markdown
Contributor Author

jorenham commented Apr 6, 2025

Pardon the spam. I wanted to explain some of the non-obvious choices I made here. Feel free to resolve them as you see fit.

@jorenham
Copy link
Copy Markdown
Contributor Author

This is ready for review, in case you missed it :)

@KelSolaar KelSolaar changed the title add scipy-stubs as dev dependency PR: Add scipy-stubs as development dependency. Apr 12, 2025
Copy link
Copy Markdown
Member

@KelSolaar KelSolaar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot! LGTM besides the private _typeshed import.

Comment thread colour/io/luts/sony_spimtx.py Outdated
Co-authored-by: Thomas Mansencal <thomas.mansencal@gmail.com>
@KelSolaar KelSolaar merged commit 12044e4 into colour-science:develop Apr 20, 2025
16 checks passed
@KelSolaar
Copy link
Copy Markdown
Member

Thanks @jorenham, merged!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants