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

🩹 Fix incompatibility of plot_data_and_fits with matplotlib>=3.8 #275

Merged
merged 6 commits into from
Jun 30, 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
46 changes: 45 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,55 @@ jobs:
- name: Run tests
run: python -m pytest --nbval --cov=./ --cov-report term --cov-report xml --cov-config pyproject.toml tests

- name: Codecov Upload
continue-on-error: true
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
name: test-pyglotaran-dev
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

test-min-versions:
name: Test dependency min versions
runs-on: ubuntu-latest
needs: [pre-commit, docs]

steps:
- name: Check out repo
uses: actions/checkout@v4

- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Install dependencies
run: |
python -m pip install -U pip wheel
python -m pip install -r requirements_min.txt
python -m pip install -e ".[test]"

- name: Show installed dependencies
run: pip freeze

- name: Run tests
run: python -m pytest --nbval --cov=./ --cov-report term --cov-report xml --cov-config pyproject.toml tests

- name: Codecov Upload
continue-on-error: true
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
name: test-min-versions
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

deploy:
name: Deploy to PyPi
runs-on: ubuntu-latest
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
needs: test
needs: [test, test-pyglotaran-dev, test-min-versions]
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
steps:
Expand Down
8 changes: 8 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

(changes-0_7_3)=

## 0.7.3 (Unreleased)

- 🩹 Fix incompatibility of plot_data_and_fits with matplotlib>=3.8 (#275)
- 🩹 Fix deprecation warning for using xr.Dataset.dim (#267)
- 🩹 Fix very slow data/residual plots (#239)

(changes-0_7_2)=

## 0.7.2 (2023-12-07)
Expand Down
3 changes: 2 additions & 1 deletion pyglotaran_extras/plotting/plot_traces.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pyglotaran_extras.plotting.utils import add_unique_figure_legend
from pyglotaran_extras.plotting.utils import extract_dataset_scale
from pyglotaran_extras.plotting.utils import extract_irf_location
from pyglotaran_extras.plotting.utils import get_next_cycler_color
from pyglotaran_extras.plotting.utils import select_plot_wavelengths

__all__ = ["select_plot_wavelengths", "plot_fitted_traces"]
Expand Down Expand Up @@ -96,7 +97,7 @@ def plot_data_and_fits(
(result_data.data / scale).plot(x="time", ax=axis, label=f"{dataset_name}_data")
(result_data.fitted_data / scale).plot(x="time", ax=axis, label=f"{dataset_name}_fit")
else:
[next(axis._get_lines.prop_cycler) for _ in range(2)]
[get_next_cycler_color(axis) for _ in range(2)]
if linlog:
axis.set_xscale("symlog", linthresh=linthresh)
axis.xaxis.set_minor_locator(MinorSymLogLocator(linthresh))
Expand Down
23 changes: 23 additions & 0 deletions pyglotaran_extras/plotting/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from matplotlib.pyplot import Axes

from pyglotaran_extras.types import BuiltinSubPlotLabelFormatFunctionKey
from pyglotaran_extras.types import CyclerColor
from pyglotaran_extras.types import ResultLike
from pyglotaran_extras.types import SubPlotLabelCoord

Expand Down Expand Up @@ -215,6 +216,28 @@ def add_unique_figure_legend(fig: Figure, axes: Axes) -> None:
fig.legend(*zip(*unique, strict=True))


def get_next_cycler_color(ax: Axes) -> CyclerColor:
"""Get next color from cycler assigned to ``ax``.

Note
----
This will advance the cycler to the next state.

Parameters
----------
ax : Axes
Axes to get the color from.

Returns
-------
CyclerColor
"""
# Matplotlib<3.8 compat
if hasattr(ax._get_lines, "prop_cycler"):
return next(ax._get_lines.prop_cycler)
return {"color": ax._get_lines.get_next_color()}


def select_plot_wavelengths(
result: ResultLike,
axes_shape: tuple[int, int] = (4, 4),
Expand Down
12 changes: 12 additions & 0 deletions pyglotaran_extras/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@
from collections.abc import Mapping
from collections.abc import Sequence
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Literal
from typing import TypeAlias
from typing import TypedDict

import xarray as xr
from glotaran.project.result import Result

if TYPE_CHECKING:
from pyglotaran_extras.plotting.style import ColorCode


class UnsetType:
"""Type for the ``Unset`` singleton."""
Expand All @@ -26,6 +31,13 @@ def __repr__(self) -> str: # noqa: DOC
This way we can prevent regressions.
"""


class CyclerColor(TypedDict):
"""Color value returned by a cycler."""

color: str | ColorCode


DatasetConvertible: TypeAlias = xr.Dataset | xr.DataArray | str | Path
"""Types of data which can be converted to a dataset."""
ResultLike: TypeAlias = (
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ dynamic = [
dependencies = [
"cycler>=0.10",
"matplotlib>=3.3",
"numpy>=1.21.2",
"numpy>=1.22",
"pyglotaran>=0.7",
"tabulate>=0.8.9",
"xarray>=2022.3",
Expand Down
8 changes: 8 additions & 0 deletions requirements_min.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Minimum versions runtime dependencies

cycler==0.10
numpy==1.22
matplotlib==3.3
pyglotaran==0.7.2
tabulate==0.8.9
xarray==2022.3
12 changes: 4 additions & 8 deletions tests/plotting/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from pyglotaran_extras.plotting.utils import calculate_ticks_in_units_of_pi
from pyglotaran_extras.plotting.utils import ensure_axes_array
from pyglotaran_extras.plotting.utils import format_sub_plot_number_upper_case_letter
from pyglotaran_extras.plotting.utils import get_next_cycler_color
from pyglotaran_extras.plotting.utils import not_single_element_dims

if TYPE_CHECKING:
Expand All @@ -42,11 +43,9 @@ def test_add_cycler_if_not_none_single_axis(cycler: Cycler | None, expected_cycl
ax = plt.subplot()
add_cycler_if_not_none(ax, cycler)

ax_cycler = iter(ax._get_lines._cycler_items)

for _ in range(10):
expected = next(expected_cycler)
assert next(ax_cycler) == expected
assert get_next_cycler_color(ax) == expected


@pytest.mark.parametrize(
Expand All @@ -58,13 +57,10 @@ def test_add_cycler_if_not_none_multiple_axes(cycler: Cycler | None, expected_cy
_, axes = plt.subplots(1, 2)
add_cycler_if_not_none(axes, cycler)

ax0_cycler = iter(axes[0]._get_lines._cycler_items)
ax1_cycler = iter(axes[1]._get_lines._cycler_items)

for _ in range(10):
expected = next(expected_cycler)
assert next(ax0_cycler) == expected
assert next(ax1_cycler) == expected
assert get_next_cycler_color(axes[0]) == expected
assert get_next_cycler_color(axes[1]) == expected


@pytest.mark.parametrize(
Expand Down
Loading