Skip to content

Commit ac5cda9

Browse files
committed
Define as features
1 parent cc96122 commit ac5cda9

File tree

1 file changed

+92
-39
lines changed

1 file changed

+92
-39
lines changed

unidep/_pixi.py

Lines changed: 92 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -60,48 +60,101 @@ def _initialize_pixi_data(
6060
return pixi_data
6161

6262

63-
def _process_dependencies(
63+
def group_by_origin(
64+
resolved_deps: dict[str, dict[Platform | None, dict[CondaPip, Spec]]],
65+
) -> dict[Path, dict[str, dict[Platform | None, dict[CondaPip, Spec]]]]:
66+
groups: dict[Path, dict[str, dict[Platform | None, dict[CondaPip, Spec]]]] = {}
67+
for pkg_name, platform_map in resolved_deps.items():
68+
for plat, manager_map in platform_map.items():
69+
for manager, spec in manager_map.items():
70+
for origin in spec.origin:
71+
# Normalize origin to a Path object
72+
origin_path = Path(origin)
73+
groups.setdefault(origin_path, {})
74+
groups[origin_path].setdefault(pkg_name, {})
75+
groups[origin_path][pkg_name].setdefault(plat, {})
76+
groups[origin_path][pkg_name][plat][manager] = spec
77+
return groups
78+
79+
80+
def _process_dependencies( # noqa: PLR0912
6481
pixi_data: dict[str, dict[str, Any]],
6582
resolved_dependencies: dict[str, dict[Platform | None, dict[CondaPip, Spec]]],
6683
) -> None:
67-
# Extract conda and pip dependencies
68-
conda_deps, pip_deps = _extract_conda_pip_dependencies(resolved_dependencies)
69-
70-
# Process conda dependencies
71-
for pkg_name, platform_to_spec in conda_deps.items():
72-
for _platform, spec in platform_to_spec.items():
73-
pin = spec.pin or "*"
74-
if _platform is None:
75-
# Applies to all platforms
76-
pixi_data["dependencies"][pkg_name] = pin
77-
else:
78-
# Platform-specific dependency
79-
# Ensure target section exists
80-
target = pixi_data["target"].setdefault(_platform, {})
81-
deps = target.setdefault("dependencies", {})
82-
deps[pkg_name] = pin
83-
84-
# Process pip dependencies
85-
for pkg_name, platform_to_spec in pip_deps.items():
86-
for _platform, spec in platform_to_spec.items():
87-
pin = spec.pin or "*"
88-
if _platform is None:
89-
# Applies to all platforms
90-
pixi_data["pypi-dependencies"][pkg_name] = pin
91-
else:
92-
# Platform-specific dependency
93-
# Ensure target section exists
94-
target = pixi_data["target"].setdefault(_platform, {})
95-
deps = target.setdefault("pypi-dependencies", {})
96-
deps[pkg_name] = pin
97-
98-
# Remove empty sections if necessary
99-
if not pixi_data["dependencies"]:
100-
del pixi_data["dependencies"]
101-
if not pixi_data["pypi-dependencies"]:
102-
del pixi_data["pypi-dependencies"]
103-
if not pixi_data["target"]:
104-
del pixi_data["target"]
84+
"""Process the resolved dependencies and update the pixi manifest data.
85+
86+
This function first groups the resolved dependencies by origin (using
87+
group_by_origin) and then creates a separate feature (under the "feature"
88+
key in pixi_data) for each origin. The feature name is derived using the
89+
parent directory's stem of the origin file.
90+
91+
After creating the per-origin features, if the manifest does not yet have an
92+
"environments" table, we automatically add one with:
93+
- a "default" environment that includes all features, and
94+
- one environment per feature (with the feature name as the sole member).
95+
"""
96+
# --- Step 1: Group by origin and create per-origin features ---
97+
origin_groups = group_by_origin(resolved_dependencies)
98+
features = pixi_data.setdefault("feature", {})
99+
100+
for origin_path, group_deps in origin_groups.items():
101+
# Derive a feature name from the parent folder of the origin file.
102+
feature_name = origin_path.resolve().parent.stem
103+
104+
# Initialize the feature entry.
105+
feature_entry: dict[str, Any] = {
106+
"dependencies": {},
107+
"pypi-dependencies": {},
108+
"target": {},
109+
}
110+
111+
# Extract conda and pip dependencies from the grouped data.
112+
group_conda, group_pip = _extract_conda_pip_dependencies(group_deps)
113+
114+
# Process conda dependencies for this feature.
115+
for pkg_name, platform_to_spec in group_conda.items():
116+
for _platform, spec in platform_to_spec.items():
117+
pin = spec.pin or "*"
118+
if _platform is None:
119+
feature_entry["dependencies"][pkg_name] = pin
120+
else:
121+
target = feature_entry["target"].setdefault(_platform, {})
122+
deps = target.setdefault("dependencies", {})
123+
deps[pkg_name] = pin
124+
125+
# Process pip dependencies for this feature.
126+
for pkg_name, platform_to_spec in group_pip.items():
127+
for _platform, spec in platform_to_spec.items():
128+
pin = spec.pin or "*"
129+
if _platform is None:
130+
feature_entry["pypi-dependencies"][pkg_name] = pin
131+
else:
132+
target = feature_entry["target"].setdefault(_platform, {})
133+
deps = target.setdefault("pypi-dependencies", {})
134+
deps[pkg_name] = pin
135+
136+
# Remove empty sections.
137+
if not feature_entry["dependencies"]:
138+
del feature_entry["dependencies"]
139+
if not feature_entry["pypi-dependencies"]:
140+
del feature_entry["pypi-dependencies"]
141+
if not feature_entry["target"]:
142+
del feature_entry["target"]
143+
144+
# Save this feature entry.
145+
features[feature_name] = feature_entry
146+
147+
# --- Step 2: Automatically add the environments table if not already defined ---
148+
if "environments" not in pixi_data:
149+
all_features = list(features.keys())
150+
pixi_data["environments"] = {}
151+
# The "default" environment will include all features.
152+
pixi_data["environments"]["default"] = all_features
153+
# Also create one environment per feature.
154+
for feat in all_features:
155+
# Environment names cannot use _, only lowercase letters, digits, and -
156+
name = feat.replace("_", "-")
157+
pixi_data["environments"][name] = [feat]
105158

106159

107160
def _write_pixi_toml(

0 commit comments

Comments
 (0)