Skip to content

Commit 91f40a5

Browse files
hjmjohnsonclaude
andcommitted
ENH: Dynamically update ITK dependency pins in remote module builds
Remote modules hard-code ITK sub-package version pins in their pyproject.toml (e.g., itk-io == 5.4.*). When building against ITK 6, these pins cause pip install conflicts. Add _update_module_itk_deps() which rewrites exact ITK pins to minimum version constraints (>= 5.4) before invoking scikit-build-core. This runs automatically during build_external_module_python_wheel() when ITK_PACKAGE_VERSION is set, so remote module wheels always have dependency metadata compatible with the ITK version they were built against. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d136916 commit 91f40a5

File tree

1 file changed

+63
-0
lines changed

1 file changed

+63
-0
lines changed

scripts/build_python_instance_base.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,12 +710,75 @@ def discover_python_venvs(
710710
"""
711711
pass
712712

713+
@staticmethod
714+
def _update_module_itk_deps(pyproject_path: Path, itk_version: str) -> bool:
715+
"""Rewrite ITK dependency pins in a remote module's pyproject.toml.
716+
717+
Replaces hard-coded ITK sub-package version pins (e.g.
718+
``itk-io == 5.4.*``) with a pin matching the ITK version being
719+
built against (e.g. ``itk-io >= 5.4``). This ensures that
720+
wheels produced for ITK 6 can be installed alongside ITK 6
721+
packages without pip dependency conflicts.
722+
723+
Parameters
724+
----------
725+
pyproject_path : Path
726+
Path to the module's ``pyproject.toml``.
727+
itk_version : str
728+
The ITK PEP 440 version string being built (e.g. ``6.0.0b2``).
729+
Used to compute the minimum major version for the ``>=`` pin.
730+
731+
Returns
732+
-------
733+
bool
734+
*True* if any dependency was rewritten.
735+
"""
736+
import re
737+
738+
text = pyproject_path.read_text(encoding="utf-8")
739+
# Match lines like: "itk-core == 5.4.*" or "itk-filtering==5.4.*"
740+
pattern = re.compile(
741+
r'"(itk-[a-z]+)\s*==\s*[\d]+\.[\d]+\.\*"'
742+
)
743+
744+
# Determine the minimum version floor from the ITK version being built.
745+
# For "6.0.0b2.post757" -> major "6", floor "5.4" (backward compat).
746+
# For "5.4.0" -> floor "5.4".
747+
try:
748+
major = int(itk_version.split(".")[0])
749+
except (ValueError, IndexError):
750+
major = 5
751+
min_floor = "5.4" if major >= 5 else itk_version.rsplit(".", 1)[0]
752+
753+
changed = False
754+
def _replace(m: re.Match) -> str:
755+
nonlocal changed
756+
changed = True
757+
pkg = m.group(1)
758+
return f'"{pkg} >= {min_floor}"'
759+
760+
new_text = pattern.sub(_replace, text)
761+
if changed:
762+
pyproject_path.write_text(new_text, encoding="utf-8")
763+
print(
764+
f"Updated ITK dependency pins in {pyproject_path} "
765+
f"(>= {min_floor} for ITK {itk_version})"
766+
)
767+
return changed
768+
713769
def build_external_module_python_wheel(self):
714770
"""Build a wheel for an external ITK remote module via scikit-build-core."""
715771
self.module_source_dir = Path(self.module_source_dir)
716772
out_dir = self.module_source_dir / "dist"
717773
out_dir.mkdir(parents=True, exist_ok=True)
718774

775+
# Dynamically update ITK dependency pins to match the version being built
776+
module_pyproject = self.module_source_dir / "pyproject.toml"
777+
if module_pyproject.is_file():
778+
itk_ver = self.package_env_config.get("ITK_PACKAGE_VERSION", "")
779+
if itk_ver:
780+
self._update_module_itk_deps(module_pyproject, itk_ver)
781+
719782
# Ensure venv tools are first in PATH
720783
py_exe = str(self.package_env_config["PYTHON_EXECUTABLE"]) # Python3_EXECUTABLE
721784

0 commit comments

Comments
 (0)