Skip to content

Commit 3a40fa4

Browse files
Migrate back to mypy and enable typechecking in CI (#2595)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 79343ff commit 3a40fa4

9 files changed

Lines changed: 83 additions & 24 deletions

File tree

.github/workflows/ci.yml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
with:
3131
fetch-depth: 2
3232
persist-credentials: false
33-
- uses: xarray-contrib/ci-trigger@74ddc46fc6ca7509549ac7b660d7a185948c1e74 # v1
33+
- uses: xarray-contrib/ci-trigger@74ddc46fc6ca7509549ac7b660d7a185948c1e74 # v1.2
3434
id: check-skip
3535
with:
3636
keyword: "[skip-ci]"
@@ -54,7 +54,7 @@ jobs:
5454
persist-credentials: false
5555
- uses: Parcels-code/pixi-lock/create-and-cache@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
5656
id: pixi-lock
57-
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
57+
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
5858
with:
5959
name: pixi-lock
6060
path: pixi.lock
@@ -122,7 +122,7 @@ jobs:
122122
flags: unit-tests
123123
- name: Upload test results
124124
if: ${{ always() }} # Always run this step, even if tests fail
125-
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
125+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
126126
with:
127127
name: Unittest report ${{ matrix.os }}-${{ matrix.pixi-environment }}
128128
path: ${{ env.COVERAGE_REPORT }}
@@ -169,7 +169,7 @@ jobs:
169169
flags: integration-tests
170170
- name: Upload test results
171171
if: ${{ always() }} # Always run this step, even if tests fail
172-
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
172+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
173173
with:
174174
name: Integration test report ${{ matrix.os }}-${{ matrix.pixi-environment }}
175175
path: ${{ env.COVERAGE_REPORT }}
@@ -182,7 +182,7 @@ jobs:
182182
- typechecking
183183
steps:
184184
- name: Merge Artifacts
185-
uses: actions/upload-artifact/merge@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
185+
uses: actions/upload-artifact/merge@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
186186
with:
187187
name: Testing reports
188188
pattern: "* report *"
@@ -208,8 +208,14 @@ jobs:
208208
cache: true
209209
cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
210210
- name: Typechecking
211-
run: | # TODO: Remove `|| true` once typechecking is stable
212-
pixi run typing --output-format github || true
211+
run: |
212+
pixi run typing --non-interactive --html-report mypy-report
213+
- name: Upload test results
214+
if: ${{ always() }} # Upload even on mypy error
215+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
216+
with:
217+
name: Mypy report
218+
path: mypy-report
213219
build-and-upload-nightly-parcels: # for alpha testing
214220
needs: [cache-pixi-lock]
215221
permissions:

.github/workflows/pypi-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
else
4848
echo "✅ Looks good"
4949
fi
50-
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
50+
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
5151
with:
5252
name: releases
5353
path: dist

docs/development/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ See below for more Pixi commands relevant to development.
132132

133133
**Code quality**
134134

135-
- `pixi run lint` - Run [pre-commit](https://pre-commit.com/) hooks on all files (includes formatting, linting, and other code quality checks)
136-
- `pixi run typing` - Run [ty](https://docs.astral.sh/ty/) type checking on the codebase
135+
- `pixi run lint` - Run pre-commit hooks on all files (includes formatting, linting, and other code quality checks)
136+
- `pixi run typing` - Run mypy type checking on the codebase
137137

138138
**Different environments**
139139

pixi.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,12 @@ numpydoc = "*"
141141
numpydoc-lint = { cmd = "python tools/numpydoc-public-api.py", description = "Lint public API docstrings with numpydoc." }
142142

143143
[feature.typing.dependencies]
144-
ty = "*"
144+
mypy = "*"
145+
lxml = "*" # in CI
146+
types-tqdm = "*"
145147

146148
[feature.typing.tasks]
147-
typing = { cmd = "ty check", description = "Run static type checking with ty." }
149+
typing = { cmd = "mypy src/parcels --install-types", description = "Run static type checking with mypy." }
148150

149151

150152
[environments]

pyproject.toml

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,53 @@ convention = "numpy"
159159
[tool.ruff.lint.isort]
160160
known-first-party = ["parcels"]
161161

162-
[tool.ty.src]
163-
include = ["./src/"]
162+
[tool.mypy]
163+
files = ['src']
164+
check_untyped_defs = true
165+
show_error_context = true
166+
warn_redundant_casts = true
167+
warn_unused_configs = true
168+
warn_unused_ignores = true
169+
164170
exclude = [
165-
"./src/parcels/interpolators/", # ignore for now
166-
"./src/parcels/kernels/_advection.py",
171+
# Temporarily exclude files. As we improve typechecking across the codebase, remove these
172+
"src/parcels/_core/utils/time.py",
173+
"src/parcels/_core/utils/interpolation.py",
174+
"src/parcels/_core/utils/unstructured.py",
175+
"src/parcels/_core/particle.py",
176+
"src/parcels/_core/field.py",
177+
"src/parcels/_core/kernel.py",
178+
"src/parcels/_core/particleset.py",
179+
"src/parcels/_core/xgrid.py",
180+
"src/parcels/_core/warnings.py",
181+
"src/parcels/_core/constants.py",
182+
"src/parcels/_core/utils/sgrid.py",
183+
"src/parcels/_core/utils/string.py",
184+
"src/parcels/_core/particlefile.py",
185+
"src/parcels/_core/uxgrid.py",
186+
"src/parcels/_core/spatialhash.py",
187+
"src/parcels/_core/statuscodes.py",
188+
"src/parcels/_core/particlesetview.py",
189+
"src/parcels/_core/fieldset.py",
190+
"src/parcels/_core/index_search.py",
191+
"src/parcels/interpolators/_xinterpolators.py",
192+
"src/parcels/interpolators/__init__.py",
193+
"src/parcels/interpolators/_uxinterpolators.py",
194+
"src/parcels/kernels/_sigmagrids.py",
195+
"src/parcels/kernels/_advectiondiffusion.py",
196+
"src/parcels/kernels/__init__.py",
197+
"src/parcels/kernels/_advection.py",
198+
]
199+
200+
[[tool.mypy.overrides]]
201+
module = [
202+
"scipy.spatial",
203+
"zarr",
204+
"zarr.storage",
205+
"cftime",
206+
"netCDF4",
207+
"pooch",
208+
"xgcm",
209+
"uxarray",
167210
]
211+
ignore_missing_imports = true

src/parcels/_core/basegrid.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class GridType(IntEnum):
2424
class BaseGrid(ABC):
2525
"""Base class for parcels.XGrid and parcels.UxGrid defining common methods and properties"""
2626

27+
_spatialhash: SpatialHash | None
28+
2729
@abstractmethod
2830
def search(self, z: float, y: float, x: float, ei=None) -> dict[str, tuple[int, float | np.ndarray]]:
2931
"""

src/parcels/_core/utils/sgrid.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ def _grid3d_to_ascii(grid: Grid3DMetadata) -> str:
694694
return "\n".join(lines)
695695

696696

697-
def _attach_sgrid_metadata(ds, grid: Grid2DMetadata | Grid3DMetadata):
697+
def _attach_sgrid_metadata(ds: xr.Dataset, grid: Grid2DMetadata | Grid3DMetadata):
698698
"""Copies the dataset and attaches the SGRID metadata in 'grid' variable. Modifies 'conventions' attribute."""
699699
ds = ds.copy()
700700
ds["grid"] = (

src/parcels/_datasets/remote.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,15 @@
1818

1919
_DATA_URL = f"https://github.com/Parcels-code/parcels-data/raw/{_DATA_REPO_TAG}"
2020

21-
_DATA_HOME = os.environ.get("PARCELS_EXAMPLE_DATA")
22-
if _DATA_HOME is None:
23-
_DATA_HOME = pooch.os_cache("parcels")
24-
_DATA_HOME = Path(_DATA_HOME)
21+
22+
def _get_data_home() -> Path:
23+
data_home = os.environ.get("PARCELS_EXAMPLE_DATA")
24+
if data_home is None:
25+
data_home = pooch.os_cache("parcels")
26+
return Path(data_home)
27+
28+
29+
_DATA_HOME = _get_data_home()
2530

2631
# See instructions at https://github.com/Parcels-code/parcels-data for adding new datasets
2732
_ODIE_REGISTRY_FILES: list[str] = (
@@ -193,7 +198,7 @@ class _Purpose(enum.Enum):
193198

194199
# The first here is a human readable key used to open datasets, with an object to open the datasets
195200
# fmt: off
196-
_DATASET_KEYS_AND_CONFIGS: dict[str, tuple[_V3Dataset, _Purpose]] = dict([
201+
_DATASET_KEYS_AND_CONFIGS: dict[str, tuple[_ParcelsDataset, _Purpose]] = dict([
197202
("MovingEddies_data/P", (_V3Dataset(_ODIE,"data/MovingEddies_data/moving_eddiesP.nc"), _Purpose.TUTORIAL)),
198203
("MovingEddies_data/U", (_V3Dataset(_ODIE,"data/MovingEddies_data/moving_eddiesU.nc"), _Purpose.TUTORIAL)),
199204
("MovingEddies_data/V", (_V3Dataset(_ODIE,"data/MovingEddies_data/moving_eddiesV.nc"), _Purpose.TUTORIAL)),

src/parcels/_datasets/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,10 +231,10 @@ def replace_arrays_with_zeros(
231231
return ds
232232

233233

234-
def _fill_with_dummy_data(d: dict[str, dict]):
234+
def _fill_with_dummy_data(d: dict[str, Any]):
235235
assert isinstance(d, dict)
236236
if "dtype" in d:
237-
d["data"] = np.zeros(d["shape"], dtype=d["dtype"]) # type:ignore[no-matching-overload] # loading from unsanitized data
237+
d["data"] = np.zeros(d["shape"], dtype=d["dtype"])
238238
del d["dtype"]
239239
del d["shape"]
240240

0 commit comments

Comments
 (0)