Skip to content

Commit 7ff0002

Browse files
committed
Migrate env compat checker to packages and constraints
1 parent cc0480d commit 7ff0002

9 files changed

Lines changed: 477 additions & 124 deletions

File tree

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ jobs:
101101
| Input | Description | Default |
102102
|-------|-------------|---------|
103103
| `project-file` | Path to pyproject.toml | `pyproject.toml` |
104-
| `pyhc-requirements-url` | URL to PyHC requirements.txt | (official GitHub URL) |
104+
| `pyhc-packages-url` | URL to PyHC packages.txt | (official GitHub URL) |
105+
| `pyhc-constraints-url` | URL to PyHC constraints.txt | (official GitHub URL) |
105106
| `extras` | Extras selection: `auto`, `none`, or comma-separated list | `auto` |
106107

107108
#### Outputs
@@ -153,8 +154,9 @@ pyhc-env-compat-check --extras auto pyproject.toml
153154
pyhc-env-compat-check --extras none pyproject.toml
154155
pyhc-env-compat-check --extras mth5,vires pyproject.toml
155156

156-
# Use a local requirements.txt or alternate URL
157-
pyhc-env-compat-check --requirements ./requirements.txt pyproject.toml
157+
# Use local packages/constraints files or alternate URLs
158+
pyhc-env-compat-check --packages ./packages.txt pyproject.toml
159+
pyhc-env-compat-check --constraints ./constraints.txt pyproject.toml
158160

159161
# Only check that uv is installed
160162
pyhc-env-compat-check --check-uv

pyhc-env-compat/action.yml

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ inputs:
1212
description: "Path to pyproject.toml file"
1313
required: false
1414
default: "pyproject.toml"
15-
pyhc-requirements-url:
16-
description: "URL to PyHC Environment requirements.txt"
15+
pyhc-packages-url:
16+
description: "URL to PyHC Environment packages.txt"
1717
required: false
18-
default: "https://raw.githubusercontent.com/heliophysicsPy/pyhc-docker-environment/main/docker/pyhc-environment/contents/requirements.txt"
18+
default: "https://raw.githubusercontent.com/heliophysicsPy/pyhc-docker-environment/main-v2/docker/pyhc-environment/contents/packages.txt"
19+
pyhc-constraints-url:
20+
description: "URL to PyHC Environment constraints.txt"
21+
required: false
22+
default: "https://raw.githubusercontent.com/heliophysicsPy/pyhc-docker-environment/main-v2/docker/pyhc-environment/contents/constraints.txt"
1923
extras:
2024
description: "Extras selection: auto | none | comma-separated list (e.g., mth5,vires)"
2125
required: false
@@ -72,17 +76,22 @@ runs:
7276
fi
7377
fi
7478
75-
REQUIREMENTS_ARG=""
76-
if [ -n "${{ inputs.pyhc-requirements-url }}" ]; then
77-
REQUIREMENTS_ARG="--requirements ${{ inputs.pyhc-requirements-url }}"
79+
PACKAGES_ARG=""
80+
if [ -n "${{ inputs.pyhc-packages-url }}" ]; then
81+
PACKAGES_ARG="--packages ${{ inputs.pyhc-packages-url }}"
82+
fi
83+
84+
CONSTRAINTS_ARG=""
85+
if [ -n "${{ inputs.pyhc-constraints-url }}" ]; then
86+
CONSTRAINTS_ARG="--constraints ${{ inputs.pyhc-constraints-url }}"
7887
fi
7988
8089
EXTRAS_ARG=""
8190
if [ -n "${{ inputs.extras }}" ]; then
8291
EXTRAS_ARG="--extras ${{ inputs.extras }}"
8392
fi
8493
85-
python -m pyhc_actions.env_compat.main $REQUIREMENTS_ARG $EXTRAS_ARG "${{ inputs.project-file }}"
94+
python -m pyhc_actions.env_compat.main $PACKAGES_ARG $CONSTRAINTS_ARG $EXTRAS_ARG "${{ inputs.project-file }}"
8695
EXIT_CODE=$?
8796
8897
if [ $EXIT_CODE -eq 0 ]; then
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
"""PyHC Environment compatibility checker."""
22

33
from pyhc_actions.env_compat.uv_resolver import check_compatibility
4-
from pyhc_actions.env_compat.fetcher import fetch_pyhc_requirements
4+
from pyhc_actions.env_compat.fetcher import (
5+
fetch_pyhc_packages,
6+
fetch_pyhc_constraints,
7+
)
58

69
__all__ = [
710
"check_compatibility",
8-
"fetch_pyhc_requirements",
11+
"fetch_pyhc_packages",
12+
"fetch_pyhc_constraints",
913
]

src/pyhc_actions/env_compat/fetcher.py

Lines changed: 80 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,87 @@
1-
"""Fetch PyHC Environment requirements."""
1+
"""Fetch PyHC Environment package and constraint files."""
22

33
from __future__ import annotations
44

55
import re
66
from pathlib import Path
7-
from typing import TYPE_CHECKING
7+
from typing import Callable
88

99
import requests
1010
import yaml
1111

12-
if TYPE_CHECKING:
13-
pass
14-
12+
# Default URL to PyHC Environment packages.txt
13+
PYHC_PACKAGES_URL = (
14+
"https://raw.githubusercontent.com/heliophysicsPy/pyhc-docker-environment/"
15+
"main-v2/docker/pyhc-environment/contents/packages.txt"
16+
)
1517

16-
# Default URL to PyHC Environment requirements.txt
17-
PYHC_REQUIREMENTS_URL = (
18+
# Default URL to PyHC Environment constraints.txt
19+
PYHC_CONSTRAINTS_URL = (
1820
"https://raw.githubusercontent.com/heliophysicsPy/pyhc-docker-environment/"
19-
"main/docker/pyhc-environment/contents/requirements.txt"
21+
"main-v2/docker/pyhc-environment/contents/constraints.txt"
2022
)
2123

2224
# Default URL to PyHC Environment environment.yml (conda environment file)
2325
PYHC_ENVIRONMENT_YML_URL = (
2426
"https://raw.githubusercontent.com/heliophysicsPy/pyhc-docker-environment/"
25-
"main/docker/pyhc-environment/contents/environment.yml"
27+
"main-v2/docker/pyhc-environment/contents/environment.yml"
2628
)
2729

2830

29-
def fetch_pyhc_requirements(url: str | None = None) -> str:
30-
"""Fetch PyHC Environment requirements.txt content.
31+
def fetch_pyhc_packages(url: str | None = None) -> str:
32+
"""Fetch PyHC Environment packages.txt content.
3133
3234
Args:
3335
url: URL to fetch from (default: official GitHub raw URL)
3436
3537
Returns:
36-
Contents of requirements.txt as string
38+
Contents of packages.txt as string
3739
3840
Raises:
3941
requests.RequestException: If fetch fails
4042
"""
41-
url = url or PYHC_REQUIREMENTS_URL
43+
url = url or PYHC_PACKAGES_URL
4244

4345
response = requests.get(url, timeout=30)
4446
response.raise_for_status()
4547

4648
return response.text
4749

4850

49-
def parse_requirements_for_uv(requirements_text: str) -> list[str]:
50-
"""Parse requirements.txt and return list suitable for uv.
51+
def fetch_pyhc_constraints(url: str | None = None) -> str:
52+
"""Fetch PyHC Environment constraints.txt content.
53+
54+
Args:
55+
url: URL to fetch from (default: official GitHub raw URL)
56+
57+
Returns:
58+
Contents of constraints.txt as string
59+
60+
Raises:
61+
requests.RequestException: If fetch fails
62+
"""
63+
url = url or PYHC_CONSTRAINTS_URL
64+
65+
response = requests.get(url, timeout=30)
66+
response.raise_for_status()
67+
68+
return response.text
69+
70+
71+
def parse_package_specs_for_uv(raw_text: str) -> list[str]:
72+
"""Parse package-spec text and return entries suitable for uv.
5173
5274
Filters out comments, blank lines, and incompatible lines.
5375
5476
Args:
55-
requirements_text: Raw requirements.txt content
77+
raw_text: Raw text from packages.txt or constraints.txt
5678
5779
Returns:
58-
List of requirement strings
80+
List of package spec strings
5981
"""
60-
requirements = []
82+
package_specs = []
6183

62-
for line in requirements_text.split("\n"):
84+
for line in raw_text.split("\n"):
6385
line = line.strip()
6486

6587
# Skip empty lines and comments
@@ -74,39 +96,57 @@ def parse_requirements_for_uv(requirements_text: str) -> list[str]:
7496
if line.startswith(".") or line.startswith("/"):
7597
continue
7698

77-
requirements.append(line)
99+
package_specs.append(line)
100+
101+
return package_specs
78102

79-
return requirements
80103

104+
def _load_from_source(
105+
source: str | Path | None,
106+
fetcher: Callable[[str | None], str],
107+
) -> str:
108+
"""Load text from URL or local file."""
109+
if source is None:
110+
return fetcher()
81111

82-
def load_pyhc_requirements(
83-
source: str | Path | None = None,
84-
) -> list[str]:
85-
"""Load PyHC requirements from URL or local file.
112+
if isinstance(source, Path) or (
113+
isinstance(source, str) and not source.startswith("http")
114+
):
115+
path = Path(source)
116+
with open(path) as f:
117+
return f.read()
118+
119+
return fetcher(str(source))
120+
121+
122+
def load_pyhc_packages(source: str | Path | None = None) -> list[str]:
123+
"""Load PyHC packages from URL or local file.
86124
87125
Args:
88-
source: URL or path to requirements file (None uses default URL)
126+
source: URL or path to packages file (None uses default URL)
89127
90128
Returns:
91-
List of requirement strings
129+
List of package spec strings
92130
"""
93-
if source is None:
94-
# Fetch from default URL
95-
text = fetch_pyhc_requirements()
96-
elif isinstance(source, Path) or (isinstance(source, str) and not source.startswith("http")):
97-
# Load from local file
98-
path = Path(source)
99-
with open(path) as f:
100-
text = f.read()
101-
else:
102-
# Fetch from URL
103-
text = fetch_pyhc_requirements(str(source))
131+
text = _load_from_source(source, fetch_pyhc_packages)
132+
return parse_package_specs_for_uv(text)
133+
134+
135+
def load_pyhc_constraints(source: str | Path | None = None) -> list[str]:
136+
"""Load PyHC constraints from URL or local file.
104137
105-
return parse_requirements_for_uv(text)
138+
Args:
139+
source: URL or path to constraints file (None uses default URL)
140+
141+
Returns:
142+
List of constraint spec strings
143+
"""
144+
text = _load_from_source(source, fetch_pyhc_constraints)
145+
return parse_package_specs_for_uv(text)
106146

107147

108148
def get_package_from_pyproject(pyproject_path: Path | str) -> str:
109-
"""Get package directory path for use in requirements.
149+
"""Get package directory path for local editable install specs.
110150
111151
Handles both file paths (pyproject.toml) and directory paths (for setup.py
112152
packages where main.py passes the project directory directly).

src/pyhc_actions/env_compat/main.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
discover_optional_extras,
1515
)
1616
from pyhc_actions.env_compat.fetcher import (
17-
PYHC_REQUIREMENTS_URL,
18-
load_pyhc_requirements,
17+
PYHC_PACKAGES_URL,
18+
PYHC_CONSTRAINTS_URL,
19+
load_pyhc_packages,
20+
load_pyhc_constraints,
1921
get_pyhc_python_version,
2022
)
2123

@@ -36,7 +38,8 @@ def main(args: list[str] | None = None) -> int:
3638
Examples:
3739
%(prog)s # Check ./pyproject.toml
3840
%(prog)s path/to/pyproject.toml # Check specific file
39-
%(prog)s --requirements local.txt # Use local requirements file
41+
%(prog)s --packages local.txt # Use local packages file
42+
%(prog)s --constraints local.txt # Use local constraints file
4043
""",
4144
)
4245

@@ -48,10 +51,17 @@ def main(args: list[str] | None = None) -> int:
4851
)
4952

5053
parser.add_argument(
51-
"--requirements",
52-
"-r",
54+
"--packages",
55+
"-p",
5356
default=None,
54-
help=f"Path or URL to PyHC requirements.txt (default: {PYHC_REQUIREMENTS_URL})",
57+
help=f"Path or URL to PyHC packages.txt (default: {PYHC_PACKAGES_URL})",
58+
)
59+
60+
parser.add_argument(
61+
"--constraints",
62+
"-c",
63+
default=None,
64+
help=f"Path or URL to PyHC constraints.txt (default: {PYHC_CONSTRAINTS_URL})",
5565
)
5666

5767
parser.add_argument(
@@ -102,13 +112,25 @@ def main(args: list[str] | None = None) -> int:
102112
reporter = Reporter(title="PyHC Environment Compatibility Check")
103113
reporter.set_file_path(str(project_path))
104114

105-
# Pre-load PyHC requirements once to avoid repeated downloads
115+
# Pre-load PyHC packages once to avoid repeated downloads
116+
try:
117+
pyhc_packages = load_pyhc_packages(parsed_args.packages)
118+
except Exception as e:
119+
reporter.add_error(
120+
package="pyhc-packages",
121+
message=f"Failed to load PyHC packages: {e}",
122+
context="base",
123+
)
124+
reporter.print_report()
125+
reporter.write_github_summary()
126+
return 1
127+
106128
try:
107-
pyhc_requirements = load_pyhc_requirements(parsed_args.requirements)
129+
pyhc_constraints = load_pyhc_constraints(parsed_args.constraints)
108130
except Exception as e:
109131
reporter.add_error(
110-
package="pyhc-requirements",
111-
message=f"Failed to load PyHC requirements: {e}",
132+
package="pyhc-constraints",
133+
message=f"Failed to load PyHC constraints: {e}",
112134
context="base",
113135
)
114136
reporter.print_report()
@@ -150,7 +172,8 @@ def main(args: list[str] | None = None) -> int:
150172
# Always run base check
151173
is_compatible, conflicts = check_compatibility(
152174
pyproject_path=project_path,
153-
pyhc_requirements=pyhc_requirements,
175+
pyhc_packages=pyhc_packages,
176+
pyhc_constraints=pyhc_constraints,
154177
pyhc_python=pyhc_python,
155178
extra=None,
156179
context="base",
@@ -164,7 +187,8 @@ def main(args: list[str] | None = None) -> int:
164187
for extra in extras_to_check:
165188
is_compatible, conflicts = check_compatibility(
166189
pyproject_path=project_path,
167-
pyhc_requirements=pyhc_requirements,
190+
pyhc_packages=pyhc_packages,
191+
pyhc_constraints=pyhc_constraints,
168192
pyhc_python=pyhc_python,
169193
extra=extra,
170194
context=extra,

0 commit comments

Comments
 (0)