Skip to content
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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
# Run static checks only for Ubuntu and Python 3.9
# Run static checks only for Ubuntu and the lowest support version of Python
static-checks:
name: Static checks
runs-on: ubuntu-latest
Expand All @@ -25,7 +25,7 @@ jobs:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-python
with:
python-version: "3.9"
python-version: "3.10"
requirements: tox
# Instead of running all checks in one task (i.e., `tox -m static`),
# we'll instead run them one-by-one, so that it's easier to
Expand All @@ -47,11 +47,11 @@ jobs:
strategy:
matrix:
python-version:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
#- "3.14"

Copilot AI Oct 11, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commented-out Python 3.14 version in the CI matrix should either be removed or uncommented. If Python 3.14 testing is not ready, consider adding a comment explaining the reason or timeline for enabling it.

Suggested change
#- "3.14"
# - "3.14" # Python 3.14 is not yet available on GitHub Actions runners; will enable when released and supported.

Copilot uses AI. Check for mistakes.
os:
- ubuntu-latest
# There shouldn't be any behavior differences between OSes,
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
fetch-depth: 0
- uses: ./.github/actions/setup-python
with:
python-version: "3.9"
python-version: "3.10"
requirements: tox
- name: Build source (sdist) and binary (wheel) distributions
run: tox -e build-dists
Expand Down Expand Up @@ -112,7 +112,7 @@ jobs:
fetch-depth: 0
- uses: ./.github/actions/setup-python
with:
python-version: "3.9"
python-version: "3.10"
requirements: tox
- name: Generate release notes
run: tox -e release-notes
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
default_language_version:
python: python3.9
python: python3.10

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ version: 2
build:
os: ubuntu-lts-latest
tools:
python: "3.9"
python: "3.10"
apt_packages:
# chromium-browser is required by Kaleido to generate Plotly figures via fig.write_image()
- chromium-browser
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# >>> Variables
# ==============================================================

BASE_PYTHON ?= python3.9
BASE_PYTHON ?= python3.10

VENV_PATH := .venv
VENV_BIN := $(VENV_PATH)/bin
Expand Down
89 changes: 48 additions & 41 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from __future__ import annotations

import ast
import importlib.metadata
import inspect
import pkgutil
import sys
from contextlib import contextmanager
from datetime import datetime
Expand All @@ -10,6 +13,8 @@

from typing_extensions import Any

import ridgeplot as ridgeplot_pkg

try:
from ridgeplot_examples import ALL_EXAMPLES
except ImportError:
Expand Down Expand Up @@ -271,47 +276,49 @@


# Type aliases
_TYPE_ALIASES_FULLY_QUALIFIED = {
# ------- ._color.css_colors -------------------
"ridgeplot._color.css_colors.CssNamedColor",
# ------- ._color.interpolation ----------------
"ridgeplot._color.interpolation.ColorscaleInterpolants",
"ridgeplot._color.interpolation.SolidColormode",
# ------- ._kde --------------------------------
"ridgeplot._kde.KDEPoints",
"ridgeplot._kde.KDEBandwidth",
# ------- ._missing ----------------------------
"ridgeplot._missing.MISSING",
"ridgeplot._missing.MissingType",
# ------- ._types ------------------------------
"ridgeplot._types.Color",
"ridgeplot._types.ColorScale",
"ridgeplot._types.NormalisationOption",
"ridgeplot._types.CollectionL1",
"ridgeplot._types.CollectionL2",
"ridgeplot._types.CollectionL3",
"ridgeplot._types.Float",
"ridgeplot._types.Int",
"ridgeplot._types.Numeric",
"ridgeplot._types.NumericT",
"ridgeplot._types.XYCoordinate",
"ridgeplot._types.DensityTrace",
"ridgeplot._types.DensitiesRow",
"ridgeplot._types.Densities",
"ridgeplot._types.ShallowDensities",
"ridgeplot._types.SamplesTrace",
"ridgeplot._types.SamplesRow",
"ridgeplot._types.Samples",
"ridgeplot._types.ShallowSamples",
"ridgeplot._types.TraceType",
"ridgeplot._types.TraceTypesArray",
"ridgeplot._types.ShallowTraceTypesArray",
"ridgeplot._types.LabelsArray",
"ridgeplot._types.ShallowLabelsArray",
"ridgeplot._types.SampleWeights",
"ridgeplot._types.SampleWeightsArray",
"ridgeplot._types.ShallowSampleWeightsArray",
}
def _get_module_type_aliases(module_name: str) -> set[str]:
module = import_module(module_name)
source = inspect.getsource(module)
tree = ast.parse(source)
type_aliases = {
f"{module_name}.{node.target.id}"
for node in ast.walk(tree)
# Handle annotated assignments: x: TypeAlias = ...
if (
isinstance(node, ast.AnnAssign)
and isinstance(node.target, ast.Name)
and isinstance(node.annotation, ast.Name)
and node.annotation.id == "TypeAlias"
)
}
return type_aliases


def _get_package_type_aliases(package_name: str) -> set[str]:
"""Recursively extract TypeAlias definitions from a package."""
package = import_module(package_name)
prefix = f"{package.__name__}."
type_aliases = set()
for _, modname, is_pkg in pkgutil.walk_packages(package.__path__, prefix):
if is_pkg:
type_aliases.update(_get_package_type_aliases(modname))
else:
type_aliases.update(_get_module_type_aliases(modname))
return type_aliases


_TYPE_ALIASES_FULLY_QUALIFIED = (
# Automatically extract all TypeAlias
# definitions from the `ridgeplot` package
_get_package_type_aliases(ridgeplot_pkg.__name__)
| {
# `MISSING` is a special case that is
# widely referenced in type annotations
"ridgeplot._missing.MISSING",
# Same thing with the `NumericT` TypeVar
"ridgeplot._types.NumericT",
}
)
for fq in _TYPE_ALIASES_FULLY_QUALIFIED:
module_name, _, type_name = fq.rpartition(".")
try:
Expand Down
6 changes: 3 additions & 3 deletions docs/development/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Here are our guidelines for setting a **working** development environment. Most
:::{admonition} Prerequisites
:class: important

To follow along with this guide, you will need to have [git](https://git-scm.com/), [make](https://www.gnu.org/software/make/), and [uv](https://docs.astral.sh/uv/) installed on your system. We recommend using [uv](https://docs.astral.sh/uv/guides/install-python/) to also manage your Python installations. For this project you will need Python 3.9 or higher.
To follow along with this guide, you will need to have [git](https://git-scm.com/), [make](https://www.gnu.org/software/make/), and [uv](https://docs.astral.sh/uv/) installed on your system. We recommend using [uv](https://docs.astral.sh/uv/guides/install-python/) to also manage your Python installations. For this project you will need Python 3.10 or higher.
:::

First, you will need to [clone](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo#step-2-create-a-local-clone-of-your-fork) this repository. For this, make sure you have a [GitHub account](https://github.com/join), fork ridgeplot by clicking the [Fork](https://github.com/tpvasconcelos/ridgeplot/fork) button, and clone the main repository locally (_e.g.,_ using SSH)
Expand Down Expand Up @@ -55,10 +55,10 @@ The following command will:
make init
```

The default and **recommended** base Python is `python3.9`. However, if you encounter any issues or don't have this specific version installed on your system, you can change by it exporting the `BASE_PYTHON` environment variable to a valid executable you do have installed. Please note that we no longer support any Python versions lower than 3.9. For example, to use `python3.13`, you should run:
The default and **recommended** base Python is `python3.10`. However, if you encounter any issues or don't have this specific version installed on your system, you can change by it exporting the `BASE_PYTHON` environment variable to a valid executable you do have installed. Please note that we no longer support any Python versions lower than 3.10. For example, to use `python3.14`, you should run:

```shell
BASE_PYTHON=python3.13 make init
BASE_PYTHON=python3.14 make init
```

If for whatever reason you don't have a working internet connection (✈️, ⛵, 🏕️, 🚀, etc.), you can try exposing the `OFFLINE=1` environment variable. This will only work if compatible packages have already been cached by `uv` on your system.
Expand Down
3 changes: 2 additions & 1 deletion docs/reference/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ This document outlines the list of changes to ridgeplot between each release. Fo
Unreleased changes
------------------

- Dropped support for Python 3.9, in accordance with the official Python support policy[^1] ({gh-pr}`345`)
- Bump project classification from Pre-Alpha to Alpha ({gh-pr}`336`)
- Bump actions/github-script from 7 to 8 ({gh-pr}`338`)

### CI/CD

- Bump actions/github-script from 7 to 8 ({gh-pr}`338`)
- pre-commit autoupdate ({gh-pr}`340`)

---
Expand Down
4 changes: 2 additions & 2 deletions misc/brand/logo.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
},
"outputs": [],
"source": [
"from typing import TYPE_CHECKING, Union\n",
"from typing import TYPE_CHECKING\n",
"\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
Expand All @@ -70,7 +70,7 @@
},
"outputs": [],
"source": [
"ColorsRow = Union[str, list[str]]\n",
"ColorsRow = str | list[str]\n",
"Colors = tuple[ColorsRow, ColorsRow, ColorsRow, ColorsRow]\n",
"\n",
"\n",
Expand Down
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[mypy]
python_version = 3.9
python_version = 3.10

# Import discovery ---
files = src/ridgeplot, tests, docs, cicd_utils
Expand Down
5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ classifiers = [
"License :: OSI Approved :: MIT License",
"Natural Language :: English",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",

Copilot AI Oct 11, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commented-out Python 3.14 classifier should either be removed or uncommented with a clear reason for keeping it commented. If Python 3.14 support is not yet ready, consider adding a TODO comment explaining when it will be enabled.

Suggested change
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.13",
# TODO: Enable the Python 3.14 classifier once support is tested and confirmed.

Copilot uses AI. Check for mistakes.
#"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: Implementation :: CPython",
"Topic :: Software Development",
"Topic :: Scientific/Engineering",
Expand All @@ -37,13 +37,12 @@ classifiers = [
keywords = [
"ridgeline", "ridgeplot", "joyplot", "ggridges", "ridges", "ridge", "plot", "plotting", "distplot", "plotly", "data-visualization", "visualization", "data-science", "statistics", "ggplot"
]
requires-python = ">=3.9"
requires-python = ">=3.10"
dependencies = [
"numpy>=1",
"plotly>=5.20", # The `fillgradient` option was added in 5.20
"statsmodels>=0.12,!=0.14.2", # See GH197 for details
"typing-extensions",
'importlib-resources; python_version<"3.10"',
]

[project.urls]
Expand Down
2 changes: 1 addition & 1 deletion pyrightconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"extraPaths": [
"cicd_utils"
],
"pythonVersion": "3.9",
"pythonVersion": "3.10",
"pythonPlatform": "All",
"typeCheckingMode": "strict",
// stricter settings
Expand Down
3 changes: 2 additions & 1 deletion requirements/typing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ types-pytz
types-PyYAML
types-requests
types-tqdm
plotly-stubs; python_version >= "3.10"
pandas-stubs
# These seem to bring more problems than they solve for now... 👇
# plotly-stubs

# pyright also needs to inherit other environment dependencies in
# order to correctly infer types for code in tests, docs, etc.
Expand Down
1 change: 0 additions & 1 deletion ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ ignore = [

# pandas-vet (PD)
"PD015", # Use `.merge` method instead of `pd.merge` function. They have equivalent functionality.
"PD901", # `df` is a bad variable name. Be kinder to your future self.

# Convention (PLC)
"PLC0415", # `import` should be at the top-level of a file
Expand Down
4 changes: 3 additions & 1 deletion src/ridgeplot/_color/css_colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

from __future__ import annotations

from typing import TypeAlias

from typing_extensions import Literal

# Taken from https://www.w3.org/TR/css-color-3/#svg-color

CssNamedColor = Literal[
CssNamedColor: TypeAlias = Literal[
"aliceblue",
"antiquewhite",
"aqua",
Expand Down
9 changes: 4 additions & 5 deletions src/ridgeplot/_color/interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, TypeAlias

from typing_extensions import Literal, Protocol

from ridgeplot._color.utils import apply_alpha, round_color, to_rgb, unpack_rgb
from ridgeplot._types import CollectionL2, ColorScale
from ridgeplot._utils import get_xy_extrema, normalise_min_max
from ridgeplot._vendor.more_itertools import zip_strict

if TYPE_CHECKING:
from collections.abc import Generator
Expand Down Expand Up @@ -110,7 +109,7 @@ def slice_colorscale(
# ==============================================================


ColorscaleInterpolants = CollectionL2[float]
ColorscaleInterpolants: TypeAlias = CollectionL2[float]
"""A :data:`ColorscaleInterpolants` contains the interpolants for a :data:`ColorScale`.

Example
Expand Down Expand Up @@ -151,7 +150,7 @@ def __call__(self, ctx: InterpolationContext) -> ColorscaleInterpolants: ...

def _mul(a: tuple[Numeric, ...], b: tuple[Numeric, ...]) -> tuple[Numeric, ...]:
"""Multiply two tuples element-wise."""
return tuple(a_i * b_i for a_i, b_i in zip_strict(a, b))
return tuple(a_i * b_i for a_i, b_i in zip(a, b, strict=True))


def _interpolate_row_index(ctx: InterpolationContext) -> ColorscaleInterpolants:
Expand Down Expand Up @@ -215,7 +214,7 @@ def _interpolate_mean_means(ctx: InterpolationContext) -> ColorscaleInterpolants
]


SolidColormode = Literal[
SolidColormode: TypeAlias = Literal[
"row-index",
"trace-index",
"trace-index-row-wise",
Expand Down
3 changes: 1 addition & 2 deletions src/ridgeplot/_color/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

if TYPE_CHECKING:
from collections.abc import Collection
from typing import Union

from ridgeplot._types import Color

Expand Down Expand Up @@ -54,7 +53,7 @@ def unpack_rgb(rgb: str) -> tuple[float, float, float, float] | tuple[float, flo
prefix = rgb.split("(")[0] + "("
values_str = map(str.strip, rgb.removeprefix(prefix).removesuffix(")").split(","))
values_num = tuple(int(v) if v.isdecimal() else float(v) for v in values_str)
return cast("Union[tuple[float, float, float, float], tuple[float, float, float]]", values_num)
return cast("tuple[float, float, float, float] | tuple[float, float, float]", values_num)


def apply_alpha(color: Color, alpha: float) -> str:
Expand Down
7 changes: 3 additions & 4 deletions src/ridgeplot/_figure_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
normalise_row_attrs,
ordered_dedup,
)
from ridgeplot._vendor.more_itertools import zip_strict

if TYPE_CHECKING:
from collections.abc import Collection
Expand Down Expand Up @@ -188,12 +187,12 @@ def create_ridgeplot(
fig = go.Figure()
ith_trace = 0
for ith_row, (row_traces, row_trace_types, row_trace_labels, row_colors) in enumerate(
zip_strict(densities, trace_types, trace_labels, solid_colors)
zip(densities, trace_types, trace_labels, solid_colors, strict=True)
):
y_base = float(-ith_row * y_max * spacing)
tickvals.append(y_base)
for trace, trace_type, label, color in zip_strict(
row_traces, row_trace_types, row_trace_labels, row_colors
for trace, trace_type, label, color in zip(
row_traces, row_trace_types, row_trace_labels, row_colors, strict=True
):
trace_drawer = get_trace_cls(trace_type)(
trace=trace,
Expand Down
Loading