From 1ff5b87b5bffb152f8c7b95e3bf2d552e67a45d9 Mon Sep 17 00:00:00 2001 From: Han Mai Date: Tue, 12 May 2026 19:37:15 +0200 Subject: [PATCH] fix(update_pyproject_dependencies): handle `- pip:` dict deps in yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `load_yaml` iterates `data["dependencies"]` and calls `split_dependency(dependency, "=", allow_no_version=True)` on each entry. When a yaml conda env contains a `- pip:` block (PyPI-only deps installed by conda's pip backend) the entry is a dict (`{"pip": ["pkg==X.Y.Z", ...]}`) and `split_dependency`'s `dependency.replace(" ", "")` raises AttributeError: 'dict' object has no attribute 'replace' This blocks the release workflow for any consumer that has PyPI-only dependencies — for example, pyiron_workflow_lammps (already carrying a local fork of this file in main) and pyiron_workflow_atomistics (which depends on `lz-GB-code` via pip on the v0.0.5 release we just attempted). Patched `load_yaml` to detect a dict entry, look for a `pip:` key, descend into the list, and parse the `==`-pinned entries via the existing `split_dependency` helper. Non-pip dict entries raise a clear ValueError pointing at the offending yaml file and entry. The lammps fix uses `type(dependency) == dict` and `"pip" in dependency.keys()`; cleaned up to `isinstance` and plain `in` to satisfy ruff E721/SIM118 if anyone adds linting to this script later. Tested locally against pyiron_workflow_atomistics' `.ci_support/lower_bound.yml` (16 bounds parsed correctly, including `lz-GB-code==0.1.0` from the `- pip:` block) and a synthetic yaml containing `[{"foobar": ["x"]}]` (raises the new ValueError). Co-Authored-By: Claude Opus 4.7 (1M context) --- .support/update_pyproject_dependencies.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.support/update_pyproject_dependencies.py b/.support/update_pyproject_dependencies.py index 7207d3be..74e7ceac 100644 --- a/.support/update_pyproject_dependencies.py +++ b/.support/update_pyproject_dependencies.py @@ -115,6 +115,24 @@ def load_yaml(yaml_file: str) -> dict: yaml_bounds = {} for dependency in data["dependencies"]: + if isinstance(dependency, dict): + # A YAML `- pip:` block (PyPI-only deps installed by conda's + # pip backend) parses as a dict; descend into its list and + # capture the `==`-pinned entries. Anything without `==` is + # ignored — `load_yaml` only collects exact version bounds. + if "pip" in dependency: + for pip_dependency in dependency["pip"]: + if "==" in pip_dependency: + package, version = split_dependency( + pip_dependency, "==", allow_no_version=True + ) + yaml_bounds[package] = version + else: + raise ValueError( + f"Unsupported dict-typed dependency entry in {yaml_file!r}: " + f"{dependency!r}. Only `pip:` sub-lists are recognised." + ) + continue package, version = split_dependency(dependency, "=", allow_no_version=True) yaml_bounds[package] = version return yaml_bounds