Skip to content

Commit

Permalink
Merge pull request #74 from khaeru/ci-gha-commands
Browse files Browse the repository at this point in the history
Miscellaneous improvements for 2022-W42
  • Loading branch information
khaeru committed Oct 28, 2022
2 parents d340bc1 + 63771be commit 5d89c87
Show file tree
Hide file tree
Showing 14 changed files with 94 additions and 75 deletions.
7 changes: 3 additions & 4 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,15 @@ jobs:
python-version: "3.x"

- name: Upgrade pip, wheel, setuptools-scm
id: pip
run: |
python -m pip install --upgrade pip wheel setuptools-scm
# Locate pip cache directory
echo "::set-output name=cache-dir::$(pip cache dir)"
echo "pip-cache-dir=$(pip cache dir)" >> $GITHUB_ENV
- name: Cache Python packages
uses: actions/cache@v3
with:
path: ${{ steps.pip.outputs.cache-dir }}
path: ${{ env.pip-cache-dir }}
key: lint-${{ runner.os }}

- name: Check "black" code style
Expand All @@ -48,5 +47,5 @@ jobs:
- name: Check typing with mypy
# Also install packages that provide type hints
run: |
pip install mypy ixmp pytest types-PyYAML "xarray!=2022.6.0"
pip install mypy ixmp pytest types-PyYAML types-setuptools xarray
mypy .
12 changes: 4 additions & 8 deletions .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:

steps:
- name: Cancel previous runs that have not completed
uses: styfle/cancel-workflow-action@0.10.0
uses: styfle/cancel-workflow-action@0.11.0
with:
access_token: ${{ github.token }}

Expand All @@ -55,16 +55,16 @@ jobs:
python-version: ${{ matrix.python-version }}

- name: Upgrade pip, wheel, setuptools-scm
id: pip
shell: bash
run: |
python -m pip install --upgrade pip wheel setuptools-scm
# Locate pip cache directory
echo "::set-output name=cache-dir::$(pip cache dir)"
echo "pip-cache-dir=$(pip cache dir)" >> $GITHUB_ENV
- name: Cache Python packages
uses: actions/cache@v3
with:
path: ${{ steps.pip.outputs.cache-dir }}
path: ${{ env.pip-cache-dir }}
key: ${{ matrix.os }}-py${{ matrix.python-version }}
restore-keys: |
${{ matrix.os }}-
Expand All @@ -74,10 +74,6 @@ jobs:
- name: Install Python package and dependencies
run: |
pip install --editable .[docs,tests]
# TEMPORARY downgrade xarray pending khaeru/genno#67
pip install xarray!=2022.6.0
# TEMPORARY downgrade matplotlib pending has2k1/plotnine#619
pip install "matplotlib<3.6.0"
- name: Run test suite using pytest
run: pytest genno --trace-config --verbose --cov-report=xml --cov-report=term --color=yes
Expand Down
7 changes: 5 additions & 2 deletions doc/whatsnew.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ What's new
:backlinks: none
:depth: 1

.. Next release
.. ============
Next release
============

- Fix :meth:`~.AttrSeries.cumprod` for 1-dimensional :class:`.AttrSeries` (:pull:`74`).
- Adjust for compatibility with pint 0.20 (released 2022-10-25) (:pull:`74`).

v1.14.0 (2022-09-27)
====================
Expand Down
3 changes: 2 additions & 1 deletion genno/computations.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import pandas as pd
import pint
from xarray.core.types import InterpOptions
from xarray.core.utils import either_dict_or_kwargs

from genno.core.attrseries import AttrSeries, _multiindex_of
Expand Down Expand Up @@ -420,7 +421,7 @@ def group_sum(qty, group, sum):
def interpolate(
qty: Quantity,
coords: Mapping[Hashable, Any] = None,
method: str = "linear",
method: InterpOptions = "linear",
assume_sorted: bool = True,
kwargs: Mapping[str, Any] = None,
**coords_kwargs: Any,
Expand Down
2 changes: 1 addition & 1 deletion genno/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ def units(c: Computer, info):
registry.define(defs)
except KeyError:
pass
except pint.DefinitionSyntaxError as e:
except (TypeError, pint.DefinitionSyntaxError, pint.RedefinitionError) as e:
log.warning(e)
else:
log.info(f"Apply global unit definitions {defs}")
Expand Down
61 changes: 29 additions & 32 deletions genno/core/attrseries.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
import logging
import warnings
from functools import partial
from typing import (
Any,
Hashable,
Iterable,
List,
Mapping,
Optional,
Sequence,
Tuple,
Union,
cast,
)
from typing import Any, Hashable, Iterable, List, Mapping, Optional, Tuple, Union, cast

import numpy as np
import pandas as pd
Expand All @@ -21,6 +10,7 @@
from xarray.core.utils import either_dict_or_kwargs

from genno.core.quantity import Quantity
from genno.core.types import Dims

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -147,14 +137,12 @@ def cumprod(self, dim=None, axis=None, skipna=None, **kwargs):
"""Like :meth:`xarray.DataArray.cumprod`."""
if axis:
log.info(f"{self.__class__.__name__}.cumprod(…, axis=…) is ignored")
if skipna is None:
skipna = self.dtype == float

return self.__class__(
self.unstack(dim)
.cumprod(axis=1, skipna=skipna, **kwargs)
.stack()
.reorder_levels(self.dims),
attrs=self.attrs,
)
# Group on dimensions other than `dim`
result = self._maybe_groupby(dim).cumprod(skipna=skipna, **kwargs)
return AttrSeries(result, attrs=self.attrs)

@property
def dims(self) -> Tuple[Hashable, ...]:
Expand Down Expand Up @@ -427,15 +415,18 @@ def shift(

def sum(
self,
dim: Optional[Union[Hashable, Sequence[Hashable]]] = None,
dim: Dims = None,
# Signature from xarray.DataArray
# *,
# skipna: bool | None = None,
# min_count: int | None = None,
skipna: Optional[bool] = None,
min_count: Optional[int] = None,
keep_attrs: Optional[bool] = None,
**kwargs: Any,
) -> "AttrSeries":
"""Like :meth:`xarray.DataArray.sum`."""
if skipna is not None or min_count is not None:
raise NotImplementedError

if dim is None or isinstance(dim, Hashable):
dim = tuple(filter(None, (dim,)))

Expand All @@ -447,16 +438,7 @@ def sum(
)

# Create the object on which to .sum()
if len(dim) in (0, len(self.index.names)):
obj = cast(pd.Series, super())
else:
# Group on dimensions other than `dim`
obj = self.groupby(
list(filter(lambda d: d not in dim, self.index.names)), # type: ignore
observed=True,
)

return AttrSeries(obj.sum(**kwargs), attrs=self.attrs)
return AttrSeries(self._maybe_groupby(dim).sum(**kwargs), attrs=self.attrs)

def squeeze(self, dim=None, *args, **kwargs):
"""Like :meth:`xarray.DataArray.squeeze`."""
Expand Down Expand Up @@ -554,3 +536,18 @@ def align_levels(self, other):

# Reorder, if that would do anything
return result.reorder_levels(order) if len(order) > 1 else result

def _maybe_groupby(self, dim):
"""Return an object for operations along dimension(s) `dim`.
If `dim` is a subset of :attr:`dims`, returns a SeriesGroupBy object along the
other dimensions.
"""
if len(set(dim)) in (0, len(self.index.names)):
return cast(pd.Series, super())
else:
# Group on dimensions other than `dim`
return self.groupby(
list(filter(lambda d: d not in dim, self.index.names)), # type: ignore
observed=True,
)
27 changes: 16 additions & 11 deletions genno/core/quantity.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from functools import update_wrapper
from typing import Any, Dict, Hashable, Mapping, Optional, Sequence, Tuple, Union
from typing import Any, Dict, Hashable, Mapping, Optional, Tuple, Union

import numpy as np
import pandas as pd
import pint
import xarray
from xarray.core.types import InterpOptions

from .types import Dims

#: Name of the class used to implement :class:`.Quantity`.
CLASS = "AttrSeries"
Expand Down Expand Up @@ -108,8 +111,10 @@ def dims(self) -> Tuple[Hashable, ...]:
... # pragma: no cover

def assign_coords(
self, coords: Optional[Mapping[Any, Any]] = None, **coords_kwargs: Any
) -> "Quantity":
self,
coords: Optional[Mapping[Any, Any]] = None,
**coords_kwargs: Any,
):
... # pragma: no cover

def copy(
Expand All @@ -129,9 +134,9 @@ def expand_dims(

def interp(
self,
coords: Mapping[Hashable, Any] = None,
method: str = "linear",
assume_sorted: bool = True,
coords: Mapping[Any, Any] = None,
method: InterpOptions = "linear",
assume_sorted: bool = False,
kwargs: Mapping[str, Any] = None,
**coords_kwargs: Any,
):
Expand Down Expand Up @@ -167,14 +172,14 @@ def shift(

def sum(
self,
dim: Optional[Union[Hashable, Sequence[Hashable]]] = None,
dim: Dims = None,
# Signature from xarray.DataArray
# *,
# skipna: bool | None = None,
# min_count: int | None = None,
*,
skipna: Optional[bool] = None,
min_count: Optional[int] = None,
keep_attrs: Optional[bool] = None,
**kwargs: Any,
) -> "Quantity":
): # NB "Quantity" here offends mypy
... # pragma: no cover

def to_numpy(self) -> np.ndarray:
Expand Down
4 changes: 2 additions & 2 deletions genno/core/sparsedataarray.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Dict, Hashable, List, Mapping, Sequence, Tuple, Union
from typing import Any, Dict, Hashable, Mapping, Optional, Sequence, Tuple, Union

import numpy as np
import pandas as pd
Expand Down Expand Up @@ -205,7 +205,7 @@ def sel(
)

def to_dataframe(
self, name: Hashable = None, dim_order: List[Hashable] = None
self, name: Optional[Hashable] = None, dim_order: Sequence[Hashable] = None
) -> pd.DataFrame:
"""Convert this array and its coords into a :class:`~xarray.DataFrame`.
Expand Down
7 changes: 7 additions & 0 deletions genno/core/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from typing import Hashable, Iterable, Union

# Duplicating xarray 2022.10.0
Dims = Union[str, Iterable[Hashable], None]


__all__ = ["Dims"]
10 changes: 10 additions & 0 deletions genno/tests/core/test_attrseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,16 @@ def foo():

@pytest.fixture
def bar():
"""A 1-dimensional quantity."""
yield AttrSeries([0, 1], index=pd.Index(["a1", "a2"], name="a"))


def test_cumprod(bar):
"""AttrSeries.cumprod works with 1-dimensional quantities."""
result = (1.1 + bar).cumprod("a")
assert ("a",) == result.dims


def test_interp(foo):
with pytest.raises(NotImplementedError):
foo.interp(coords=dict(a=["a1", "a1.5", "a2"], b=["b1", "b1.5", "b2"]))
Expand Down Expand Up @@ -75,6 +82,9 @@ def test_sum(foo, bar):
# Fails with v1.13.0 AttrSeries.sum() using unstack()
AttrSeries(_baz.set_index(["a", "b", "c"])["value"]).sum(dim="c")

with pytest.raises(NotImplementedError):
bar.sum("a", skipna=False)


@pytest.mark.skip
def test_sum_large(N_data=1e7): # pragma: no cover
Expand Down
4 changes: 1 addition & 3 deletions genno/tests/test_core.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import re

from genno import configure
from genno.util import REPLACE_UNITS


def test_configure_units(caplog):
# Warning is logged on invalid definitions
configure(units=dict(define="0 = [0] * %"))
assert re.match(r'missing unary operator "."', caplog.messages[0])
assert 1 == len(caplog.records) and "WARNING" == caplog.records[0].levelname

# Unit replacements are stored
configure(units=dict(replace={"foo": "bar"}))
Expand Down
10 changes: 9 additions & 1 deletion genno/tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def test_filter_concat_args(caplog):
),
ids=lambda argvalue: repr(argvalue),
)
def test_parse_units(ureg, input, expected):
def test_parse_units0(ureg, input, expected):
if isinstance(expected, str):
# Expected to work
result = parse_units(input, ureg)
Expand All @@ -77,6 +77,14 @@ def test_parse_units(ureg, input, expected):
parse_units(pd.Series(input))


def test_parse_units1(ureg, caplog):
"""Multiple attempts to (re)define new units."""
parse_units("JPY")
parse_units("GBP/JPY")
with pytest.raises(ValueError):
parse_units("GBP/JPY/$?")


@pytest.mark.parametrize(
"value, exp",
(
Expand Down
6 changes: 3 additions & 3 deletions genno/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,10 @@ def invalid(unit):

# Try to parse again
return registry.Unit(unit)
except (pint.UndefinedUnitError, pint.RedefinitionError):
# define() failed
except pint.PintError:
# registry.define() failed somehow
raise invalid(unit)
except (AttributeError, TypeError):
except (AttributeError, TypeError, pint.PintError):
# Unit contains a character like '-' that throws off pint
# NB this 'except' clause must be *after* UndefinedUnitError, since that is a
# subclass of AttributeError.
Expand Down

0 comments on commit 5d89c87

Please sign in to comment.