Skip to content

Commit

Permalink
Merge pull request #1660 from OceanParcels/v/type-annotations
Browse files Browse the repository at this point in the history
Type annotations, mypy config, pydoclint setup, and other changes
  • Loading branch information
VeckoTheGecko authored Aug 29, 2024
2 parents 997e3f8 + 6fa0515 commit 85b6cde
Show file tree
Hide file tree
Showing 27 changed files with 399 additions and 204 deletions.
2 changes: 0 additions & 2 deletions .github/actions/install-parcels/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ runs:
environment-file: ${{ inputs.environment-file }}
python-version: ${{ inputs.python-version }}
channels: conda-forge
cache-environment: true
cache-downloads: true
- name: MPI support
if: ${{ ! (runner.os == 'Windows') }}
run: conda install -c conda-forge mpich mpi4py
Expand Down
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
28 changes: 26 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ on:
- "master"
- "test-me/*"
pull_request:
branches:
- "*"
schedule:
- cron: "0 7 * * 1" # Run every Monday at 7:00 UTC

concurrency:
group: branch-${{ github.head_ref }}
cancel-in-progress: true

defaults:
run:
shell: bash -el {0}
Expand Down Expand Up @@ -81,3 +83,25 @@ jobs:
with:
name: Integration test report
path: ${{ matrix.os }}_integration_test_report.html
typechecking:
name: mypy
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Conda and parcels
uses: ./.github/actions/install-parcels
with:
environment-file: environment.yml
- run: conda install lxml # dep for report generation
- name: Typechecking
run: |
mypy --install-types --non-interactive parcels --cobertura-xml-report mypy_report
- name: Upload mypy coverage to Codecov
uses: codecov/[email protected]
if: ${{ always() }} # Upload even on error of mypy
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: mypy_report/cobertura.xml
flags: mypy
fail_ci_if_error: false
17 changes: 15 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,27 @@ repos:
rev: v0.5.6
hooks:
- id: ruff
args: [ --fix ]
args: [--fix, --show-fixes]
- id: ruff
name: ruff (isort jupyter)
args: [--select, I, --fix]
types_or: [ jupyter ]
types_or: [jupyter]
- id: ruff-format
types_or: [ python, jupyter ]
- repo: https://github.com/biomejs/pre-commit
rev: v0.4.0
hooks:
- id: biome-format

# Ruff doesn't have full coverage of pydoclint https://github.com/astral-sh/ruff/issues/12434
- repo: https://github.com/PyCQA/flake8
rev: 7.0.0
hooks:
- id: flake8
name: pydoclint
files: "none"
# files: parcels/fieldset.py # put here instead of in config file due to https://github.com/pre-commit/pre-commit-hooks/issues/112#issuecomment-215613842
args:
- --select=DOC103 # TODO: Expand coverage to other codes
additional_dependencies:
- pydoclint[flake8]
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
## Parcels

[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/OceanParcels/parcels/master?labpath=docs%2Fexamples%2Fparcels_tutorial.ipynb)
[![unit-tests](https://github.com/OceanParcels/parcels/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/OceanParcels/parcels/actions/workflows/unit-tests.yml)
[![codecov](https://codecov.io/gh/OceanParcels/parcels/branch/master/graph/badge.svg)](https://codecov.io/gh/OceanParcels/parcels)
[![Anaconda-release](https://anaconda.org/conda-forge/parcels/badges/version.svg)](https://anaconda.org/conda-forge/parcels/)
[![Anaconda-date](https://anaconda.org/conda-forge/parcels/badges/latest_release_date.svg)](https://anaconda.org/conda-forge/parcels/)
[![Zenodo](https://zenodo.org/badge/DOI/10.5281/zenodo.823561.svg)](https://doi.org/10.5281/zenodo.823561)
[![Code style: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/format.json)](https://github.com/astral-sh/ruff)
[![unit-tests](https://github.com/OceanParcels/parcels/actions/workflows/ci.yml/badge.svg)](https://github.com/OceanParcels/parcels/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/OceanParcels/parcels/branch/master/graph/badge.svg)](https://codecov.io/gh/OceanParcels/parcels)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/5353/badge)](https://bestpractices.coreinfrastructure.org/projects/5353)
[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/OceanParcels/parcels/master?labpath=docs%2Fexamples%2Fparcels_tutorial.ipynb)

**Parcels** (**P**robably **A** **R**eally **C**omputationally **E**fficient **L**agrangian **S**imulator) is a set of Python classes and methods to create customisable particle tracking simulations using output from Ocean Circulation models. Parcels can be used to track passive and active particulates such as water, plankton, [plastic](http://www.topios.org/) and [fish](https://github.com/Jacketless/IKAMOANA).

Expand Down
1 change: 0 additions & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,5 @@ comment:
require_base: false
require_head: true
hide_project_coverage: true

# When modifying this file, please validate using
# curl -X POST --data-binary @codecov.yml https://codecov.io/validate
7 changes: 5 additions & 2 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ dependencies:
- pytest-html
- coverage

# Typing
- mypy
- types-tqdm
- types-psutil

# Linting
- flake8>=2.1.0
- pre_commit
- pydocstyle

# Docs
- ipython
Expand Down
19 changes: 19 additions & 0 deletions parcels/_compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Import helpers for compatability between installations."""

__all__ = ["MPI", "KMeans"]

from typing import Any

MPI: Any | None = None
KMeans: Any | None = None

try:
from mpi4py import MPI # type: ignore[no-redef]
except ModuleNotFoundError:
pass

# KMeans is used in MPI. sklearn not installed by default
try:
from sklearn.cluster import KMeans # type: ignore[no-redef]
except ModuleNotFoundError:
pass
45 changes: 45 additions & 0 deletions parcels/_typing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""
Typing support for Parcels.
This module contains type aliases used throughout Parcels as well as functions that are
used for runtime parameter validation (to ensure users are only using the right params).
"""

import ast
import datetime
import os
from typing import Callable, Literal


class ParcelsAST(ast.AST):
ccode: str


InterpMethodOption = Literal[
"linear",
"nearest",
"freeslip",
"partialslip",
"bgrid_velocity",
"bgrid_w_velocity",
"cgrid_velocity",
"linear_invdist_land_tracer",
"nearest",
"bgrid_tracer",
"cgrid_tracer",
] # corresponds with `tracer_interp_method`
InterpMethod = (
InterpMethodOption | dict[str, InterpMethodOption]
) # corresponds with `interp_method` (which can also be dict mapping field names to method)
PathLike = str | os.PathLike
Mesh = Literal["spherical", "flat"] # corresponds with `mesh`
VectorType = Literal["3D", "2D"] | None # corresponds with `vector_type`
ChunkMode = Literal["auto", "specific", "failsafe"] # corresponds with `chunk_mode`
GridIndexingType = Literal["pop", "mom5", "mitgcm", "nemo"] # corresponds with `grid_indexing_type`
UpdateStatus = Literal["not_updated", "first_updated", "updated"] # corresponds with `update_status`
TimePeriodic = float | datetime.timedelta | Literal[False] # corresponds with `update_status`
NetcdfEngine = Literal["netcdf4", "xarray", "scipy"]


KernelFunction = Callable[..., None]
5 changes: 1 addition & 4 deletions parcels/compilation/codecompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
import subprocess
from struct import calcsize

try:
from mpi4py import MPI
except ModuleNotFoundError:
MPI = None
from parcels._compat import MPI

_tmp_dir = os.getcwd()

Expand Down
6 changes: 3 additions & 3 deletions parcels/compilation/codegenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ class KernelGenerator(ABC, ast.NodeVisitor):

# Intrinsic variables that appear as function arguments
kernel_vars = ["particle", "fieldset", "time", "output_time", "tol"]
array_vars = []
array_vars: list[str] = []

def __init__(self, fieldset=None, ptype=JITParticle):
self.fieldset = fieldset
Expand All @@ -419,7 +419,7 @@ def __init__(self, fieldset=None, ptype=JITParticle):
self.vector_field_args = collections.OrderedDict()
self.const_args = collections.OrderedDict()

def generate(self, py_ast, funcvars):
def generate(self, py_ast, funcvars: list[str]):
# Replace occurrences of intrinsic objects in Python AST
transformer = IntrinsicTransformer(self.fieldset, self.ptype)
py_ast = transformer.visit(py_ast)
Expand All @@ -434,7 +434,7 @@ def generate(self, py_ast, funcvars):
# Insert variable declarations for non-intrinsic variables
# Make sure that repeated variables are not declared more than
# once. If variables occur in multiple Kernels, give a warning
used_vars = []
used_vars: list[str] = []
funcvars_copy = copy(funcvars) # editing a list while looping over it is dangerous
for kvar in funcvars:
if kvar in used_vars + ["particle_dlon", "particle_dlat", "particle_ddepth"]:
Expand Down
Loading

0 comments on commit 85b6cde

Please sign in to comment.