Skip to content

Commit 59186c4

Browse files
Merge pull request #36 from renan-r-santos/fix-pixi-on-pixi
Fix `pixi` using `PIXI_PROJECT_MANIFEST` instead of the nearest `pixi.toml` during manifest discovery
2 parents 054357f + 9c40656 commit 59186c4

File tree

18 files changed

+6585
-4209
lines changed

18 files changed

+6585
-4209
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
fail-fast: false
1313
matrix:
1414
os: [ubuntu-latest, macos-latest, windows-latest]
15-
pixi-version: ["0.30.0", "0.34.0"]
15+
pixi-version: ["0.30.0", "0.38.0"]
1616
runs-on: ${{ matrix.os }}
1717
steps:
1818
- name: Checkout repo
@@ -25,13 +25,13 @@ jobs:
2525
run-install: false
2626

2727
- name: Install uv
28-
uses: astral-sh/setup-uv@v3
28+
uses: astral-sh/setup-uv@v4
2929

3030
- name: Test, lint and typecheck
3131
run: uv run tox
3232

3333
- name: Upload coverage to Codecov
34-
uses: codecov/codecov-action@v4
34+
uses: codecov/codecov-action@v5
3535
with:
3636
fail_ci_if_error: true
3737
token: ${{ secrets.CODECOV_TOKEN }}

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
uses: actions/checkout@v4
1818

1919
- name: Install uv
20-
uses: astral-sh/setup-uv@v3
20+
uses: astral-sh/setup-uv@v4
2121

2222
- name: Build release
2323
run: uv build

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ uv run tox run -e type_check
5656

5757
1. Bump
5858
1. Increment version in `pyproject.toml`
59-
2. Update all Pixi lock files by running `uv sync`
59+
2. Update all lock files by running `uv sync -U` and `pixi update`
6060
3. Commit with message "chore: Bump version number to X.Y.Z"
6161
4. Push commit to GitHub
6262
5. Check [CI](https://github.com/renan-r-santos/pixi-kernel/actions/workflows/ci.yml) to ensure

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "pixi-kernel"
3-
version = "0.5.1"
3+
version = "0.5.2"
44
description = "Jupyter kernels using Pixi for reproducible notebooks"
55
license = { text = "MIT" }
66
authors = [
@@ -43,8 +43,8 @@ dev-dependencies = [
4343
"mypy>=1,<2",
4444
"pytest>=8,<9",
4545
"pytest-asyncio>=0.24,<0.25",
46-
"pytest-cov>=5,<6",
47-
"ruff>=0.6,<0.7",
46+
"pytest-cov>=6,<7",
47+
"ruff>=0.8,<0.9",
4848
"tox-uv>=1,<2",
4949
]
5050

src/pixi_kernel/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import logging
2+
3+
logging.basicConfig(level=logging.INFO, format="pixi-kernel %(levelname)s: %(message)s")

src/pixi_kernel/errors.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
PIXI_KERNEL_NOT_FOUND = """
3232
To run the {kernel_name} kernel, you need to add the {required_package} package to
3333
your project dependencies. You can do this by running 'pixi add {required_package}'
34-
in your project directory and restarting your kernel.
34+
in your project directory and restarting your kernel. Make sure the prefix
35+
{prefix}
36+
points to the correct Pixi environment.
3537
3638
If you continue to face issues, report them at https://github.com/renan-r-santos/pixi-kernel/issues
3739
"""

src/pixi_kernel/pixi.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import logging
34
import shutil
45
import subprocess
56
from pathlib import Path
@@ -11,6 +12,8 @@
1112

1213
MINIMUM_PIXI_VERSION = "0.30.0"
1314

15+
logger = logging.getLogger(__name__)
16+
1417

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

3033

31-
def ensure_readiness(*, cwd: Path, required_package: str, kernel_name: str) -> Environment:
34+
def ensure_readiness(
35+
*,
36+
cwd: Path,
37+
env: dict[str, str],
38+
required_package: str,
39+
kernel_name: str,
40+
) -> Environment:
3241
"""Ensure the Pixi environment is ready to run the kernel.
3342
3443
This function checks the following:
@@ -43,12 +52,16 @@ def ensure_readiness(*, cwd: Path, required_package: str, kernel_name: str) -> E
4352
4453
Returns the path to the Pixi environment prefix.
4554
"""
55+
# Remove PIXI_IN_SHELL for when JupyterLab is started from a Pixi shell
56+
# https://github.com/renan-r-santos/pixi-kernel/issues/35
57+
env.pop("PIXI_IN_SHELL", None)
58+
4659
# Ensure Pixi is in PATH
4760
if shutil.which("pixi") is None:
4861
raise RuntimeError(PIXI_NOT_FOUND.format(kernel_name=kernel_name))
4962

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

@@ -66,10 +79,13 @@ def ensure_readiness(*, cwd: Path, required_package: str, kernel_name: str) -> E
6679
# Ensure there is a Pixi project in the current working directory or any of its parents
6780
result = subprocess.run(
6881
["pixi", "info", "--json"],
69-
cwd=str(cwd.absolute()),
82+
cwd=cwd,
7083
capture_output=True,
84+
env=env,
7185
text=True,
7286
)
87+
logger.info(f"pixi info stderr: {result.stderr}")
88+
logger.info(f"pixi info stdout: {result.stdout}")
7389
if result.returncode != 0:
7490
raise RuntimeError(f"Failed to run 'pixi info': {result.stderr}")
7591

@@ -85,16 +101,17 @@ def ensure_readiness(*, cwd: Path, required_package: str, kernel_name: str) -> E
85101
# a typo in the toml file (parsing error) or there is no project at all.
86102
result = subprocess.run(
87103
["pixi", "project", "version", "get"],
88-
cwd=str(cwd.absolute()),
104+
cwd=cwd,
89105
capture_output=True,
106+
env=env,
90107
text=True,
91108
)
92109
raise RuntimeError(result.stderr)
93110

94111
# Find the default environment and check if the required kernel package is a dependency
95-
for env in pixi_info.environments:
96-
if env.name == "default":
97-
default_environment = env
112+
for pixi_env in pixi_info.environments:
113+
if pixi_env.name == "default":
114+
default_environment = pixi_env
98115
break
99116
else:
100117
raise RuntimeError("Default Pixi environment not found.")
@@ -103,17 +120,14 @@ def ensure_readiness(*, cwd: Path, required_package: str, kernel_name: str) -> E
103120
if required_package not in dependencies:
104121
raise RuntimeError(
105122
PIXI_KERNEL_NOT_FOUND.format(
106-
kernel_name=kernel_name, required_package=required_package
123+
kernel_name=kernel_name,
124+
required_package=required_package,
125+
prefix=default_environment.prefix,
107126
)
108127
)
109128

110129
# Make sure the environment can be solved and is up-to-date
111-
result = subprocess.run(
112-
["pixi", "install"],
113-
cwd=str(cwd.absolute()),
114-
capture_output=True,
115-
text=True,
116-
)
130+
result = subprocess.run(["pixi", "install"], cwd=cwd, capture_output=True, env=env, text=True)
117131
if result.returncode != 0:
118132
raise RuntimeError(f"Failed to run 'pixi install': {result.stderr}")
119133

src/pixi_kernel/provisioner.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from logging import Logger
1+
import logging
2+
import os
23
from pathlib import Path
34
from typing import Any, Optional, cast
45

@@ -7,14 +8,15 @@
78

89
from .pixi import ensure_readiness
910

11+
logger = logging.getLogger(__name__)
12+
1013

1114
class PixiKernelProvisioner(LocalProvisioner): # type: ignore
1215
async def pre_launch(self, **kwargs: Any) -> dict[str, Any]:
1316
"""Perform any steps in preparation for kernel process launch.
1417
1518
This includes ensuring Pixi is installed and that a Pixi project is available.
1619
"""
17-
logger = cast(Logger, self.log)
1820
kernel_spec = cast(KernelSpec, self.kernel_spec)
1921

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

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

34-
environment = ensure_readiness(
35-
cwd=cwd,
37+
env: dict[str, str] = kwargs.get("env", os.environ)
38+
pixi_environment = ensure_readiness(
39+
cwd=cwd.resolve(),
40+
env=env,
3641
required_package=required_package,
3742
kernel_name=kernel_spec.display_name,
3843
)
3944

4045
# R kernel needs special treatment
4146
# https://github.com/renan-r-santos/pixi-kernel/issues/15
4247
if required_package == "r-irkernel":
43-
r_libs_path = str(Path(environment.prefix) / "lib" / "R" / "library")
48+
r_libs_path = str(Path(pixi_environment.prefix) / "lib" / "R" / "library")
4449
kernel_spec.env["R_LIBS"] = r_libs_path
4550
kernel_spec.env["R_LIBS_SITE"] = r_libs_path
4651
kernel_spec.env["R_LIBS_USER"] = r_libs_path

0 commit comments

Comments
 (0)