Skip to content

Commit cd4b876

Browse files
committed
Allow to have some default ipywidgets version
1 parent 72d794e commit cd4b876

1 file changed

Lines changed: 67 additions & 0 deletions

File tree

‎.tools/python/merge_requirements.py‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@
44
This updated script resolves simple version conflicts by choosing the newest version seen
55
for a given package (and emits a pinned `pkg==<newest>`). It still preserves
66
non-package lines (pip options, VCS/URLs) and supports simple `-r` includes.
7+
8+
It also automatically adds ipywidgets if missing, with a version determined by
9+
Python, JupyterLab, and matplotlib versions from environment.yaml.
710
"""
811
from pathlib import Path
912
import argparse
1013
import sys
1114
from collections import OrderedDict
1215
from datetime import datetime
1316
import re
17+
import yaml
1418

1519

1620
def normalize_line(line: str) -> str:
@@ -129,6 +133,50 @@ def find_requirements_files(source_dir: Path):
129133
return sorted(source_dir.rglob("requirements*.txt"))
130134

131135

136+
def read_environment_yaml(env_path: Path) -> dict:
137+
"""Read environment.yaml and extract Python, JupyterLab, matplotlib versions."""
138+
if not env_path.exists():
139+
return {}
140+
try:
141+
data = yaml.safe_load(env_path.read_text(encoding="utf-8"))
142+
versions = {}
143+
for dep in data.get("dependencies", []):
144+
if isinstance(dep, str):
145+
if dep.startswith("python="):
146+
versions["python"] = dep.split("=")[-1]
147+
elif dep.startswith("jupyterlab="):
148+
versions["jupyterlab"] = dep.split("=")[-1]
149+
elif dep.startswith("matplotlib="):
150+
versions["matplotlib"] = dep.split("=")[-1]
151+
return versions
152+
except Exception:
153+
return {}
154+
155+
156+
def resolve_ipywidgets_version(env_versions: dict) -> str:
157+
"""Resolve ipywidgets version based on Python, JupyterLab, matplotlib versions.
158+
159+
Returns a version spec string like ">=7.0.0,<8.0.0" or a pinned version.
160+
"""
161+
py_ver = env_versions.get("python", "3.11")
162+
jl_ver = env_versions.get("jupyterlab", "4.0")
163+
mpl_ver = env_versions.get("matplotlib", "3.0")
164+
165+
# Simple heuristic: ipywidgets 8.x for modern stacks, 7.x for older
166+
try:
167+
py_major, py_minor = map(int, py_ver.split(".")[:2])
168+
jl_major = int(jl_ver.split(".")[0])
169+
170+
if jl_major >= 4 and py_major >= 3 and py_minor >= 9:
171+
return "8.1.1" # Latest stable for modern Python + JupyterLab 4
172+
elif py_major >= 3 and py_minor >= 8:
173+
return "8.0.4" # Good for Python 3.8+
174+
else:
175+
return "7.7.2" # Fallback for older environments
176+
except Exception:
177+
return ">=7.0.0" # Safe fallback
178+
179+
132180
def main():
133181
parser = argparse.ArgumentParser(description="Merge multiple requirements.txt files into one.")
134182
parser.add_argument("--source-dir", default="notebooks", help="Directory to scan for requirements files")
@@ -163,6 +211,25 @@ def main():
163211
for ol in other_lines.keys():
164212
final_lines.append(ol)
165213

214+
# Check if ipywidgets is already present
215+
has_ipywidgets = "ipywidgets" in pkgs
216+
217+
# If not, resolve and add it based on environment.yaml
218+
if not has_ipywidgets:
219+
env_path = Path(args.env_output) if args.generate_env else Path("environment.yaml")
220+
env_versions = read_environment_yaml(env_path)
221+
ipyw_version = resolve_ipywidgets_version(env_versions)
222+
223+
# Add to pkgs and pkg_order
224+
pkgs["ipywidgets"] = {
225+
"name": "ipywidgets",
226+
"pinned": True,
227+
"ver": ipyw_version,
228+
"marker": ""
229+
}
230+
pkg_order.append("ipywidgets")
231+
print(f"Added ipywidgets=={ipyw_version} (resolved from environment)")
232+
166233
# collect package lines
167234
if args.sort:
168235
pkg_keys = sorted(pkg_order, key=str.casefold)

0 commit comments

Comments
 (0)