Source code for conda_workspaces.parsers.pixi_toml
"""Parser for pixi.toml workspace manifests.
Reads ``[workspace]`` (or ``[project]`` for legacy manifests),
``[dependencies]``, ``[pypi-dependencies]``, ``[feature.*]``,
``[environments]``, and ``[target.*]`` tables from pixi.toml.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
import tomlkit
from ..exceptions import TaskNotFoundError, TaskParseError, WorkspaceParseError
from ..models import WorkspaceConfig
from .base import ManifestParser
from .normalize import parse_feature_tasks, parse_tasks_and_targets
from .toml import _parse_channels, _parse_features_and_envs
if TYPE_CHECKING:
from pathlib import Path
from ..models import Task
[docs]
class PixiTomlParser(ManifestParser):
"""Parse ``pixi.toml`` manifests (workspace and tasks)."""
filenames = ("pixi.toml",)
[docs]
def can_handle(self, path: Path) -> bool:
return path.name in self.filenames
[docs]
def has_workspace(self, path: Path) -> bool:
if not path.exists():
return False
try:
data = tomlkit.loads(path.read_text(encoding="utf-8"))
except Exception:
return False
return "workspace" in data or "project" in data
[docs]
def parse(self, path: Path) -> WorkspaceConfig:
try:
text = path.read_text(encoding="utf-8")
data = tomlkit.loads(text)
except Exception as exc:
raise WorkspaceParseError(path, str(exc)) from exc
root = str(path.parent)
# workspace table (pixi v0.23+) or legacy project table
ws = data.get("workspace", data.get("project", {}))
if not ws:
raise WorkspaceParseError(path, "No [workspace] or [project] table found")
config = WorkspaceConfig(
name=ws.get("name"),
version=ws.get("version"),
description=ws.get("description"),
channels=_parse_channels(ws.get("channels", [])),
platforms=list(ws.get("platforms", [])),
root=root,
manifest_path=str(path),
channel_priority=ws.get("channel-priority"),
)
_parse_features_and_envs(data, config, path)
return config
[docs]
def has_tasks(self, path: Path) -> bool:
if not path.exists():
return False
try:
data = tomlkit.loads(path.read_text(encoding="utf-8"))
except Exception:
return False
return bool(data.get("tasks"))
[docs]
def parse_tasks(self, path: Path) -> dict[str, Task]:
try:
data = tomlkit.loads(path.read_text(encoding="utf-8")).unwrap()
except Exception as exc:
raise TaskParseError(str(path), str(exc)) from exc
tasks = parse_tasks_and_targets(data)
parse_feature_tasks(data, tasks)
return tasks
[docs]
def add_task(self, path: Path, name: str, task: Task) -> None:
if path.exists():
doc = tomlkit.loads(path.read_text(encoding="utf-8"))
else:
doc = tomlkit.document()
tasks_section = doc.setdefault("tasks", tomlkit.table())
tasks_section[name] = self.task_to_toml_inline(task)
path.write_text(tomlkit.dumps(doc), encoding="utf-8")
[docs]
def remove_task(self, path: Path, name: str) -> None:
doc = tomlkit.loads(path.read_text(encoding="utf-8"))
tasks_section = doc.get("tasks", {})
if name not in tasks_section:
raise TaskNotFoundError(name, list(tasks_section.keys()))
del tasks_section[name]
self.remove_target_overrides(doc, name)
path.write_text(tomlkit.dumps(doc), encoding="utf-8")