Skip to content

Commit

Permalink
feat: support editable installs for pixi build (prefix-dev#2661)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hofer-Julian authored Dec 13, 2024
1 parent 4a4b10c commit e061747
Show file tree
Hide file tree
Showing 9 changed files with 905 additions and 3 deletions.
4 changes: 4 additions & 0 deletions crates/pixi_build_types/src/procedures/conda_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ pub struct CondaBuildParams {
///
/// The directory may not yet exist.
pub work_directory: PathBuf,

/// Whether we want to install the package as editable
// TODO: remove this parameter as soon as we have profiles
pub editable: bool,
}

/// Identifier of an output.
Expand Down
2 changes: 2 additions & 0 deletions src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,8 @@ impl BuildContext {
channel_configuration: ChannelConfiguration {
base_url: self.channel_config.channel_alias.clone(),
},
// only use editable for build path dependencies
editable: source_spec.source.as_path().is_some(),
outputs: Some(vec![CondaOutputIdentifier {
name: Some(source_spec.package_record.name.as_normalized().to_string()),
version: Some(source_spec.package_record.version.version().to_string()),
Expand Down
1 change: 1 addition & 0 deletions src/cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ pub async fn execute(args: Args) -> miette::Result<()> {
base_url: channel_config.channel_alias,
},
outputs: None,
editable: false,
work_directory: work_dir.path().to_path_buf(),
variant_configuration: Some(Default::default()),
},
Expand Down
2 changes: 1 addition & 1 deletion src/lock_file/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl Project {
&self,
options: UpdateLockFileOptions,
) -> miette::Result<LockFileDerivedData<'_>> {
self::update_lock_file(self, options).await
update_lock_file(self, options).await
}

/// Get lockfile without checking
Expand Down
2 changes: 2 additions & 0 deletions tests/data/pixi_build/editable-pyproject/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.pixi
#*.egg-info
778 changes: 778 additions & 0 deletions tests/data/pixi_build/editable-pyproject/pixi.lock

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions tests/data/pixi_build/editable-pyproject/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
[project]
dependencies = []
name = "editable-pyproject"
requires-python = ">= 3.11"
version = "0.1.0"

[build-system]
build-backend = "hatchling.build"
requires = ["hatchling"]

[tool.pixi.project]
channels = ["https://prefix.dev/conda-forge"]
platforms = ["win-64", "linux-64", "osx-64", "osx-arm64"]
preview = ["pixi-build"]

[tool.pixi.host-dependencies]
# To be able to install this pyproject we need to install the dependencies of
# the python build-system defined above. Note that different from the
# pyproject build-system this refers to a conda package instead of a pypi
# package.
hatchling = "==1.26.3"

# The build-system section defines the build system that will be used to turn
# the source code of this package into a conda package. Similarly to the above
# [build-system] section this section instructs pixi which build backend to
# use. The build-backend is an executable that is installed and invoked by
# pixi with the sole purpose to build the package.
[tool.pixi.build-system]
# The name of the build backend to use. This name refers both to the name of
# the package that provides the build backend and the name of the executable
# inside the package that is invoked.
#
# The `build-backend` key also functions as a dependency declaration. At least
# a version specifier must be added.
build-backend = { name = "pixi-build-python", version = "*" }
# These are the conda channels that are used to resolve the dependencies of the
# build backend package.
channels = [
"https://prefix.dev/pixi-build-backends",
"https://prefix.dev/conda-forge",
]

[tool.pixi.dependencies]
editable-pyproject = { path = "." }

[tool.pixi.tasks]
check-editable = "python -c 'import editable_pyproject; editable_pyproject.check_editable()'"

# This section marks the project as a pixi package.
#
# Normally a number of fields would be set here, like the name, version, etc.
# However, since all these fields are already defined in the [project] section
# at the top of this file they are not required.
[tool.pixi.package]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
__version__ = "1.0.0"

import sys
from pathlib import Path
import site


def is_editable() -> bool:
package_name = "editable_pyproject"
for site_package in site.getsitepackages():
egg_link_path = Path(site_package).joinpath(f"_{package_name}.pth")
if egg_link_path.is_file():
return True
return False


def check_editable() -> None:
if is_editable():
print("The package is installed as editable.")
sys.exit(0)
else:
print("The package is not installed as editable.")
sys.exit(1)
42 changes: 40 additions & 2 deletions tests/integration_python/pixi_build/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,13 @@ def test_smokey(pixi: Path, build_data: Path, tmp_pixi_workspace: Path) -> None:
def test_source_change_trigger_rebuild(
pixi: Path, build_data: Path, tmp_pixi_workspace: Path
) -> None:
test_data = build_data.joinpath("simple-pyproject")
project = "simple-pyproject"
test_data = build_data.joinpath(project)

# TODO: Setting the cache dir shouldn't be necessary!
env = {"PIXI_CACHE_DIR": str(tmp_pixi_workspace.joinpath("pixi_cache"))}

target_dir = tmp_pixi_workspace.joinpath("simple-pyproject")
target_dir = tmp_pixi_workspace.joinpath(project)
shutil.copytree(test_data, target_dir)
manifest_path = target_dir.joinpath("pyproject.toml")

Expand Down Expand Up @@ -113,3 +114,40 @@ def test_source_change_trigger_rebuild(
stdout_contains="The version of simple-pyproject is 2.0.0",
env=env,
)


def test_editable_pyproject(pixi: Path, build_data: Path, tmp_pixi_workspace: Path) -> None:
project = "editable-pyproject"
test_data = build_data.joinpath(project)

# TODO: Setting the cache dir shouldn't be necessary!
env = {
"PIXI_CACHE_DIR": str(tmp_pixi_workspace.joinpath("pixi_cache")),
}

target_dir = tmp_pixi_workspace.joinpath(project)
shutil.copytree(test_data, target_dir)
manifest_path = target_dir.joinpath("pyproject.toml")

verify_cli_command(
[
pixi,
"install",
"--manifest-path",
manifest_path,
],
env=env,
)

# Verify that package is installed as editable
verify_cli_command(
[
pixi,
"run",
"--manifest-path",
manifest_path,
"check-editable",
],
env=env,
stdout_contains="The package is installed as editable.",
)

0 comments on commit e061747

Please sign in to comment.