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

Support Python3.13 #2054

Merged
merged 17 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,12 @@ jobs:
markers: -m 'not slow'
python-version: "3.12"
tox-env: standard
# Coverage is not yet supported on Python3.13 . See `tox.ini`.
- os: 'ubuntu-latest'
testdata-cache: '~/.cache/xclim-testdata'
markers: -m 'not slow'
python-version: "3.13"
tox-env: standard
# Windows builds
- os: 'windows-latest'
testdata-cache: 'C:\Users\runneradmin\AppData\Local\xclim-testdata\xclim-testdata\Cache'
Expand Down Expand Up @@ -254,6 +260,7 @@ jobs:
ip-api.com:80
ipapi.co:443
motd.ubuntu.com:443
objects.githubusercontent.com:443
proxy.golang.org:443
pypi.org:443
raw.githubusercontent.com:443
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ v0.55.0 (unreleased)
--------------------
Contributors to this version: Juliette Lavoie (:user:`juliettelavoie`), Trevor James Smith (:user:`Zeitsperre`), Sascha Hofmann (:user:`saschahofmann`).

Announcements
^^^^^^^^^^^^^
* `xclim` now officially supports Python 3.13 (using `numba` v0.61.0). (:issue:`2022`, :pull:`2054`).
* `xclim` version 0.55.0 will be the last version to support Python 3.10. The next version will require Python 3.11 or higher. (:pull:`2054`).

New indicators
^^^^^^^^^^^^^^
* Added ``xclim.indices.holiday_snow_days`` to compute the number of days with snow on the ground during holidays ("Christmas Days"). (:issue:`2029`, :pull:`2030`).
Expand Down
3 changes: 2 additions & 1 deletion docs/autodoc_indicator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Sphinx extension that acts as a autodoc patch for documenting Indicator instances.
"""
Sphinx extension that acts as a autodoc patch for documenting Indicator instances.

By default, indicator instances are skipped by autodoc because their subclass is not a builtin type of python.

Expand Down
3 changes: 2 additions & 1 deletion docs/notebooks/_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@


def _find_current_folder():
"""Find the folder containing the notebooks.
"""
Find the folder containing the notebooks.

Needed in order to run the notebooks from the docs/notebooks folder.
"""
Expand Down
3 changes: 2 additions & 1 deletion docs/notebooks/example/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
def extreme_precip_accumulation_and_days(
pr: xr.DataArray, perc: float = 95, freq: str = "YS"
) -> tuple[xr.DataArray, xr.DataArray]:
"""Total precipitation accumulation during extreme events and number of days of such precipitation.
"""
Total precipitation accumulation during extreme events and number of days of such precipitation.

The `perc` percentile of the precipitation (including all values, not in a day-of-year manner)
is computed. Then, for each period, the days where `pr` is above the threshold are accumulated,
Expand Down
3 changes: 2 additions & 1 deletion docs/notebooks/extendxclim.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@
"def tx_days_compare(\n",
" tasmax: xr.DataArray, thresh: str = \"0 degC\", op: str = \">\", freq: str = \"YS\"\n",
"):\n",
" r\"\"\"Number of days where maximum daily temperature is above or under a threshold.\n",
" r\"\"\"\n",
" Number of days where maximum daily temperature is above or under a threshold.\n",
"\n",
" The daily maximum temperature is compared to a threshold using a given operator and the number\n",
" of days where the condition is true is returned.\n",
Expand Down
3 changes: 2 additions & 1 deletion docs/notebooks/partitioning.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@
"\n",
"\n",
"def graph_fraction_of_total_variance(variance, ref=\"2000\", ax=None):\n",
" \"\"\"Figure from Hawkins and Sutton (2009) showing fraction of total variance.\n",
" \"\"\"\n",
" Figure from Hawkins and Sutton (2009) showing fraction of total variance.\n",
"\n",
" Parameters\n",
" ----------\n",
Expand Down
6 changes: 2 additions & 4 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ dependencies:
- notebook
- numpydoc >=1.8.0
- pandas-stubs >=2.2
- pip >=24.2.0
- pooch >=1.8.0
- pre-commit >=3.7
- pybtex >=0.24.0
- pygments <2.19 # FIXME: temporary fix for sphinx-codeautolink
- pylint >=3.3.1
- pytest >=8.0.0
- pytest-cov >=5.0.0
Expand All @@ -70,7 +70,7 @@ dependencies:
- sphinx >=7.0.0
- sphinx-autobuild >=2024.4.16
- sphinx-autodoc-typehints
- sphinx-codeautolink >=0.15.2,!=0.16.0 # FIXME: temporary fix for sphinx-codeautolink
- sphinx-codeautolink >=0.16.2
- sphinx-copybutton
- sphinx-mdinclude
- sphinxcontrib-bibtex
Expand All @@ -81,5 +81,3 @@ dependencies:
- vulture =2.14
- xdoctest >=1.1.5
- yamllint >=1.35.1
- pip >=24.2.0
- pygments <2.19 #FIXME: temporary fix, https://github.com/felix-hilden/sphinx-codeautolink/issues/153
11 changes: 5 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
# "Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Scientific/Engineering :: Atmospheric Science",
"Topic :: Scientific/Engineering :: Hydrology",
Expand Down Expand Up @@ -64,7 +64,6 @@ dev = [
"bump-my-version ==0.31.1",
"codespell ==2.4.1",
"coverage[toml] >=7.5.0",
"coveralls >=4.0.1", # coveralls is not yet compatible with Python 3.13
"deptry ==0.23.0",
"flake8 >=7.1.1",
"flake8-rst-docstrings >=0.3.0",
Expand Down Expand Up @@ -104,16 +103,14 @@ docs = [
"nc-time-axis >=1.4.1",
"pooch >=1.8.0",
"pybtex >=0.24.0",
"pygments <2.19", # FIXME: temporary fix for sphinx-codeautolink
"sphinx >=7.0.0",
"sphinx-autobuild >=2024.4.16",
"sphinx-autodoc-typehints",
"sphinx-codeautolink >=0.15.2,!=0.16.0", # FIXME: temporary fix for sphinx-codeautolink
"sphinx-codeautolink >=0.16.2",
"sphinx-copybutton",
"sphinx-mdinclude",
"sphinxcontrib-bibtex",
"sphinxcontrib-svg2pdfconverter[Cairosvg]",
"pygments <2.19" # FIXME: temporary fix, https://github.com/felix-hilden/sphinx-codeautolink/issues/153
"sphinxcontrib-svg2pdfconverter[Cairosvg]"
]
extras = ["fastnanquantile >=0.0.2", "flox >=0.9", "POT >=0.9.4"]
all = ["xclim[dev]", "xclim[docs]", "xclim[extras]"]
Expand Down Expand Up @@ -329,11 +326,13 @@ exclude = [
"docs/notebooks/xclim_training/*.ipynb"
]
extend-select = [
"D213", # multi-line-summary-second-line
"RUF022" # unsorted-dunder-all
]
ignore = [
"B028", # no-explicit-stacklevel
"D205", # blank-line-after-summary
"D212", # multi-line-summary-first-line
"D400", # ends-in-period
"D401" # non-imperative-mood
]
Expand Down
9 changes: 6 additions & 3 deletions src/xclim/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def _get_indicator(indicator_name):


def _get_input(ctx):
"""Return the input dataset stored in the given context.
"""
Return the input dataset stored in the given context.

If the dataset is not open, opens it with open_dataset if a single path was given,
or with `open_mfdataset` if a tuple or glob path was given.
Expand All @@ -71,7 +72,8 @@ def _get_input(ctx):


def _get_output(ctx):
"""Return the output dataset stored in the given context.
"""
Return the output dataset stored in the given context.

If the output dataset doesn't exist, create it.
"""
Expand All @@ -86,7 +88,8 @@ def _get_output(ctx):


def _process_indicator(indicator, ctx, **params):
"""Add given climate indicator to the output dataset from variables in the input dataset.
"""
Add given climate indicator to the output dataset from variables in the input dataset.

Computation is not triggered here if dask is enabled.
"""
Expand Down
14 changes: 9 additions & 5 deletions src/xclim/core/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import itertools
import re
import string
import textwrap
import warnings
from ast import literal_eval
from collections.abc import Callable, Sequence
Expand Down Expand Up @@ -257,9 +258,11 @@ def parse_doc(doc: str) -> dict:
if doc is None:
return {}

# Retrocompatilbity as python 3.13 does this by default
doc = textwrap.dedent(doc)
out = {}

sections = re.split(r"(\w+\s?\w+)\n\s+-{3,50}", doc) # obj.__doc__.split('\n\n')
sections = re.split(r"(\w+\s?\w+)\n-{3,50}", doc) # obj.__doc__.split('\n\n')
intro = sections.pop(0)
if intro:
intro_content = list(map(str.strip, intro.strip().split("\n\n")))
Expand Down Expand Up @@ -295,11 +298,12 @@ def _parse_parameters(section):
"""
curr_key = None
params = {}

for line in section.split("\n"):
if line.startswith(" " * 6): # description
if line.startswith(" "): # description
s = " " if params[curr_key]["description"] else ""
params[curr_key]["description"] += s + line.strip()
elif line.startswith(" " * 4) and ":" in line: # param title
elif not line.startswith(" ") and ":" in line: # param title
name, annot = line.split(":", maxsplit=1)
curr_key = name.strip()
params[curr_key] = {"description": ""}
Expand All @@ -320,10 +324,10 @@ def _parse_returns(section):
params = {}
for line in section.split("\n"):
if line.strip():
if line.startswith(" " * 6): # long_name
if line.startswith(" "): # long_name
s = " " if params[curr_key]["long_name"] else ""
params[curr_key]["long_name"] += s + line.strip()
elif line.startswith(" " * 4): # param title
elif not line.startswith(" "): # param title
annot, *name = reversed(line.split(":", maxsplit=1))
if name:
curr_key = name[0].strip()
Expand Down
15 changes: 10 additions & 5 deletions src/xclim/core/indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,8 @@ def __new__(cls, **kwds): # noqa: C901

@staticmethod
def _parse_indice(compute, passed_parameters): # noqa: F841
"""Parse the compute function.
"""
Parse the compute function.

- Metadata is extracted from the docstring
- Parameters are parsed from the docstring (description, choices), decorator (units), signature (kind, default)
Expand Down Expand Up @@ -578,7 +579,8 @@ def _parse_indice(compute, passed_parameters): # noqa: F841

@classmethod
def _added_parameters(cls):
"""Create a list of tuples for arguments to add to the call signature (name, Parameter).
"""
Create a list of tuples for arguments to add to the call signature (name, Parameter).

These can't be in the compute function signature, the class is in charge of removing them
from the params passed to the compute function, likely through an override of
Expand Down Expand Up @@ -1030,7 +1032,8 @@ def _postprocess(self, outs, das, params):
return outs

def _bind_call(self, func, **das):
"""Call function using `__call__` `DataArray` arguments.
"""
Call function using `__call__` `DataArray` arguments.

This will try to bind keyword arguments to `func` arguments. If this fails,
`func` is called with positional arguments only.
Expand Down Expand Up @@ -1063,7 +1066,8 @@ def _bind_call(self, func, **das):
def _get_translated_metadata(
cls, locale, var_id=None, names=None, append_locale_name=True
):
"""Get raw translated metadata for the current indicator and a given locale.
"""
Get raw translated metadata for the current indicator and a given locale.

All available translated metadata from the current indicator and those it is
based on are merged, with the highest priority set to the current one.
Expand Down Expand Up @@ -1094,7 +1098,8 @@ def _update_attrs(
var_id: str | None = None,
names: Sequence[str] | None = None,
):
"""Format attributes with the run-time values of `compute` call parameters.
"""
Format attributes with the run-time values of `compute` call parameters.

Cell methods and history attributes are updated, adding to existing values.
The language of the string is taken from the `OPTIONS` configuration dictionary.
Expand Down
Loading