Source code for conda_tasks.parsers.pyproject_toml
"""Parser for pyproject.toml task definitions.
Reads from:
- ``[tool.conda.tasks]`` (preferred)
- ``[tool.conda-tasks.tasks]`` (legacy alias)
- ``[tool.pixi.tasks]`` (fallback for pixi compatibility)
Platform overrides:
- ``[tool.conda.target.<platform>.tasks]``
- ``[tool.conda-tasks.target.<platform>.tasks]``
- ``[tool.pixi.target.<platform>.tasks]``
"""
from __future__ import annotations
from typing import TYPE_CHECKING
import tomlkit
from ..exceptions import TaskParseError
from .base import TaskFileParser
from .normalize import normalize_override, normalize_task
if TYPE_CHECKING:
from pathlib import Path
from typing import ClassVar
from ..models import Task
[docs]
class PyprojectTomlParser(TaskFileParser):
"""Reads task definitions from ``pyproject.toml``."""
extensions: ClassVar[tuple[str, ...]] = (".toml",)
filenames: ClassVar[tuple[str, ...]] = ("pyproject.toml",)
[docs]
def can_handle(self, path: Path) -> bool:
"""Return True if *path* is a ``pyproject.toml`` with task definitions."""
if path.name not in self.filenames:
return False
try:
data = tomlkit.loads(path.read_text(encoding="utf-8")).unwrap()
except Exception:
return False
tool = data.get("tool", {})
return bool(
tool.get("conda", {}).get("tasks")
or tool.get("conda-tasks", {}).get("tasks")
or tool.get("pixi", {}).get("tasks")
)
[docs]
def parse(self, path: Path) -> dict[str, Task]:
"""Parse tasks from conda, conda-tasks, or pixi tool tables."""
try:
data = tomlkit.loads(path.read_text(encoding="utf-8")).unwrap()
except Exception as exc:
raise TaskParseError(str(path), str(exc)) from exc
tool = data.get("tool", {})
conda_section = tool.get("conda", {})
conda_tasks_section = tool.get("conda-tasks", {})
pixi_section = tool.get("pixi", {})
raw_tasks = (
conda_section.get("tasks")
or conda_tasks_section.get("tasks")
or pixi_section.get("tasks")
or {}
)
target_section = (
conda_section.get("target")
or conda_tasks_section.get("target")
or pixi_section.get("target")
or {}
)
if not isinstance(raw_tasks, dict):
raise TaskParseError(str(path), "tasks must be a table")
tasks: dict[str, Task] = {}
for name, defn in raw_tasks.items():
tasks[name] = normalize_task(name, defn)
if isinstance(target_section, dict):
for platform, platform_data in target_section.items():
if not isinstance(platform_data, dict):
continue
platform_tasks = platform_data.get("tasks", {})
for name, defn in platform_tasks.items():
override = normalize_override(
defn if isinstance(defn, dict) else {"cmd": defn}
)
if name in tasks:
existing = tasks[name]
if existing.platforms is None:
existing.platforms = {}
existing.platforms[platform] = override
else:
task = normalize_task(name, defn)
task.platforms = {platform: override}
tasks[name] = task
return tasks
[docs]
def add_task(self, path: Path, name: str, task: Task) -> None:
"""Not supported — ``pyproject.toml`` is read-only."""
raise NotImplementedError(
"Writing to pyproject.toml is not supported. Use conda.toml instead."
)
[docs]
def remove_task(self, path: Path, name: str) -> None:
"""Not supported — ``pyproject.toml`` is read-only."""
raise NotImplementedError(
"Writing to pyproject.toml is not supported. Use conda.toml instead."
)