Source code for conda_workspaces.manifests.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 TaskParseError, WorkspaceParseError
from ..models import WorkspaceConfig
from .base import ManifestParser
from .normalize import parse_feature_tasks, parse_tasks_and_targets
from .toml import (
parse_archive_config,
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)."""
format_alias = "pixi"
filenames = ("pixi.toml",)
exporter_format = "pixi-toml"
[docs]
def can_handle(self, path: Path) -> bool:
return path.name in self.filenames
[docs]
def has_workspace(self, path: Path) -> bool:
data = self.read_toml(str(path))
return "workspace" in data or "project" in data
[docs]
def parse(self, path: Path) -> WorkspaceConfig:
try:
text = path.read_text(encoding="utf-8")
# ``unwrap()`` returns plain Python types; otherwise tomlkit
# subclasses (e.g. ``tomlkit.items.String``) leak through the
# data model and trip ruamel.yaml's exact-type key dispatch
# at lockfile write time. Callers that need round-tripping
# (``conda workspace add/remove/init``) still work on the raw
# ``TOMLDocument`` separately.
data = tomlkit.loads(text).unwrap()
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")
(
platforms,
platform_subdirs,
platform_system_requirements,
) = self.parse_workspace_platforms(
ws.get("platforms", []),
path,
)
config = WorkspaceConfig(
name=ws.get("name"),
version=ws.get("version"),
description=ws.get("description"),
channels=parse_channels(ws.get("channels", [])),
platforms=platforms,
platform_subdirs=platform_subdirs,
platform_system_requirements=platform_system_requirements,
root=root,
manifest_path=str(path),
channel_priority=ws.get("channel-priority"),
archive=parse_archive_config(ws),
)
parse_features_and_envs(data, config, path, self)
return config
[docs]
def has_tasks(self, path: Path) -> bool:
return bool(self.read_toml(str(path)).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