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
53 changes: 26 additions & 27 deletions spec0_action/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from packaging.specifiers import SpecifierSet
from typing import Sequence, cast
from typing import Sequence, Dict
import datetime

from spec0_action.versions import repr_spec_set, tighten_lower_bound
Expand All @@ -15,20 +15,18 @@
)
from packaging.version import Version


__all__ = ["read_schedule", "read_toml", "write_toml", "update_pyproject_toml"]


def update_pyproject_dependencies(dependencies: dict, schedule: SupportSchedule):
def update_pyproject_dependencies(dependencies: dict, schedule: Dict[str, str]):
# Iterate by idx because we want to update it inplace
for i in range(len(dependencies)):
dep_str = dependencies[i]
pkg, extras, spec, env = parse_pep_dependency(dep_str)

if isinstance(spec, Url) or pkg not in schedule["packages"]:
if isinstance(spec, Url) or pkg not in schedule:
continue

new_lower_bound = Version(schedule["packages"][pkg])
new_lower_bound = Version(schedule[pkg])
try:
spec = tighten_lower_bound(spec or SpecifierSet(), new_lower_bound)
# Will raise a value error if bound is already tighter, in this case we just do nothing and continue
Expand Down Expand Up @@ -75,42 +73,43 @@ def update_dependency_table(dep_table: dict, new_versions: dict):
pkg_data["version"] = repr_spec_set(spec)


def update_pixi_dependencies(pixi_tables: dict, schedule: SupportSchedule):
if "pypi-dependencies" in pixi_tables:
update_dependency_table(pixi_tables["pypi-dependencies"], schedule["packages"])
if "dependencies" in pixi_tables:
update_dependency_table(pixi_tables["dependencies"], schedule["packages"])
def update_pixi_dependencies(pixi_tables: dict, schedule: Dict[str, str]):
if "pypi-dependencies" in pixi_tables:
update_dependency_table(pixi_tables["pypi-dependencies"], schedule)
if "dependencies" in pixi_tables:
update_dependency_table(pixi_tables["dependencies"], schedule)

if "feature" in pixi_tables:
for _, feature_data in pixi_tables["feature"].items():
if "dependencies" in feature_data:
update_dependency_table(
feature_data["dependencies"], schedule["packages"]
)
update_dependency_table(feature_data["dependencies"], schedule)


def update_pyproject_toml(
pyproject_data: dict, schedule_data: Sequence[SupportSchedule]
):
now = datetime.datetime.now(datetime.UTC)
try:
new_version = cast(
SupportSchedule,
next(
filter(
lambda s: now >= datetime.datetime.fromisoformat(s["start_date"]),
schedule_data,
)
),
)
except StopIteration:
applicable = sorted(
filter(
lambda s: now >= datetime.datetime.fromisoformat(s["start_date"]),
schedule_data,
),
key=lambda s: datetime.datetime.fromisoformat(s["start_date"]),
)
new_version = {}
for schedule in applicable:
# Fill in the latest known requirement (schedule is sorted, newer entries overwrite older)
for pkg, version in schedule["packages"].items():
new_version[pkg] = version

if not new_version:
raise RuntimeError(
"Could not find schedule that applies to current time, perhaps your schedule is outdated."
)

if "python" in new_version["packages"]:
if "python" in new_version:
pyproject_data["project"]["requires-python"] = repr_spec_set(
parse_version_spec(new_version["packages"]["python"])
parse_version_spec(new_version["python"])
)
update_pyproject_dependencies(
pyproject_data["project"]["dependencies"], new_version
Expand Down
6 changes: 3 additions & 3 deletions tests/test_data/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ requires-python = ">=3.11"
dependencies = [
'pandas>=1.0.0,<3',
'xarray>=2021.1.0',
"ipython>=8.7.0,<4",
"numpy[foo,bar]>=1.10.0,<2",
"scikit-learn>1.2,<1.4;sys_platform=='win32'",
"ipython>=8.7.0",
"numpy[foo,bar]>=1.10.0",
"scikit-learn>1.3,<1.5;sys_platform=='win32'",
"scikit-learn>1.2;sys_platform!='win32'"
]
6 changes: 3 additions & 3 deletions tests/test_data/pyproject_pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ description = "This is just a dummy package for testing the spec 0 update gi
requires-python = ">=3.10"
version = "0.1.0"
dependencies = [
"ipython>=8.7.0,<4",
"numpy[foo,bar]>=1.10.0,<2",
"scikit-learn>1.2,<1.4;sys_platform=='win32'",
"ipython>=8.7.0",
"numpy[foo,bar]>=1.10.0",
"scikit-learn>1.2,<1.5;sys_platform=='win32'",
"scikit-learn>1.2;sys_platform!='win32'"
]

Expand Down
16 changes: 8 additions & 8 deletions tests/test_data/pyproject_pixi_updated.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
authors = [{ name = "Scientific Python Developers"}]
name = "tests"
description = "This is just a dummy package for testing the spec 0 update github action and should not be used"
requires-python = ">=3.11"
requires-python = ">=3.12"
version = "0.1.0"
dependencies = [
"ipython>=8.8.0,<4",
"numpy[foo,bar]>=1.25.0,<2",
"scikit-learn>=1.3.0,<1.4;sys_platform=='win32'",
"scikit-learn>=1.3.0;sys_platform!='win32'"
"ipython>=8.20.0",
"numpy[foo,bar]>=2.0.0",
"scikit-learn>=1.4.0,<1.5;sys_platform=='win32'",
"scikit-learn>=1.4.0;sys_platform!='win32'"
]
[build-system]
build-backend = "hatchling.build"
Expand All @@ -20,15 +20,15 @@ platforms = ["linux-64"]

[tool.pixi.pypi-dependencies]
tests = { path = ".", editable = true }
scikit-learn = ">=1.3.0"
scikit-learn = ">=1.4.0"

[tool.pixi.tasks]

[tool.pixi.feature.foo.dependencies]
xarray = ">=2023.1.0"
xarray = ">=2024.1.0"

[tool.pixi.environments]
bar = ["foo"]

[tool.pixi.dependencies]
numpy = ">=1.25.0,<2"
numpy = ">=2.0.0,<2"
14 changes: 7 additions & 7 deletions tests/test_data/pyproject_updated.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ build-backend = "setuptools.build_meta"
[project]
name = "setuptools_test"
version = "0.1.0"
requires-python = ">=3.11"
requires-python = ">=3.12"
dependencies = [
'pandas>=1.0.0,<3',
'xarray>=2023.1.0',
"ipython>=8.8.0,<4",
"numpy[foo,bar]>=1.25.0,<2",
"scikit-learn>=1.3.0,<1.4;sys_platform=='win32'",
"scikit-learn>=1.3.0;sys_platform!='win32'"
'pandas>=2.2.0,<3',
'xarray>=2024.1.0',
"ipython>=8.20.0",
"numpy[foo,bar]>=2.0.0",
"scikit-learn>=1.4.0,<1.5;sys_platform=='win32'",
"scikit-learn>=1.4.0;sys_platform!='win32'"
]
19 changes: 18 additions & 1 deletion tests/test_update_pyproject_toml.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
import datetime

import pytest

from spec0_action.parsing import read_schedule, read_toml
from spec0_action import update_pyproject_toml

# Fixed time to avoid test results changing over time...
FAKE_TIME = datetime.datetime(2025, 10, 30, 0, 0, 0, tzinfo=datetime.UTC)

@pytest.fixture
def patch_datetime_now(monkeypatch):

class mydatetime(datetime.datetime):
@classmethod
def now(cls, *args, **kwds):
return FAKE_TIME

monkeypatch.setattr(datetime, 'datetime', mydatetime)


def test_update_pyproject_toml():
def test_update_pyproject_toml(patch_datetime_now):
expected = read_toml("tests/test_data/pyproject_updated.toml")
pyproject_data = read_toml("tests/test_data/pyproject.toml")
test_schedule = read_schedule("tests/test_data/test_schedule.json")
Expand Down