Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix pixi using PIXI_PROJECT_MANIFEST instead of the nearest pixi.toml during manifest discovery #36

Merged
merged 7 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
pixi-version: ["0.30.0", "0.34.0"]
pixi-version: ["0.30.0", "0.38.0"]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repo
Expand All @@ -25,13 +25,13 @@ jobs:
run-install: false

- name: Install uv
uses: astral-sh/setup-uv@v3
uses: astral-sh/setup-uv@v4

- name: Test, lint and typecheck
run: uv run tox

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v3
uses: astral-sh/setup-uv@v4

- name: Build release
run: uv build
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ uv run tox run -e type_check

1. Bump
1. Increment version in `pyproject.toml`
2. Update all Pixi lock files by running `uv sync`
2. Update all lock files by running `uv sync -U` and `pixi update`
3. Commit with message "chore: Bump version number to X.Y.Z"
4. Push commit to GitHub
5. Check [CI](https://github.com/renan-r-santos/pixi-kernel/actions/workflows/ci.yml) to ensure
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pixi-kernel"
version = "0.5.1"
version = "0.5.2"
description = "Jupyter kernels using Pixi for reproducible notebooks"
license = { text = "MIT" }
authors = [
Expand Down Expand Up @@ -43,8 +43,8 @@ dev-dependencies = [
"mypy>=1,<2",
"pytest>=8,<9",
"pytest-asyncio>=0.24,<0.25",
"pytest-cov>=5,<6",
"ruff>=0.6,<0.7",
"pytest-cov>=6,<7",
"ruff>=0.8,<0.9",
"tox-uv>=1,<2",
]

Expand Down
3 changes: 3 additions & 0 deletions src/pixi_kernel/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import logging

logging.basicConfig(level=logging.INFO, format="pixi-kernel %(levelname)s: %(message)s")
4 changes: 3 additions & 1 deletion src/pixi_kernel/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
PIXI_KERNEL_NOT_FOUND = """
To run the {kernel_name} kernel, you need to add the {required_package} package to
your project dependencies. You can do this by running 'pixi add {required_package}'
in your project directory and restarting your kernel.
in your project directory and restarting your kernel. Make sure the prefix
{prefix}
points to the correct Pixi environment.
If you continue to face issues, report them at https://github.com/renan-r-santos/pixi-kernel/issues
"""
28 changes: 22 additions & 6 deletions src/pixi_kernel/pixi.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import logging
import shutil
import subprocess
from pathlib import Path
Expand All @@ -11,6 +12,8 @@

MINIMUM_PIXI_VERSION = "0.30.0"

logger = logging.getLogger(__name__)


class PixiInfo(BaseModel):
environments: list[Environment] = Field(alias="environments_info")
Expand All @@ -28,7 +31,13 @@ class Project(BaseModel):
manifest_path: str


def ensure_readiness(*, cwd: Path, required_package: str, kernel_name: str) -> Environment:
def ensure_readiness(
*,
cwd: Path,
env: dict[str, str],
required_package: str,
kernel_name: str,
) -> Environment:
"""Ensure the Pixi environment is ready to run the kernel.

This function checks the following:
Expand All @@ -48,7 +57,7 @@ def ensure_readiness(*, cwd: Path, required_package: str, kernel_name: str) -> E
raise RuntimeError(PIXI_NOT_FOUND.format(kernel_name=kernel_name))

# Ensure a supported Pixi version is installed
result = subprocess.run(["pixi", "--version"], capture_output=True, text=True)
result = subprocess.run(["pixi", "--version"], capture_output=True, env=env, text=True)
if result.returncode != 0 or not result.stdout.startswith("pixi "):
raise RuntimeError(PIXI_VERSION_ERROR.format(kernel_name=kernel_name))

Expand All @@ -68,8 +77,11 @@ def ensure_readiness(*, cwd: Path, required_package: str, kernel_name: str) -> E
["pixi", "info", "--json"],
cwd=str(cwd.absolute()),
renan-r-santos marked this conversation as resolved.
Show resolved Hide resolved
capture_output=True,
env=env,
text=True,
)
logger.info(f"pixi info stderr: {result.stderr}")
logger.info(f"pixi info stdout: {result.stdout}")
if result.returncode != 0:
raise RuntimeError(f"Failed to run 'pixi info': {result.stderr}")

Expand All @@ -87,14 +99,15 @@ def ensure_readiness(*, cwd: Path, required_package: str, kernel_name: str) -> E
["pixi", "project", "version", "get"],
cwd=str(cwd.absolute()),
renan-r-santos marked this conversation as resolved.
Show resolved Hide resolved
capture_output=True,
env=env,
text=True,
)
raise RuntimeError(result.stderr)

# Find the default environment and check if the required kernel package is a dependency
for env in pixi_info.environments:
if env.name == "default":
default_environment = env
for pixi_env in pixi_info.environments:
if pixi_env.name == "default":
default_environment = pixi_env
break
else:
raise RuntimeError("Default Pixi environment not found.")
Expand All @@ -103,7 +116,9 @@ def ensure_readiness(*, cwd: Path, required_package: str, kernel_name: str) -> E
if required_package not in dependencies:
raise RuntimeError(
PIXI_KERNEL_NOT_FOUND.format(
kernel_name=kernel_name, required_package=required_package
kernel_name=kernel_name,
required_package=required_package,
prefix=default_environment.prefix,
)
)

Expand All @@ -112,6 +127,7 @@ def ensure_readiness(*, cwd: Path, required_package: str, kernel_name: str) -> E
["pixi", "install"],
cwd=str(cwd.absolute()),
renan-r-santos marked this conversation as resolved.
Show resolved Hide resolved
capture_output=True,
env=env,
text=True,
)
if result.returncode != 0:
Expand Down
13 changes: 11 additions & 2 deletions src/pixi_kernel/provisioner.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from logging import Logger
import logging
import os
from pathlib import Path
from typing import Any, Optional, cast

Expand All @@ -7,14 +8,15 @@

from .pixi import ensure_readiness

logger = logging.getLogger(__name__)


class PixiKernelProvisioner(LocalProvisioner): # type: ignore
async def pre_launch(self, **kwargs: Any) -> dict[str, Any]:
"""Perform any steps in preparation for kernel process launch.

This includes ensuring Pixi is installed and that a Pixi project is available.
"""
logger = cast(Logger, self.log)
kernel_spec = cast(KernelSpec, self.kernel_spec)

kernel_metadata: Optional[dict[str, str]] = kernel_spec.metadata.get("pixi-kernel")
Expand All @@ -29,10 +31,17 @@ async def pre_launch(self, **kwargs: Any) -> dict[str, Any]:
raise ValueError("Pixi Kernel metadata is missing the 'required-package' key")

cwd = Path(kwargs.get("cwd", Path.cwd()))
logger.info(f"JupyterLab provided this value for cwd: {kwargs.get('cwd', None)}")
logger.info(f"The current working directory is {cwd}")

# Remove PIXI_IN_SHELL for when JupyterLab is started from a Pixi shell
# https://github.com/renan-r-santos/pixi-kernel/issues/35
env: dict[str, str] = kwargs.get("env", os.environ)
env.pop("PIXI_IN_SHELL", None)

environment = ensure_readiness(
cwd=cwd,
env=env,
required_package=required_package,
kernel_name=kernel_spec.display_name,
)
Expand Down
Loading