Skip to content

Commit 8402ebf

Browse files
committed
feat: Add local package support to pixi.toml generation
When generating pixi.toml files, UniDep now automatically detects local pip-installable packages (those with setup.py or pyproject.toml with [build-system]) and adds them as editable dependencies. This allows Pixi to install the local packages in development mode, similar to 'pip install -e .'. Changes: - Use is_pip_installable() to detect local packages - Add local packages as editable dependencies in pypi-dependencies - For single packages: use path='.' - For monorepos: use path='./feature_name' for each feature - Add test to verify local package detection and inclusion
1 parent 86403dd commit 8402ebf

File tree

2 files changed

+80
-2
lines changed

2 files changed

+80
-2
lines changed

tests/test_pixi.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,57 @@ def test_pixi_with_version_pins(tmp_path: Path) -> None:
161161
assert 'sympy = ">=1.11"' in content # Space should be removed
162162

163163

164+
def test_pixi_with_local_package(tmp_path: Path) -> None:
165+
"""Test that local packages are added as editable dependencies."""
166+
# Create a directory with requirements.yaml and pyproject.toml
167+
project_dir = tmp_path / "my_package"
168+
project_dir.mkdir()
169+
170+
req_file = project_dir / "requirements.yaml"
171+
req_file.write_text(
172+
textwrap.dedent(
173+
"""\
174+
channels:
175+
- conda-forge
176+
dependencies:
177+
- numpy
178+
""",
179+
),
180+
)
181+
182+
# Create a pyproject.toml with build-system to simulate a local package
183+
pyproject_file = project_dir / "pyproject.toml"
184+
pyproject_file.write_text(
185+
textwrap.dedent(
186+
"""\
187+
[build-system]
188+
requires = ["setuptools"]
189+
190+
[project]
191+
name = "my-package"
192+
""",
193+
),
194+
)
195+
196+
output_file = tmp_path / "pixi.toml"
197+
generate_pixi_toml(
198+
project_dir,
199+
output_file=output_file,
200+
verbose=False,
201+
)
202+
203+
assert output_file.exists()
204+
content = output_file.read_text()
205+
206+
# Check that the local package is added as an editable dependency
207+
# TOML can format this as either inline or table format
208+
assert "pypi-dependencies" in content
209+
assert "my_package" in content
210+
assert 'path = "."' in content
211+
assert "editable = true" in content
212+
assert 'numpy = "*"' in content
213+
214+
164215
def test_pixi_empty_dependencies(tmp_path: Path) -> None:
165216
"""Test handling of requirements file with no dependencies."""
166217
req_file = tmp_path / "requirements.yaml"

unidep/_pixi.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from typing import TYPE_CHECKING, Any
88

99
from unidep._dependencies_parsing import parse_requirements
10-
from unidep.utils import identify_current_platform
10+
from unidep.utils import identify_current_platform, is_pip_installable
1111

1212
if TYPE_CHECKING:
1313
from unidep._dependencies_parsing import ParsedRequirements
@@ -23,7 +23,7 @@
2323
HAS_TOML = False
2424

2525

26-
def generate_pixi_toml( # noqa: PLR0912
26+
def generate_pixi_toml( # noqa: PLR0912, C901, PLR0915
2727
*requirements_files: Path,
2828
project_name: str | None = None,
2929
channels: list[str] | None = None,
@@ -61,6 +61,20 @@ def generate_pixi_toml( # noqa: PLR0912
6161
pixi_data["dependencies"] = conda_deps
6262
if pip_deps:
6363
pixi_data["pypi-dependencies"] = pip_deps
64+
65+
# Check if there's a local package in the same directory
66+
req_file = requirements_files[0]
67+
req_dir = req_file.parent if req_file.is_file() else req_file
68+
if is_pip_installable(req_dir):
69+
# Add the local package as an editable dependency
70+
if "pypi-dependencies" not in pixi_data:
71+
pixi_data["pypi-dependencies"] = {}
72+
# For single package, use current directory
73+
package_name = req_dir.name
74+
pixi_data["pypi-dependencies"][package_name] = {
75+
"path": ".",
76+
"editable": True,
77+
}
6478
else:
6579
# Multiple files: create features
6680
pixi_data["feature"] = {}
@@ -84,6 +98,19 @@ def generate_pixi_toml( # noqa: PLR0912
8498
if pip_deps:
8599
feature["pypi-dependencies"] = pip_deps
86100

101+
# Check if there's a local package in the same directory
102+
req_dir = req_file.parent if req_file.is_file() else req_file
103+
if is_pip_installable(req_dir):
104+
# Add the local package as an editable dependency
105+
if "pypi-dependencies" not in feature:
106+
feature["pypi-dependencies"] = {}
107+
# Use relative path from the output file location
108+
rel_path = f"./{feature_name}"
109+
feature["pypi-dependencies"][feature_name] = {
110+
"path": rel_path,
111+
"editable": True,
112+
}
113+
87114
if feature: # Only add non-empty features
88115
pixi_data["feature"][feature_name] = feature
89116
all_features.append(feature_name)

0 commit comments

Comments
 (0)