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

Confirm support for Python 3.13 and drop 3.8 #143

Merged
merged 7 commits into from
Nov 8, 2024
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
13 changes: 7 additions & 6 deletions .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,19 @@ jobs:
# release. Only test SparseDataArray where possible.
# Earliest version supported by genno = earliest Python that has not
# reached EOL
- {version: "3.9", extras: ",sparse"}
- {version: "3.10", extras: ",sparse"}
- {version: "3.11", extras: ",sparse"}
- {version: "3.9", extras: ".[sparse]"}
- {version: "3.10", extras: ".[sparse]"}
- {version: "3.11", extras: ".[sparse]"}
- {version: "3.12", extras: ".[sparse]"}
# Latest release / latest supported by genno / testable on GHA
- {version: "3.12", extras: ",sparse"}
- {version: "3.13", extras: '"Pint > 0.24"'}

# For fresh releases and development versions of Python, compiled binary
# wheels are not available for some dependencies, e.g. numpy, pandas.
# Compiling these on the job runner requires a more elaborate build
# environment, currently out of scope for genno. Exclude these versions
# from CI.
# - {version: "3.13.0-rc.1", extras: ""} # Development version
# - {version: "3.14.0-beta.1", extras: ""} # Development version

fail-fast: false

Expand All @@ -61,7 +62,7 @@ jobs:
macos-skip-brew-update: true

- name: Install the Python package and dependencies
run: pip install --upgrade --upgrade-strategy=eager .[tests${{ matrix.python.extras }}]
run: pip install --upgrade --upgrade-strategy=eager .[tests] ${{ matrix.python.extras }}

- name: Run test suite using pytest
run: |
Expand Down
2 changes: 2 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"dask$": ":std:doc:`dask:index`",
"pint$": ":std:doc:`pint <pint:index>`",
"plotnine$": ":class:`plotnine.ggplot`",
"pyarrow$": ":std:doc:`pyarrow <pyarrow:python/index>`",
"pyam$": ":std:doc:`pyam:index`",
"sphinx$": ":std:doc:`sphinx <sphinx:index>`",
}
Expand Down Expand Up @@ -108,6 +109,7 @@
"platformdirs": ("https://platformdirs.readthedocs.io/en/latest", None),
"plotnine": ("https://plotnine.org", None),
"pyam": ("https://pyam-iamc.readthedocs.io/en/stable", None),
"pyarrow": ("https://arrow.apache.org/docs", None),
"python": ("https://docs.python.org/3", None),
"pytest": ("https://docs.pytest.org/en/stable", None),
"sdmx1": ("https://sdmx1.readthedocs.io/en/stable", None),
Expand Down
9 changes: 8 additions & 1 deletion doc/whatsnew.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ What's new
Next release
============

- genno is compatible and tested with `NumPy 2.0 <https://numpy.org/doc/stable/release/2.0.0-notes.html>`_, released 2024-06-16 (:issue:`140`, :pull:`141`).
- :mod:`genno` supports and is tested on:

- `Python 3.13 <https://www.python.org/downloads/release/python-3130/>`_, released 2024-10-07 (:pull:`143`).
As of release time, support for :class:`.SparseDataArray` awaits :mod:`sparse`, thus `numba <https://github.com/numba/numba/issues/9413>`__ and `llvmlite <https://github.com/numba/llvmlite/issues/1084>`__.
:class:`.SparseDataArray` should be usable once these dependencies are updated.
- `NumPy 2.0 <https://numpy.org/doc/stable/release/2.0.0-notes.html>`_, released 2024-06-16 (:issue:`140`, :pull:`141`).

- Support for Python 3.8 is dropped (:pull:`143`), as it has reached end-of-life.

v1.26.0 (2024-03-27)
====================
Expand Down
4 changes: 2 additions & 2 deletions genno/caching.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from hashlib import blake2b
from inspect import getmembers, iscode
from pathlib import Path
from typing import TYPE_CHECKING, Callable, Optional, Set, Union
from typing import TYPE_CHECKING, Callable, Optional, Union

import pandas as pd

Expand All @@ -19,7 +19,7 @@
log = logging.getLogger(__name__)

# Types to ignore in Encoder.default()
IGNORE: Set[type] = set()
IGNORE: set[type] = set()


@singledispatch
Expand Down
6 changes: 3 additions & 3 deletions genno/compat/graphviz.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import re
from os import PathLike
from typing import Literal, Mapping, Optional, Set, Union
from typing import Literal, Mapping, Optional, Union

from genno.core.describe import is_list_of_keys, label

Expand Down Expand Up @@ -55,9 +55,9 @@ def __init__(
self.graph = Digraph(graph_attr=self.ga, node_attr=na, edge_attr=edge_attr)

# Nodes or edges already seen
self.seen: Set[str] = set()
self.seen: set[str] = set()
# Nodes already connected to the graph
self.connected: Set[str] = set()
self.connected: set[str] = set()

def get_attrs(self, kind: Literal["data", "func"], name: str, **defaults) -> dict:
"""Prepare attributes for a node of `kind`.
Expand Down
5 changes: 2 additions & 3 deletions genno/compat/pint.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@
"""

from importlib.metadata import version
from typing import Tuple, Type

import pint

try:
PintError: Tuple[Type[Exception], ...] = (pint.PintError,)
ApplicationRegistry: Type = pint.ApplicationRegistry
PintError: tuple[type[Exception], ...] = (pint.PintError,)
ApplicationRegistry: type = pint.ApplicationRegistry
except AttributeError: # pragma: no cover
# Older versions of pint, e.g. 0.17
PintError = (type("PintError", (Exception,), {}), pint.DefinitionSyntaxError)
Expand Down
4 changes: 2 additions & 2 deletions genno/compat/plotnine/plot.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Any, Dict, Hashable, Optional, Sequence
from typing import Any, Hashable, Optional, Sequence
from warnings import warn

import plotnine as p9
Expand Down Expand Up @@ -31,7 +31,7 @@ class Plot(ABC):
#: accepted by :meth:`generate`.
inputs: Sequence[Hashable] = []
#: Keyword arguments for :any:`plotnine.ggplot.save`.
save_args: Dict[str, Any] = dict(verbose=False)
save_args: dict[str, Any] = dict(verbose=False)

# TODO add static geoms automatically in generate()
__static: Sequence = []
Expand Down
3 changes: 1 addition & 2 deletions genno/compat/pyam/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import logging
from functools import partial
from importlib.util import find_spec
from typing import List

from genno import Computer, Key
from genno.config import handles
Expand Down Expand Up @@ -31,7 +30,7 @@ def iamc(c: Computer, info):
name = info.pop("variable")

# Chain of keys produced: first entry is the key for the base quantity
keys: List[Key] = [Key(info.pop("base"))]
keys: list[Key] = [Key(info.pop("base"))]

# Second entry is a simple rename
keys.append(single_key(c.add_single(Key(name, keys[0].dims, keys[0].tag), keys[0])))
Expand Down
8 changes: 4 additions & 4 deletions genno/compat/sdmx/operator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, Hashable, Iterable, List, Mapping, Optional, Tuple, Union
from typing import Hashable, Iterable, Mapping, Optional, Union

import genno
from genno import Quantity
Expand All @@ -23,7 +23,7 @@
def codelist_to_groups(
codes: Union["sdmx.model.common.Codelist", Iterable["sdmx.model.common.Code"]],
dim: Optional[str] = None,
) -> Mapping[str, Mapping[str, List[str]]]:
) -> Mapping[str, Mapping[str, list[str]]]:
"""Convert `codes` into a mapping from parent items to their children.

The returned value is suitable for use with :func:`~.operator.aggregate`.
Expand Down Expand Up @@ -74,7 +74,7 @@ def dataset_to_quantity(ds: "sdmx.model.common.BaseDataSet") -> Quantity:
of `ds`, if any.
"""
# Assemble attributes
attrs: Dict[str, str] = {}
attrs: dict[str, str] = {}
if ds.described_by: # pragma: no cover
attrs.update(dataflow_urn=util.urn(ds.described_by))
if ds.structured_by:
Expand Down Expand Up @@ -139,7 +139,7 @@ def quantity_to_dataset(
series_dims = list(dims[:od_index] + dims[od_index + 1 :])
grouped: Iterable = qty.to_series().groupby(series_dims)
# For as_obs()
obs_dims: Tuple[Hashable, ...] = (od.id,)
obs_dims: tuple[Hashable, ...] = (od.id,)
key_slice = slice(od_index, od_index + 1)
else:
# Pseudo-groupby object
Expand Down
8 changes: 4 additions & 4 deletions genno/compat/sdmx/util.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional, Tuple, Type, Union
from typing import Optional, Union

import sdmx

Expand Down Expand Up @@ -29,10 +29,10 @@ def urn(obj: "sdmx.model.common.MaintainableArtefact") -> str:

def handle_version(
version: Union["sdmx.format.Version", str, None],
) -> Tuple[
) -> tuple[
"sdmx.format.Version",
Type["sdmx.model.common.BaseDataSet"],
Type["sdmx.model.common.BaseObservation"],
type["sdmx.model.common.BaseDataSet"],
type["sdmx.model.common.BaseObservation"],
]:
"""Handle `version` arguments for :mod:`.sdmx.operator`.

Expand Down
7 changes: 3 additions & 4 deletions genno/compat/xarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
Optional,
Protocol,
Sequence,
Tuple,
TypeVar,
Union,
)
Expand Down Expand Up @@ -74,11 +73,11 @@ def coords(self) -> xarray.core.coordinates.DataArrayCoordinates: ...

@property
@abstractmethod
def dims(self) -> Tuple[Hashable, ...]: ...
def dims(self) -> tuple[Hashable, ...]: ...

@property
@abstractmethod
def shape(self) -> Tuple[int, ...]: ...
def shape(self) -> tuple[int, ...]: ...

@property
@abstractmethod
Expand Down Expand Up @@ -204,7 +203,7 @@ def min(
@abstractmethod
def pipe(
self,
func: Union[Callable[..., T], Tuple[Callable[..., T], str]],
func: Union[Callable[..., T], tuple[Callable[..., T], str]],
*args: Any,
**kwargs: Any,
): ...
Expand Down
13 changes: 4 additions & 9 deletions genno/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,11 @@
from typing import (
Any,
Callable,
Dict,
Iterable,
List,
Mapping,
MutableMapping,
Optional,
Sequence,
Set,
Tuple,
Type,
Union,
)
from warnings import warn
Expand All @@ -29,7 +24,7 @@
log = logging.getLogger(__name__)

#: Registry of configuration section handlers.
HANDLERS: Dict[str, "ConfigHandler"] = {}
HANDLERS: dict[str, "ConfigHandler"] = {}

#: .. deprecated:: 1.25.0
#: Instead, use:
Expand All @@ -41,7 +36,7 @@
#: handles("section_name", False, False)(store)
#:
#: Configuration sections/keys to be stored with no action.
STORE: Set[str] = set()
STORE: set[str] = set()


def configure(path: Optional[Union[Path, str]] = None, **config):
Expand Down Expand Up @@ -182,7 +177,7 @@ def parse_config(
data = PathHandler().handle(data, c)

# Assemble a queue of (args, kwargs) for Computer.add_queue()
queue: List[Tuple[Tuple, Dict]] = []
queue: list[tuple[tuple, dict]] = []

# Sections to discard, e.g. with handler._store = False
discard = set()
Expand Down Expand Up @@ -341,7 +336,7 @@ def general(c: Computer, info):
# log.debug(f"Inferred dimensions ({', '.join(key.dims)}) for '*'")

# If info["comp"] is None, the task is a list that collects other keys
_seq: Type = list
_seq: type = list
task = []

if info["comp"] is not None:
Expand Down
12 changes: 7 additions & 5 deletions genno/core/attrseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
Mapping,
Optional,
Sequence,
Tuple,
Union,
cast,
)
Expand Down Expand Up @@ -249,13 +248,13 @@ def data(self):
return self.values

@property
def dims(self) -> Tuple[Hashable, ...]:
def dims(self) -> tuple[Hashable, ...]:
"""Like :attr:`xarray.DataArray.dims`."""
# If 0-D, the single dimension has name `None` → discard
return tuple(filter(None, self.index.names))

@property
def shape(self) -> Tuple[int, ...]:
def shape(self) -> tuple[int, ...]:
"""Like :attr:`xarray.DataArray.shape`."""
idx = self.index.remove_unused_levels()
return tuple(len(idx.levels[i]) for i in map(idx.names.index, self.dims))
Expand Down Expand Up @@ -294,7 +293,10 @@ def drop_vars(
return self.droplevel(names)

def expand_dims(self, dim=None, axis=None, **dim_kwargs: Any) -> "AttrSeries":
"""Like :meth:`xarray.DataArray.expand_dims`."""
"""Like :meth:`xarray.DataArray.expand_dims`.

.. todo:: Support passing a mapping of length > 1 to `dim`.
"""
if isinstance(dim, list):
dim = dict.fromkeys(dim, [])
dim = either_dict_or_kwargs(dim, dim_kwargs, "expand_dims")
Expand Down Expand Up @@ -620,7 +622,7 @@ def xindexes(self): # pragma: no cover
# Internal methods
def align_levels(
self, other: "AttrSeries"
) -> Tuple[Sequence[Hashable], "AttrSeries"]:
) -> tuple[Sequence[Hashable], "AttrSeries"]:
"""Return a copy of `self` with ≥1 dimension(s) in the same order as `other`.

Work-around for https://github.com/pandas-dev/pandas/issues/25760 and other
Expand Down
Loading