Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update pyproject.toml and .pre-commit-config.yaml for common SciTools standards #435

Merged
merged 17 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 0 additions & 38 deletions .flake8

This file was deleted.

105 changes: 87 additions & 18 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
# See https://pre-commit.ci/#configuration
# See https://github.com/scientific-python/cookie#sp-repo-review

ci:
autofix_prs: false
autoupdate_commit_msg: "chore: update pre-commit hooks"


# Alphabetised, for lack of a better order.
files: |
(?x)(
benchmarks\/.+\.py|
docs\/.+\.py|
esmf_regrid\/.+\.py|
noxfile\.py|
pyproject\.toml|
setup\.py|
src\/.+\.py
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't seen necessary since we don't have a src file. Although it doesn't seem like it is a problem since its not causing errors.

)
minimum_pre_commit_version: 1.21.0

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: 'v5.0.0'
Expand All @@ -14,22 +35,70 @@ repos:
- id: check-merge-conflict
# Check for debugger imports and py37+ `breakpoint()` calls in Python source.
- id: debug-statements
# Don't commit to master branch.
# Check TOML file syntax.
- id: check-toml
# Check YAML file syntax.
- id: check-yaml
# Makes sure files end in a newline and only a newline.
# Duplicates Ruff W292 but also works on non-Python files.
- id: end-of-file-fixer
# Replaces or checks mixed line ending.
- id: mixed-line-ending
# Don't commit to main branch.
- id: no-commit-to-branch
- repo: https://github.com/psf/black
rev: '24.10.0'
hooks:
- id: black
# Force black to run on whole repo, using settings from pyproject.toml
pass_filenames: false
args: [--config=./pyproject.toml, .]
- repo: https://github.com/PyCQA/flake8
rev: '7.1.1'
hooks:
# Run flake8.
- id: flake8
args: [--config=./.flake8]
additional_dependencies: [
'flake8-docstrings==1.7.0',
'flake8-import-order==0.18.2',
]
# Trims trailing whitespace.
# Duplicates Ruff W291 but also works on non-Python files.
- id: trailing-whitespace

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.4.4"
hooks:
- id: ruff
types: [file, python]
args: [--fix, --show-fixes]
- id: ruff-format
types: [file, python]

- repo: https://github.com/codespell-project/codespell
rev: "v2.2.6"
hooks:
- id: codespell
types_or: [asciidoc, python, markdown, rst]
additional_dependencies: [tomli]

- repo: https://github.com/adamchainz/blacken-docs
rev: 1.16.0
hooks:
- id: blacken-docs
types: [file, rst]

- repo: https://github.com/aio-libs/sort-all
rev: v1.2.0
hooks:
- id: sort-all
types: [file, python]

- repo: https://github.com/pre-commit/mirrors-mypy
rev: 'v1.10.0'
hooks:
- id: mypy
exclude: 'noxfile\.py|docs/src/conf\.py|^benchmarks'

- repo: https://github.com/abravalheri/validate-pyproject
# More exhaustive than Ruff RUF200.
rev: "v0.18"
hooks:
- id: validate-pyproject

- repo: https://github.com/scientific-python/cookie
rev: 2024.04.23
hooks:
- id: sp-repo-review
additional_dependencies: ["repo-review[cli]"]
args: ["--show=errskip"]

- repo: https://github.com/numpy/numpydoc
rev: v1.7.0
hooks:
- id: numpydoc-validation
types: [file, python]
6 changes: 2 additions & 4 deletions benchmarks/asv_delegated_conda.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@


class CondaDelegated(Conda):
"""
Manage a Conda environment using custom user scripts, run at each commit.
"""Manage a Conda environment using custom user scripts, run at each commit.

Ignores user input variations - ``matrix`` / ``pythons`` /
``conda_environment_file``, since environment is being managed outside ASV.
Expand All @@ -36,8 +35,7 @@ def __init__(
requirements: dict,
tagged_env_vars: dict,
) -> None:
"""
Create the instance.
"""Create the instance.

Parameters
----------
Expand Down
10 changes: 3 additions & 7 deletions benchmarks/benchmarks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@


def disable_repeat_between_setup(benchmark_object):
"""
Decorate benchmarks where object persistence would be inappropriate.
"""Decorate benchmarks where object persistence would be inappropriate.

E.g:
* Data is realised during testing.
Expand All @@ -30,8 +29,7 @@ def disable_repeat_between_setup(benchmark_object):


def skip_benchmark(benchmark_object):
"""
Decorate benchmarks to be skipped.
"""Decorate benchmarks to be skipped.

Simply doesn't return the object.

Expand All @@ -43,12 +41,10 @@ def skip_benchmark(benchmark_object):
(e.g. ``def time_something(self):`` ).

"""
pass


def on_demand_benchmark(benchmark_object):
"""
Decorate benchmark(s) that are disabled unless ON_DEMAND_BENCHARKS env var is set.
"""Decorate benchmark(s) that are disabled unless ON_DEMAND_BENCHARKS env var is set.

For benchmarks that, for whatever reason, should not be run by default.
E.g:
Expand Down
1 change: 1 addition & 0 deletions benchmarks/benchmarks/esmf_regridder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
MeshToGridESMFRegridder,
)
from esmf_regrid.schemes import ESMFAreaWeightedRegridder

from ..generate_data import _curvilinear_cube, _grid_cube, _gridlike_mesh_cube


Expand Down
9 changes: 5 additions & 4 deletions benchmarks/benchmarks/esmf_regridder/scalability.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
MeshToGridESMFRegridder,
)
from esmf_regrid.schemes import ESMFAreaWeightedRegridder

from .. import on_demand_benchmark, skip_benchmark
from ..generate_data import _grid_cube, _gridlike_mesh_cube

Expand Down Expand Up @@ -64,7 +65,7 @@ def time_prepare(self, n):
class PrepareScalabilityMeshToGrid(PrepareScalabilityMixin):
"""Benchmarks for the prepare step of :class:`~esmf_regrid.esmf_regrid.schemes.MeshToGridESMFRegridder`."""

regridder = MeshToGridESMFRegridder
regridder = MeshToGridESMFRegridder # type: ignore[assignment]

def src_cube(self, n):
"""Cube to regrid from."""
Expand Down Expand Up @@ -114,7 +115,7 @@ def time_prepare(self, _, n):
class PrepareScalabilityGridToMesh(PrepareScalabilityMixin):
"""Benchmarks for the prepare step of :class:`~esmf_regrid.esmf_regrid.schemes.GridToMeshESMFRegridder`."""

regridder = GridToMeshESMFRegridder
regridder = GridToMeshESMFRegridder # type: ignore[assignment]

def tgt_cube(self, n):
"""Cube containing the regridding target grid."""
Expand Down Expand Up @@ -257,7 +258,7 @@ def time_lazy_perform(self, cache, height):
class PerformScalabilityMeshToGrid(PerformScalabilityMixin):
"""Benchmarks for the perform step of :class:`~esmf_regrid.esmf_regrid.schemes.MeshToGridESMFRegridder`."""

regridder = MeshToGridESMFRegridder
regridder = MeshToGridESMFRegridder # type: ignore[assignment]
chunk_size = [PerformScalabilityMixin.grid_size ^ 2, 10]
file_name = "chunked_cube_1d.nc"

Expand Down Expand Up @@ -298,7 +299,7 @@ def time_lazy_perform(self, cache, height):
class PerformScalabilityGridToMesh(PerformScalabilityMixin):
"""Benchmarks for the perform step of :class:`~esmf_regrid.esmf_regrid.schemes.GridToMeshESMFRegridder`."""

regridder = GridToMeshESMFRegridder
regridder = GridToMeshESMFRegridder # type: ignore[assignment]

def setup_cache(self):
"""ASV setup_cache method."""
Expand Down
19 changes: 7 additions & 12 deletions benchmarks/benchmarks/generate_data.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""
Scripts for generating supporting data for benchmarking.
"""Scripts for generating supporting data for benchmarking.

Data generated using iris-esmf-regrid should use
:func:`run_function_elsewhere`, which means that data is generated using a
Expand Down Expand Up @@ -43,7 +42,7 @@
default_data_dir = (Path(__file__).parent.parent / ".data").resolve()
# Optionally override the default data location with environment variable.
BENCHMARK_DATA = Path(environ.get("BENCHMARK_DATA", default_data_dir))
if BENCHMARK_DATA == default_data_dir:
if default_data_dir == BENCHMARK_DATA:
BENCHMARK_DATA.mkdir(exist_ok=True)
message = (
f"No BENCHMARK_DATA env var, defaulting to {BENCHMARK_DATA}. "
Expand All @@ -62,8 +61,7 @@


def run_function_elsewhere(func_to_run, *args, **kwargs):
"""
Run a given function using the :const:`DATA_GEN_PYTHON` executable.
"""Run a given function using the :const:`DATA_GEN_PYTHON` executable.

This structure allows the function to be written natively.

Expand All @@ -90,7 +88,7 @@ def run_function_elsewhere(func_to_run, *args, **kwargs):
func_string = dedent(getsource(func_to_run))
func_string = func_string.replace("@staticmethod\n", "")
func_call_term_strings = [repr(arg) for arg in args]
func_call_term_strings += [f"{name}={repr(val)}" for name, val in kwargs.items()]
func_call_term_strings += [f"{name}={val!r}" for name, val in kwargs.items()]
func_call_string = (
f"{func_to_run.__name__}(" + ",".join(func_call_term_strings) + ")"
)
Expand All @@ -115,8 +113,7 @@ def _grid_cube(
"""Call _grid_cube via :func:`run_function_elsewhere`."""

def external(*args, **kwargs):
"""
Prep and call _grid_cube, saving to a NetCDF file.
"""Prep and call _grid_cube, saving to a NetCDF file.

Saving to a file allows the original python executable to pick back up.

Expand Down Expand Up @@ -178,8 +175,7 @@ def _curvilinear_cube(
"""Call _curvilinear_cube via :func:`run_function_elsewhere`."""

def external(*args, **kwargs):
"""
Prep and call _curvilinear_cube, saving to a NetCDF file.
"""Prep and call _curvilinear_cube, saving to a NetCDF file.

Saving to a file allows the original python executable to pick back up.

Expand Down Expand Up @@ -227,8 +223,7 @@ def _gridlike_mesh_cube(n_lons, n_lats):
"""Call _gridlike_mesh via :func:`run_function_elsewhere`."""

def external(*args, **kwargs):
"""
Prep and call _gridlike_mesh, saving to a NetCDF file.
"""Prep and call _gridlike_mesh, saving to a NetCDF file.

Saving to a file allows the original python executable to pick back up.

Expand Down
23 changes: 12 additions & 11 deletions benchmarks/bm_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import shlex
import subprocess
from tempfile import NamedTemporaryFile
from typing import Literal

# The threshold beyond which shifts are 'notable'. See `asv compare`` docs
# for more.
Expand All @@ -29,7 +28,7 @@
def _echo(echo_string: str):
# Use subprocess for printing to reduce chance of printing out of sequence
# with the subsequent calls.
subprocess.run(["echo", f"BM_RUNNER DEBUG: {echo_string}"])
subprocess.run(["echo", f"BM_RUNNER DEBUG: {echo_string}"], check=False)


def _subprocess_runner(args, asv=False, **kwargs):
Expand Down Expand Up @@ -103,10 +102,10 @@ def _setup_common() -> None:

def _asv_compare(*commits: str) -> None:
"""Run through a list of commits comparing each one to the next."""
commits = [commit[:8] for commit in commits]
for i in range(len(commits) - 1):
before = commits[i]
after = commits[i + 1]
_commits = [commit[:8] for commit in commits]
for i in range(len(_commits) - 1):
before = _commits[i]
after = _commits[i + 1]
asv_command = shlex.split(
f"compare {before} {after} --factor={COMPARE_FACTOR} --split"
)
Expand All @@ -124,7 +123,7 @@ class _SubParserGenerator(ABC):
description: str = NotImplemented
epilog: str = NotImplemented

def __init__(self, subparsers: ArgumentParser.add_subparsers) -> None:
def __init__(self, subparsers: argparse._SubParsersAction) -> None:
self.subparser: ArgumentParser = subparsers.add_parser(
self.name,
description=self.description,
Expand Down Expand Up @@ -249,18 +248,20 @@ def func(args: argparse.Namespace) -> None:
)

# Only do a single round.
asv_command = shlex.split(re.sub(r"rounds=\d", "rounds=1", asv_command))
_asv_command = shlex.split(re.sub(r"rounds=\d", "rounds=1", asv_command))
try:
_subprocess_runner([*asv_command, *args.asv_args], asv=True)
_subprocess_runner([*_asv_command, *args.asv_args], asv=True)
except subprocess.CalledProcessError as err:
# C/SPerf benchmarks are much bigger than the CI ones:
# Don't fail the whole run if memory blows on 1 benchmark.
# ASV produces return code of 2 if the run includes crashes.
if err.returncode != 2:
raise

asv_command = shlex.split(f"publish {commit_range} --html-dir={publish_subdir}")
_subprocess_runner(asv_command, asv=True)
_asv_command = shlex.split(
f"publish {commit_range} --html-dir={publish_subdir}"
)
_subprocess_runner(_asv_command, asv=True)

# Print completion message.
location = BENCHMARKS_DIR / ".asv"
Expand Down
Loading