Skip to content
Open
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
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
groups:
python-dependencies:
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
Expand Down
110 changes: 10 additions & 100 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ jobs:
filters: |
python:
- 'ultraplot/**'
- 'pyproject.toml'
- 'environment.yml'
- '.github/workflows/**'
- 'tools/ci/**'

select-tests:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -52,7 +56,7 @@ jobs:
init-shell: bash
create-args: >-
--verbose
python=3.11
python=3.10
matplotlib=3.9
cache-environment: true
cache-downloads: false
Expand Down Expand Up @@ -126,107 +130,13 @@ jobs:
with:
python-version: "3.11"

- name: Install dependencies
run: pip install tomli

- id: set-versions
run: |
# Create a Python script to read and parse versions
cat > get_versions.py << 'EOF'
import tomli
import re
import json

# Read pyproject.toml
with open("pyproject.toml", "rb") as f:
data = tomli.load(f)

# Get Python version requirement
python_req = data["project"]["requires-python"]

# Parse min and max versions
min_version = re.search(r">=(\d+\.\d+)", python_req)
max_version = re.search(r"<(\d+\.\d+)", python_req)

python_versions = []
if min_version and max_version:
# Convert version strings to tuples
min_v = tuple(map(int, min_version.group(1).split(".")))
max_v = tuple(map(int, max_version.group(1).split(".")))

# Generate version list
current = min_v
while current < max_v:
python_versions.append(".".join(map(str, current)))
current = (current[0], current[1] + 1)


# parse MPL versions
mpl_req = None
for d in data["project"]["dependencies"]:
if d.startswith("matplotlib"):
mpl_req = d
break
assert mpl_req is not None, "matplotlib version not found in dependencies"
min_version = re.search(r">=(\d+\.\d+)", mpl_req)
max_version = re.search(r"<(\d+\.\d+)", mpl_req)

mpl_versions = []
if min_version and max_version:
# Convert version strings to tuples
min_v = tuple(map(int, min_version.group(1).split(".")))
max_v = tuple(map(int, max_version.group(1).split(".")))

# Generate version list
current = min_v
while current < max_v:
mpl_versions.append(".".join(map(str, current)))
current = (current[0], current[1] + 1)

# If no versions found, default to 3.9
if not mpl_versions:
mpl_versions = ["3.9"]

# Create output dictionary
midpoint_python = python_versions[len(python_versions) // 2]
midpoint_mpl = mpl_versions[len(mpl_versions) // 2]
matrix_candidates = [
(python_versions[0], mpl_versions[0]), # lowest + lowest
(midpoint_python, midpoint_mpl), # midpoint + midpoint
(python_versions[-1], mpl_versions[-1]) # latest + latest
]
test_matrix = []
seen = set()
for py_ver, mpl_ver in matrix_candidates:
key = (py_ver, mpl_ver)
if key in seen:
continue
seen.add(key)
test_matrix.append(
{"python-version": py_ver, "matplotlib-version": mpl_ver}
)

output = {
"python_versions": python_versions,
"matplotlib_versions": mpl_versions,
"test_matrix": test_matrix,
}

# Print as JSON
print(json.dumps(output))
EOF

# Run the script and capture output
OUTPUT=$(python3 get_versions.py)
PYTHON_VERSIONS=$(echo $OUTPUT | jq -r '.python_versions')
MPL_VERSIONS=$(echo $OUTPUT | jq -r '.matplotlib_versions')

echo "Detected Python versions: ${PYTHON_VERSIONS}"
echo "Detected Matplotlib versions: ${MPL_VERSIONS}"
echo "Detected test matrix: $(echo $OUTPUT | jq -c '.test_matrix')"
echo "python-versions=$(echo $PYTHON_VERSIONS | jq -c)" >> $GITHUB_OUTPUT
echo "matplotlib-versions=$(echo $MPL_VERSIONS | jq -c)" >> $GITHUB_OUTPUT
echo "test-matrix=$(echo $OUTPUT | jq -c '.test_matrix')" >> $GITHUB_OUTPUT
OUTPUT=$(python tools/ci/version_support.py)
echo "Detected Python versions: $(echo "$OUTPUT" | jq -c '.python_versions')"
echo "Detected Matplotlib versions: $(echo "$OUTPUT" | jq -c '.matplotlib_versions')"
echo "Detected test matrix: $(echo "$OUTPUT" | jq -c '.test_matrix')"
python tools/ci/version_support.py --format github-output >> $GITHUB_OUTPUT

build:
needs:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-map.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
init-shell: bash
create-args: >-
--verbose
python=3.11
python=3.10
matplotlib=3.9
cache-environment: true
cache-downloads: false
Expand Down
55 changes: 25 additions & 30 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,49 @@

import json
import os
import re
import shlex
import shutil
import tempfile
import importlib.util
from pathlib import Path

import nox

PROJECT_ROOT = Path(__file__).parent
PYPROJECT_PATH = PROJECT_ROOT / "pyproject.toml"
VERSION_SUPPORT_PATH = PROJECT_ROOT / "tools" / "ci" / "version_support.py"

nox.options.reuse_existing_virtualenvs = True
nox.options.sessions = ["tests"]


def _load_pyproject() -> dict:
try:
import tomllib
except ImportError: # pragma: no cover - py<3.11
import tomli as tomllib
with PYPROJECT_PATH.open("rb") as f:
return tomllib.load(f)


def _version_range(requirement: str) -> list[str]:
min_match = re.search(r">=(\d+\.\d+)", requirement)
max_match = re.search(r"<(\d+\.\d+)", requirement)
if not (min_match and max_match):
return []
min_v = tuple(map(int, min_match.group(1).split(".")))
max_v = tuple(map(int, max_match.group(1).split(".")))
versions = []
current = min_v
while current < max_v:
versions.append(".".join(map(str, current)))
current = (current[0], current[1] + 1)
return versions
def _load_version_support():
"""
Import the shared version-support helper from the repo checkout.
"""
spec = importlib.util.spec_from_file_location(
"version_support",
VERSION_SUPPORT_PATH,
)
if spec is None or spec.loader is None:
raise ImportError(
f"Could not load 'version_support' module from {VERSION_SUPPORT_PATH}"
)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module


def _matrix_versions() -> tuple[list[str], list[str]]:
data = _load_pyproject()
python_req = data["project"]["requires-python"]
py_versions = _version_range(python_req)
mpl_req = next(
dep for dep in data["project"]["dependencies"] if dep.startswith("matplotlib")
"""
Derive the supported Python/Matplotlib test matrix from the shared helper.
"""
version_support = _load_version_support()
data = version_support.load_pyproject(PYPROJECT_PATH)
return (
version_support.supported_python_versions(data),
version_support.supported_matplotlib_versions(data),
)
mpl_versions = _version_range(mpl_req) or ["3.9"]
return py_versions, mpl_versions


PYTHON_VERSIONS, MPL_VERSIONS = _matrix_versions()
Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ include-package-data = true
write_to = "ultraplot/_version.py"
write_to_template = "__version__ = '{version}'\n"

[tool.ultraplot.core_versions]
python = ["3.10", "3.11", "3.12", "3.13", "3.14"]
matplotlib = ["3.9", "3.10"]


[tool.ruff]
ignore = ["I001", "I002", "I003", "I004"]
Expand Down
Loading
Loading