diff --git a/.github/workflows/cd-pypi.yml b/.github/workflows/cd-pypi.yml index 5bc789d..44b14e9 100644 --- a/.github/workflows/cd-pypi.yml +++ b/.github/workflows/cd-pypi.yml @@ -1,10 +1,8 @@ name: cd - on: push: tags: - '**' - jobs: pypi: uses: ecmwf/reusable-workflows/.github/workflows/cd-pypi.yml@v2 diff --git a/.github/workflows/cd-test-pypi.yml b/.github/workflows/cd-test-pypi.yml index 9e96bec..1341f53 100644 --- a/.github/workflows/cd-test-pypi.yml +++ b/.github/workflows/cd-test-pypi.yml @@ -1,9 +1,7 @@ name: test-cd - on: pull_request: branches: ["main"] - jobs: pypi: uses: ecmwf/reusable-workflows/.github/workflows/cd-pypi.yml@v2 diff --git a/.github/workflows/label-public-pr.yml b/.github/workflows/label-public-pr.yml index dbcc936..e9a26b0 100644 --- a/.github/workflows/label-public-pr.yml +++ b/.github/workflows/label-public-pr.yml @@ -1,10 +1,8 @@ # Manage labels of pull requests that originate from forks name: label-public-pr - on: pull_request_target: types: [opened, synchronize] - jobs: label: uses: ecmwf/reusable-workflows/.github/workflows/label-pr.yml@v2 diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index b9b10d7..2072a10 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -1,18 +1,15 @@ name: Quality assurance - on: push: branches: ["main", "develop"] pull_request: branches: ["main", "develop"] types: [opened, synchronize, reopened] - jobs: pre-commit: uses: ecmwf/reusable-workflows/.github/workflows/qa-precommit-run.yml@v2 with: skip-hooks: "no-commit-to-branch" - tests: strategy: matrix: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 191a013..9961014 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,10 +19,14 @@ repos: - id: no-commit-to-branch args: [--branch, main] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.2 + rev: v0.15.4 hooks: - id: ruff-check - args: [--fix, --show-fixes] + exclude: '(dev/.*|.*_)\.py$' + args: + - --line-length=120 + - --fix + - --exit-non-zero-on-fix - id: ruff-format - repo: https://github.com/executablebooks/mdformat rev: 1.0.0 diff --git a/.readthedocs.yaml b/.readthedocs.yaml index ffcd772..95d66de 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -3,18 +3,15 @@ # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details version: 2 - build: os: ubuntu-24.04 tools: python: "3.12" - python: install: - method: pip path: . extra_requirements: - docs - sphinx: configuration: docs/conf.py diff --git a/docs/notebooks/climate_indices_analysis.ipynb b/docs/notebooks/climate_indices_analysis.ipynb index 4779524..97d33e6 100644 --- a/docs/notebooks/climate_indices_analysis.ipynb +++ b/docs/notebooks/climate_indices_analysis.ipynb @@ -41,10 +41,7 @@ "import earthkit.plots as ekp\n", "import matplotlib.pyplot as plt\n", "\n", - "from earthkit.climate.indicators.precipitation import (\n", - " daily_precipitation_intensity,\n", - " maximum_consecutive_wet_days,\n", - ")\n", + "from earthkit.climate.indicators.precipitation import daily_precipitation_intensity, maximum_consecutive_wet_days\n", "from earthkit.climate.indicators.temperature import (\n", " daily_temperature_range,\n", " heating_degree_days,\n", diff --git a/docs/notebooks/performance_analysis.ipynb b/docs/notebooks/performance_analysis.ipynb index 961a335..bfc8dba 100644 --- a/docs/notebooks/performance_analysis.ipynb +++ b/docs/notebooks/performance_analysis.ipynb @@ -556,9 +556,7 @@ "import xarray as xr\n", "\n", "\n", - "def run_climate_indicator(\n", - " func: Callable[..., Any], kwargs: Dict[str, Any], use_flox: Optional[bool] = None\n", - ") -> Any:\n", + "def run_climate_indicator(func: Callable[..., Any], kwargs: Dict[str, Any], use_flox: Optional[bool] = None) -> Any:\n", " \"\"\"\n", " Unified execution wrapper for climate indicator functions with configurable backend options.\n", "\n", @@ -1493,9 +1491,7 @@ " # Let's double check the user intent. Usually Xclim NoFlox is the \"base\" reference.\n", " # The original code searched for Library='Xclim'. We stick to that.\n", " baseline_row = df_res.loc[\n", - " (df_res[\"Indicator\"] == indicator)\n", - " & (df_res[\"Library\"] == \"Xclim\")\n", - " & (df_res[\"Mode\"] == baseline_mode)\n", + " (df_res[\"Indicator\"] == indicator) & (df_res[\"Library\"] == \"Xclim\") & (df_res[\"Mode\"] == baseline_mode)\n", " ]\n", "\n", " if not baseline_row.empty:\n", diff --git a/pyproject.toml b/pyproject.toml index 92c2082..757cb3b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] build-backend = "setuptools.build_meta" -requires = ["setuptools>=65", "wheel", "setuptools-scm>=8"] +requires = ["setuptools>=65", "setuptools-scm>=8", "wheel"] [project] authors = [ @@ -17,40 +17,36 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.14", "Topic :: Scientific/Engineering" ] dependencies = [ - "earthkit-data>=0.17.0", + "earthkit-data>=0.17", "numpy>=1.22", "xarray>=2023.1", "xclim>=0.59.1", "xsdba>=0.5,<0.6" ] description = "Tools for calculating climate indicies, climate change analysis and attribution." -dynamic = ["version", "readme"] +dynamic = ["readme", "version"] license = {file = "LICENSE"} name = "earthkit-climate" requires-python = ">=3.10" - -[project.optional-dependencies] -dev = [ - "black", - "ruff", +optional-dependencies.dev = [ + "ipykernel", + "ipython", "mypy", "pre-commit", - "ipython", "pytest", "pytest-cov", "pytest-mock", - "ipykernel" + "ruff" ] -docs = [ +optional-dependencies.docs = [ + "furo", "nbsphinx", - "roman-numerals-py>=3.1.0,<4", + "roman-numerals-py>=3.1,<4", "sphinx", - "sphinx-autoapi", - "furo" + "sphinx-autoapi" ] [tool.coverage.run] @@ -60,10 +56,9 @@ branch = true addopts = "-vv --cov=. --cov-report=html --doctest-glob='*.md' --doctest-glob='*.rst'" [tool.ruff] -line-length = 110 - -[tool.ruff.lint] -ignore = [ +line-length = 120 +preview = true +lint.ignore = [ "D1", # pydocstyle: Missing Docstrings "D107", # pydocstyle: numpy convention "D203", @@ -77,16 +72,14 @@ ignore = [ "D416", "D417" ] -select = [ - "F", # pyflakes +lint.select = [ + "D", # pydocstyle "E", # pycodestyle - "W", # pycodestyle warnings + "F", # pyflakes "I", # isort - "D" # pydocstyle + "W" # pycodestyle warnings ] - -[tool.ruff.lint.per-file-ignores] -"docs/conf.py" = [ +lint.per-file-ignores."docs/conf.py" = [ "E501" # line too long ] diff --git a/src/earthkit/climate/utils/percentile.py b/src/earthkit/climate/utils/percentile.py index 30856bc..7ccf9ce 100644 --- a/src/earthkit/climate/utils/percentile.py +++ b/src/earthkit/climate/utils/percentile.py @@ -75,9 +75,7 @@ def custom_percentile(group: DataArray) -> DataArray: expanded = ref_da.groupby(f"time.{time_component}").map( lambda group: xr.full_like( group, - ds_percentile[varname].sel( - {time_component: getattr(group.time.dt, time_component)[0].item()} - ), + ds_percentile[varname].sel({time_component: getattr(group.time.dt, time_component)[0].item()}), float, ) ) diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 4bf5a28..323b6cd 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -16,9 +16,7 @@ @pytest.fixture def dummy_precip_ds() -> xr.Dataset: """Simple constant precipitation dataset.""" - time = xr.cftime_range( - start="2001-01-01", end="2001-01-10", freq="D", calendar="noleap" - ).to_datetimeindex() + time = xr.cftime_range(start="2001-01-01", end="2001-01-10", freq="D", calendar="noleap").to_datetimeindex() ds = xr.Dataset( {"pr": ("time", [1.0] * len(time))}, coords={"time": time}, @@ -48,9 +46,7 @@ def dummy_temp_ds() -> xr.Dataset: def daily_temperature_ds() -> xr.Dataset: """Synthetic daily temperature dataset for percentile and grouping tests.""" rng = np.random.default_rng(0) - time = xr.cftime_range( - start="2000-01-01", end="2001-12-31", freq="D", calendar="noleap" - ).to_datetimeindex() + time = xr.cftime_range(start="2000-01-01", end="2001-12-31", freq="D", calendar="noleap").to_datetimeindex() data = rng.normal(loc=10.0, scale=2.0, size=time.size) ds = xr.Dataset({"tas": ("time", data)}, coords={"time": time}) return ds diff --git a/tests/unit/utils/test_conversions.py b/tests/unit/utils/test_conversions.py index b1780b0..e97283b 100644 --- a/tests/unit/utils/test_conversions.py +++ b/tests/unit/utils/test_conversions.py @@ -12,10 +12,7 @@ import xarray as xr from earthkit.data.wrappers.xarray import XArrayDatasetWrapper -from earthkit.climate.utils.conversions import ( - to_earthkit_field, - to_xarray_dataset, -) +from earthkit.climate.utils.conversions import to_earthkit_field, to_xarray_dataset @pytest.mark.parametrize( diff --git a/tools/xclim_wrappers_generator.py b/tools/xclim_wrappers_generator.py index ab0aeb7..e195184 100644 --- a/tools/xclim_wrappers_generator.py +++ b/tools/xclim_wrappers_generator.py @@ -181,18 +181,12 @@ def generate_module_content(category: str, indicators: List[Any]) -> str: # Indent the docstring correctly lines = docstring.split("\n") - indented_doc = ( - lines[0] + "\n" + "\n".join([(" " + line if line.strip() else "") for line in lines[1:]]) - ) + indented_doc = lines[0] + "\n" + "\n".join([(" " + line if line.strip() else "") for line in lines[1:]]) - code = FUNCTION_TEMPLATE.format( - func_name=func_name, xclim_func_name=xclim_func_name, docstring=indented_doc - ) + code = FUNCTION_TEMPLATE.format(func_name=func_name, xclim_func_name=xclim_func_name, docstring=indented_doc) functions_code.append(code) - return MODULE_TEMPLATE.format( - category_title=category.capitalize(), functions_code="".join(functions_code) - ) + return MODULE_TEMPLATE.format(category_title=category.capitalize(), functions_code="".join(functions_code)) def main():