Skip to content

Commit e9522a3

Browse files
authored
Merge pull request #96 from espdev/mypy
Bump version, bump minimal Python version to 3.10, fix type annotations and mypy checks
2 parents 6b98a4d + eb2e21e commit e9522a3

File tree

13 files changed

+417
-570
lines changed

13 files changed

+417
-570
lines changed

.github/workflows/main.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
max-parallel: 8
2828
matrix:
2929
platform: [ "ubuntu-latest", "macos-latest", "windows-latest" ]
30-
python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
30+
python-version: [ "3.10", "3.11", "3.12", "3.13" ]
3131
poetry-version: [ "1.8.5" ]
3232

3333
steps:
@@ -58,7 +58,7 @@ jobs:
5858
- name: Install the project dependencies
5959
run: poetry install -E docs
6060

61-
- name: Run static analysis and linters
61+
- name: Run static analysis, linters and mypy
6262
run: poetry run poe check
6363

6464
- name: Run tests

.github/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
- name: Setup Python
2727
uses: actions/setup-python@v5
2828
with:
29-
python-version: "3.11"
29+
python-version: "3.12"
3030

3131
- name: Install Poetry
3232
uses: abatilo/actions-poetry@v4

.pre-commit-config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,9 @@ repos:
1616
language: system
1717
types: [ python ]
1818
require_serial: true
19+
- id: check-types
20+
name: check-types
21+
entry: poetry run poe check-types-pre-commit
22+
language: system
23+
types: [ python ]
24+
require_serial: true

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## v1.3.0 (14.04.2025)
4+
5+
* Bump minimal Python version to 3.10
6+
* Fix type annotations
7+
* Fix checking types by mypy
8+
39
## v1.2.1 (10.04.2025)
410

511
* Update dependencies

csaps/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from csaps._shortcut import AutoSmoothingResult, csaps
77
from csaps._sspndg import NdGridCubicSmoothingSpline, NdGridSplinePPForm
88
from csaps._sspumv import CubicSmoothingSpline, SplinePPForm
9-
from csaps._types import MultivariateDataType, NdGridDataType, UnivariateDataType
9+
from csaps._types import MultivariateDataType, SequenceUnivariateDataType, UnivariateDataType
1010
from csaps._version import __version__
1111

1212
__all__ = [
@@ -23,6 +23,6 @@
2323
# Type-hints
2424
'UnivariateDataType',
2525
'MultivariateDataType',
26-
'NdGridDataType',
26+
'SequenceUnivariateDataType',
2727
'__version__',
2828
]

csaps/_base.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import numpy as np
99

10-
from ._types import TData, TExtrapolate, TNu, TProps, TSmooth, TSpline, TXi
10+
from ._types import TData, TExtrapolate, TNu, TProps, TSmooth, TSpline, TXi, FloatNDArrayType
1111

1212

1313
class ISplinePPForm(abc.ABC, Generic[TData, TProps]):
@@ -22,7 +22,7 @@ def breaks(self) -> TData:
2222
2323
Returns
2424
-------
25-
breaks : Union[np.ndarray, ty.Tuple[np.ndarray, ...]]
25+
breaks : Union[np.ndarray, Tuple[np.ndarray, ...]]
2626
Breaks data
2727
"""
2828

@@ -44,7 +44,7 @@ def order(self) -> TProps:
4444
4545
Returns
4646
-------
47-
order : ty.Union[int, ty.Tuple[int, ...]]
47+
order : Union[int, Tuple[int, ...]]
4848
The spline order
4949
"""
5050

@@ -55,7 +55,7 @@ def pieces(self) -> TProps:
5555
5656
Returns
5757
-------
58-
pieces : ty.Union[int, ty.Tuple[int, ...]]
58+
pieces : Union[int, Tuple[int, ...]]
5959
The spline pieces data
6060
"""
6161

@@ -98,5 +98,10 @@ def spline(self) -> TSpline:
9898
"""Returns spline representation in PP-form"""
9999

100100
@abc.abstractmethod
101-
def __call__(self, xi: TXi, nu: Optional[TNu] = None, extrapolate: Optional[TExtrapolate] = None) -> np.ndarray:
101+
def __call__(
102+
self,
103+
xi: TXi,
104+
nu: Optional[TNu] = None,
105+
extrapolate: Optional[TExtrapolate] = None,
106+
) -> FloatNDArrayType:
102107
"""Evaluates spline on the data sites"""

csaps/_reshape.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,11 @@ def umv_coeffs_to_canonical(arr: np.ndarray, pieces: int):
111111
112112
"""
113113

114-
ndim = arr.shape[0]
115-
order = arr.shape[1] // pieces
114+
ndim: int = arr.shape[0]
115+
order: int = arr.shape[1] // pieces
116+
117+
shape: tuple[int, ...]
118+
strides: tuple[int, ...]
116119

117120
if ndim == 1:
118121
shape = (order, pieces)

csaps/_shortcut.py

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
from typing import NamedTuple, Optional, Sequence, Union, overload
66
from collections import abc as c_abc
77

8+
import numpy as np
9+
810
from ._base import ISmoothingSpline
9-
from ._sspndg import NdGridCubicSmoothingSpline, ndgrid_prepare_data_vectors
11+
from ._sspndg import NdGridCubicSmoothingSpline
1012
from ._sspumv import CubicSmoothingSpline
11-
from ._types import MultivariateDataType, NdGridDataType, UnivariateDataType
13+
from ._types import MultivariateDataType, SequenceUnivariateDataType, UnivariateDataType
1214

1315

1416
class AutoSmoothingResult(NamedTuple):
@@ -24,6 +26,8 @@ class AutoSmoothingResult(NamedTuple):
2426
# **************************************
2527
# csaps signatures
2628
#
29+
30+
2731
@overload
2832
def csaps(
2933
xdata: UnivariateDataType,
@@ -66,10 +70,10 @@ def csaps(
6670

6771
@overload
6872
def csaps(
69-
xdata: NdGridDataType,
73+
xdata: SequenceUnivariateDataType,
7074
ydata: MultivariateDataType,
7175
*,
72-
weights: Optional[NdGridDataType] = None,
76+
weights: Optional[SequenceUnivariateDataType] = None,
7377
smooth: Optional[Sequence[float]] = None,
7478
axis: Optional[int] = None,
7579
normalizedsmooth: bool = False,
@@ -79,11 +83,11 @@ def csaps(
7983

8084
@overload
8185
def csaps(
82-
xdata: NdGridDataType,
86+
xdata: SequenceUnivariateDataType,
8387
ydata: MultivariateDataType,
84-
xidata: NdGridDataType,
88+
xidata: SequenceUnivariateDataType,
8589
*,
86-
weights: Optional[NdGridDataType] = None,
90+
weights: Optional[SequenceUnivariateDataType] = None,
8791
axis: Optional[int] = None,
8892
normalizedsmooth: bool = False,
8993
) -> AutoSmoothingResult: # pragma: no cover
@@ -92,33 +96,32 @@ def csaps(
9296

9397
@overload
9498
def csaps(
95-
xdata: NdGridDataType,
99+
xdata: SequenceUnivariateDataType,
96100
ydata: MultivariateDataType,
97-
xidata: NdGridDataType,
101+
xidata: SequenceUnivariateDataType,
98102
*,
99103
smooth: Sequence[float],
100-
weights: Optional[NdGridDataType] = None,
104+
weights: Optional[SequenceUnivariateDataType] = None,
101105
axis: Optional[int] = None,
102106
normalizedsmooth: bool = False,
103107
) -> MultivariateDataType: # pragma: no cover
104108
...
105109

106110

107-
#
108-
# csaps signatures
109111
# **************************************
112+
# csaps implementation
110113

111114

112115
def csaps(
113-
xdata: Union[UnivariateDataType, NdGridDataType],
114-
ydata: MultivariateDataType,
115-
xidata: Optional[Union[UnivariateDataType, NdGridDataType]] = None,
116+
xdata,
117+
ydata,
118+
xidata=None,
116119
*,
117-
weights: Optional[Union[UnivariateDataType, NdGridDataType]] = None,
118-
smooth: Optional[Union[float, Sequence[float]]] = None,
119-
axis: Optional[int] = None,
120-
normalizedsmooth: bool = False,
121-
) -> Union[MultivariateDataType, ISmoothingSpline, AutoSmoothingResult]:
120+
weights=None,
121+
smooth=None,
122+
axis=None,
123+
normalizedsmooth=False,
124+
):
122125
"""Smooths the univariate/multivariate/gridded data or computes the corresponding splines
123126
124127
This function might be used as the main API for smoothing any data.
@@ -220,32 +223,27 @@ def csaps(
220223
221224
"""
222225

226+
umv = True
223227
if isinstance(xdata, c_abc.Sequence):
224-
try:
225-
ndgrid_prepare_data_vectors(xdata, 'xdata')
226-
except ValueError:
227-
umv = True
228-
else:
228+
if len(xdata) and isinstance(xdata[0], (np.ndarray, c_abc.Sequence)):
229229
umv = False
230-
else:
231-
umv = True
232230

233231
if umv:
234232
axis = -1 if axis is None else axis
235233
sp = CubicSmoothingSpline(
236-
xdata,
237-
ydata,
234+
xdata=xdata,
235+
ydata=ydata,
238236
weights=weights,
239237
smooth=smooth,
240238
axis=axis,
241239
normalizedsmooth=normalizedsmooth,
242240
)
243241
else:
244242
sp = NdGridCubicSmoothingSpline(
245-
xdata,
246-
ydata,
247-
weights,
248-
smooth,
243+
xdata=xdata,
244+
ydata=ydata,
245+
weights=weights,
246+
smooth=smooth,
249247
normalizedsmooth=normalizedsmooth,
250248
)
251249

csaps/_sspndg.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
ND-Gridded cubic smoothing spline implementation
33
"""
44

5-
from typing import Optional, Sequence, Tuple, Union
5+
from typing import Optional, Sequence, Tuple, Union, cast
66
import collections.abc as c_abc
7-
from numbers import Number
87

98
import numpy as np
109
from scipy.interpolate import NdPPoly, PPoly
@@ -18,24 +17,28 @@
1817
umv_coeffs_to_flatten,
1918
)
2019
from ._sspumv import CubicSmoothingSpline
21-
from ._types import NdGridDataType, UnivariateDataType
20+
from ._types import SequenceUnivariateDataType, FloatNDArrayType, Float1DArrayTupe
2221

2322

24-
def ndgrid_prepare_data_vectors(data, name, min_size: int = 2) -> Tuple[np.ndarray, ...]:
23+
def ndgrid_prepare_data_vectors(
24+
data: SequenceUnivariateDataType,
25+
name: str,
26+
min_size: int = 2,
27+
) -> Tuple[Float1DArrayTupe, ...]:
2528
if not isinstance(data, c_abc.Sequence):
2629
raise TypeError(f"'{name}' must be a sequence of 1-d array-like (vectors) or scalars.")
2730

28-
data = list(data)
31+
data_: list[Float1DArrayTupe] = []
2932

3033
for axis, d in enumerate(data):
3134
d = np.asarray(d, dtype=np.float64)
3235
if d.ndim > 1:
3336
raise ValueError(f"All '{name}' elements must be a vector for axis {axis}.")
3437
if d.size < min_size:
3538
raise ValueError(f"'{name}' must contain at least {min_size} data points for axis {axis}.")
36-
data[axis] = d
39+
data_.append(d)
3740

38-
return tuple(data)
41+
return tuple(data_)
3942

4043

4144
class NdGridSplinePPForm(ISplinePPForm[Tuple[np.ndarray, ...], Tuple[int, ...]], NdPPoly):
@@ -76,9 +79,9 @@ def ndim(self) -> int:
7679
def shape(self) -> Tuple[int, ...]:
7780
return tuple(len(xi) for xi in self.x)
7881

79-
def __call__(
82+
def __call__( # type: ignore[override]
8083
self,
81-
x: Sequence[UnivariateDataType],
84+
x: SequenceUnivariateDataType,
8285
nu: Optional[Tuple[int, ...]] = None,
8386
extrapolate: Optional[bool] = None,
8487
) -> np.ndarray:
@@ -87,8 +90,8 @@ def __call__(
8790
Parameters
8891
----------
8992
90-
x : tuple of 1-d array-like
91-
The tuple of point values for each dimension to evaluate the spline at.
93+
x : Sequence of 1-d array-like
94+
The sequence of point values for each dimension to evaluate the spline at.
9295
9396
nu : [*Optional*] tuple of int
9497
Orders of derivatives to evaluate. Each must be non-negative.
@@ -158,7 +161,7 @@ class NdGridCubicSmoothingSpline(
158161
ISmoothingSpline[
159162
NdGridSplinePPForm,
160163
Tuple[float, ...],
161-
NdGridDataType,
164+
SequenceUnivariateDataType,
162165
Tuple[int, ...],
163166
bool,
164167
]
@@ -204,31 +207,29 @@ class NdGridCubicSmoothingSpline(
204207

205208
def __init__(
206209
self,
207-
xdata: NdGridDataType,
210+
xdata: SequenceUnivariateDataType,
208211
ydata: np.ndarray,
209-
weights: Optional[Union[UnivariateDataType, NdGridDataType]] = None,
212+
weights: Optional[SequenceUnivariateDataType] = None,
210213
smooth: Optional[Union[float, Sequence[Optional[float]]]] = None,
211214
normalizedsmooth: bool = False,
212215
) -> None:
213216
x, y, w, s = self._prepare_data(xdata, ydata, weights, smooth)
214-
coeffs, smooth = self._make_spline(x, y, w, s, normalizedsmooth)
215-
216-
self._spline = NdGridSplinePPForm.construct_fast(coeffs, x)
217-
self._smooth = smooth
217+
coeffs, self._smooth = self._make_spline(x, y, w, s, normalizedsmooth)
218+
self._spline = cast(NdGridSplinePPForm, NdGridSplinePPForm.construct_fast(coeffs, x))
218219

219220
def __call__(
220221
self,
221-
x: Union[NdGridDataType, Sequence[Number]],
222+
x: SequenceUnivariateDataType,
222223
nu: Optional[Tuple[int, ...]] = None,
223224
extrapolate: Optional[bool] = None,
224-
) -> np.ndarray:
225+
) -> FloatNDArrayType:
225226
"""Evaluate the spline for given data
226227
227228
Parameters
228229
----------
229230
230-
x : tuple of 1-d array-like
231-
The tuple of point values for each dimension to evaluate the spline at.
231+
x : Sequence of 1-d array-like
232+
The sequence of point values for each dimension to evaluate the spline at.
232233
233234
nu : [*Optional*] tuple of int
234235
Orders of derivatives to evaluate. Each must be non-negative.

0 commit comments

Comments
 (0)