Manifests#

Manifest parsers and the detection/registry system.

Each parser handles both workspace configuration and task definitions for its file format. The manifests/ package is conda-workspaces’ internal substrate; the package-root modules env_spec.py, lockfile.py and export.py sit on top and expose the public plugin API.

Manifest detection and parser registry (workspaces and tasks).

Search order (same for workspaces and tasks)#

  1. conda.toml – conda-native manifest format

  2. pixi.toml – pixi-native format (compatibility)

  3. pyproject.toml – pixi or conda tables embedded

The first file that exists and contains the relevant configuration wins.

conda_workspaces.manifests.cached_task_parse(path_str: str) dict[str, Task][source]#

Parse tasks from a manifest file (cached by path string).

conda_workspaces.manifests.cached_user_task_parse(path_str: str) dict[str, Task][source]#

Parse tasks from the user-level tasks.toml (conda.toml format).

conda_workspaces.manifests.detect_and_parse(source: str | Path | None = None) tuple[Path, WorkspaceConfig][source]#

Resolve source to a workspace manifest and parse it.

Returns (manifest_path, workspace_config).

conda_workspaces.manifests.detect_and_parse_tasks(file_path: Path | None = None, start_dir: Path | None = None, *, reject_symlinks: bool = False) tuple[Path, dict[str, Task], set[str]][source]#

Detect task files and parse them, merging user-level tasks.

Returns (resolved_path, {task_name: Task}, user_only_names) where user_only_names is the set of task names that came from the user-level file and were not overridden by the project.

Raises NoTaskFileError when neither a project nor a user task file is found.

conda_workspaces.manifests.detect_task_file(start_dir: Path | None = None, *, reject_symlinks: bool = False) Path | None[source]#

Walk up from start_dir looking for a file that contains tasks.

Returns the first match according to _SEARCH_FILES, or None.

conda_workspaces.manifests.detect_workspace_file(start_dir: str | Path | None = None, *, reject_symlinks: bool = False) Path[source]#

Walk up from start_dir to find a workspace manifest.

Returns the path to the first matching file. Raises WorkspaceNotFoundError if none is found.

conda_workspaces.manifests.find_parser(path: Path) ManifestParser[source]#

Return the parser that can handle path.

Raises WorkspaceParseError if no parser matches.

conda_workspaces.manifests.user_task_file() Path | None[source]#

Return the user-level task file path, or None.

conda_workspaces.manifests.walk_manifests(start_dir: Path, predicate: str, *, reject_symlinks: bool = False) Path | None[source]#

Walk up from start_dir looking for a manifest matching predicate.

predicate is a method name on ManifestParser — either "has_workspace" or "has_tasks". Returns the first matching file path, or None if none is found.

Abstract base class for manifest parsers (workspaces and tasks).

class conda_workspaces.manifests.base.ManifestParser[source]#

Interface that every manifest parser must implement.

Each parser handles one file format (conda.toml, pixi.toml, or pyproject.toml). Subclasses declare which files they can handle via filenames and a short format_alias ("conda" / "pixi" / "pyproject") that the CLI uses for --format values. The registry in conda_workspaces.manifests uses these to auto-detect the right parser and to resolve --format aliases to the parser that owns the matching filename.

A single parser instance handles both workspace configuration and task definitions from the same file.

add_task(path: Path, name: str, task: Task) None[source]#

Persist a top-level task definition into path.

abstractmethod can_handle(path: Path) bool[source]#

Return True if this parser can read path.

classmethod copy_manifest(source: Path, dest_dir: Path) Path[source]#

Copy the manifest at source into dest_dir; return the target path.

source may be a directory (walked via resolve_source()) or a manifest file. Raises FileNotFoundError, conda_workspaces.exceptions.WorkspaceNotFoundError, or conda_workspaces.exceptions.ManifestExistsError as appropriate; callers layer their own dry-run / console policy on top.

export(envs: Iterable[Environment]) str[source]#

Serialize envs to this parser’s manifest format.

Produces a manifest that, when written to disk and parsed by parse(), describes the same requested dependencies, channels, and declared platforms that envs carry. Each Environment is one (name, platform) pair; envs must all share the same name (conda’s CondaEnvironmentExporter hook calls multiplatform_export with per-platform copies of the same logical environment).

The default implementation writes top-level [workspace], [dependencies], [pypi-dependencies], and [target.<platform>.*] tables — the shape conda.toml and pixi.toml share. PyprojectTomlParser overrides it to nest the same content under [tool.conda] without disturbing the rest of the pyproject. Used as the multiplatform_export callable on the exporter plugins registered from conda_workspaces.plugin.

exporter_aliases: ClassVar[tuple[str, ...]] = ()#

Optional user-friendly aliases for the exporter plugin (e.g. ("conda",) for conda-toml). Empty tuple is fine.

exporter_format: ClassVar[str] = ''#

Canonical conda_environment_exporters plugin name. Empty disables exporter registration for that parser (see conda_workspaces.plugin).

filenames: ClassVar[tuple[str, ...]] = ()#
classmethod for_exporter_format(name: str) ManifestParser | None[source]#

Return the registered parser whose exporter_format matches name.

Companion to for_format_alias() for the conda_environment_exporters plugin side: conda workspace export --format <name> uses the exporter plugin name (e.g. pyproject-toml), which is stored on exporter_format rather than format_alias. Returns None when name is not a manifest-format exporter — the CLI uses this to decide whether to route writes through merge_export(), and a None result simply means “not one of ours, write verbatim”.

classmethod for_format_alias(alias: str) ManifestParser[source]#

Return the registered parser whose format_alias matches alias.

Used by conda workspace init / quickstart to turn a --format value like "pyproject" / "conda" / "pixi" into the parser (and therefore the filename) it implies. Raises ValueError when no parser claims alias. The companion lookup for conda workspace export --format is for_exporter_format() — that side matches the longer conda_environment_exporters plugin name ("pyproject-toml", "conda-toml", "pixi-toml") which is stored on exporter_format. The registry is conda_workspaces.manifests._PARSERS.

format_alias: ClassVar[str] = ''#
has_tasks(path: Path) bool[source]#

Return True if path contains task definitions.

abstractmethod has_workspace(path: Path) bool[source]#

Return True if path contains workspace configuration.

classmethod load_toml(path: Path) tomlkit.TOMLDocument[source]#

Read and parse one repository manifest under explicit limits.

classmethod load_toml_with_generation(path: Path) tuple[tomlkit.TOMLDocument, FileGeneration][source]#

Read a mutable manifest and retain the generation to replace.

classmethod manifest_data(envs: Iterable[Environment]) dict[str, Any][source]#

Fold one or more Environment objects into a manifest-shaped dict.

Returns the data that export() writers need, with the format-agnostic parts decided once:

  • name / platforms / channels describe the [workspace] table (platforms are the sorted union across envs; channels are taken from the first env — exporter callers pass the same channel list on every platform).

  • conda_deps / pypi_deps are the intersection across envs — specs that match by name and value on every platform, the ones a round-trip parse would put under the top-level [dependencies] / [pypi-dependencies] tables.

  • target[<platform>]["conda"|"pypi"] holds the per-platform delta — specs that appear on some platforms but not others, or whose value differs across platforms. A round-trip parse restores these under [target.<platform>.dependencies] / [target.<platform>.pypi-dependencies].

Used by export() (via _emit_manifest()) and exposed as a classmethod so individual parsers and exporter plugin shims can drive the same folding logic without duplicating it.

property manifest_filename: str#

Canonical filename this parser reads and writes.

The first entry in filenames — e.g. "conda.toml" for CondaTomlParser. Used by manifest_path() and the conda workspace init / quickstart CLI paths so the format-to-filename mapping lives in exactly one place.

manifest_path(root: Path) Path[source]#

Return the manifest path this parser would (or did) write inside root.

merge_export(existing_path: Path, exported: str) str[source]#

Return exported ready to write into an existing existing_path.

The default implementation returns exported unchanged — conda.toml and pixi.toml are manifests we own end-to-end, so regenerating them from an environment is a full replacement (same as conda export -f environment.yaml overwriting an existing environment.yaml).

PyprojectTomlParser overrides this to splice the exporter’s [tool.conda] subtree into the existing pyproject.toml document without disturbing peer tables ([project], [build-system], [tool.ruff] etc.), because pyproject.toml is a shared manifest owned by the Python ecosystem. Called from conda_workspaces.cli.workspace.export only when --file points to an existing file, so a fresh export still writes the exporter output verbatim.

merge_export_text(existing: str, exported: str) str[source]#

Merge exported with a previously captured existing generation.

parse(path: Path) WorkspaceConfig[source]#

Parse TOML from path and return a WorkspaceConfig.

abstractmethod parse_data(data: dict[str, Any], path: Path) WorkspaceConfig[source]#

Parse already-loaded manifest data associated with path.

parse_data_with_redacted_errors(data: dict[str, Any], path: Path) WorkspaceConfig[source]#

Parse manifest data without exposing credential-bearing diagnostics.

parse_system_requirements(requirements: Mapping[str, Any]) dict[str, str][source]#

Parse Pixi-facing system requirements into conda virtual names.

Pixi exposes TOML names like libc / macos / windows; conda virtual packages use __glibc / __osx / __win. The workspace model stores bare conda names while preserving raw __name escape hatches for callers that already use virtual package names.

parse_tasks(path: Path) dict[str, Task][source]#

Parse path and return a mapping of task-name to Task.

parse_tasks_data(data: dict[str, Any]) dict[str, Task][source]#

Parse tasks from an already loaded manifest mapping.

parse_text(path: Path, content: str) WorkspaceConfig[source]#

Parse content and bind the result to that manifest generation.

static parse_toml_text(content: str) TOMLDocument[source]#

Parse manifest TOML text under explicit resource limits.

classmethod parse_toml_text_with_redacted_errors(content: str, path: Path) tomlkit.TOMLDocument[source]#

Parse mutable manifest text without exposing sensitive diagnostics.

parse_workspace_platforms(raw: Iterable[Any], path: Path) tuple[list[str], dict[str, str], dict[str, dict[str, str]]][source]#

Parse Pixi-compatible workspace platform entries.

Bare strings remain plain conda subdirs. Inline tables can add virtual package requirements and optional Pixi rich-platform names, e.g. { name = "linux-64-cuda", platform = "linux-64", cuda = "12" }. The returned platform list contains the declared names used by features and lockfiles; platform_subdirs records the concrete conda subdir each name solves against.

static read_manifest_text(path: Path) str[source]#

Read one repository manifest under the configured byte limit.

static read_manifest_text_with_generation(path: Path) tuple[str, FileGeneration][source]#

Read a mutable manifest without links and return its generation.

remove_target_overrides(container: Container, name: str) None[source]#

Remove name from every [target.<platform>.tasks] under container.

remove_task(path: Path, name: str) None[source]#

Remove the top-level task named name from path.

classmethod resolve_source(source: Path) Path[source]#

Resolve source (directory or file) to a concrete manifest path.

Directories are walked via conda_workspaces.manifests.detect_workspace_file(); files are returned as-is. Raises FileNotFoundError when source does not exist and conda_workspaces.exceptions.WorkspaceNotFoundError when the directory contains no recognisable manifest.

rich_platform_system_requirement_keys: ClassVar[set[str]] = {'archspec', 'cuda', 'glibc', 'libc', 'linux', 'macos', 'osx', 'win', 'windows'}#
system_requirement_aliases: ClassVar[dict[str, str]] = {'libc': 'glibc', 'macos': 'osx', 'windows': 'win'}#
task_to_toml_inline(task: Task) str | InlineTable[source]#

Convert a task to a TOML-serializable value (string or inline table).

validate_no_url_credentials(data: Mapping[str, Any], path: Path, *, content: str | None = None) None[source]#

Reject sensitive URL material anywhere in manifest-owned data.

write_workspace_stub(base_dir: Path, name: str, channels: list[str], platforms: list[str]) tuple[Path, str][source]#

Create a minimal workspace manifest under base_dir.

Writes a fresh TOML document with [workspace] and an empty [dependencies] table at manifest_path() and returns (path, "Created"). Raises ManifestExistsError if the target file is already present — subclasses that share their file with other tooling (see PyprojectTomlParser) override this method to append their configuration under a nested table instead of refusing outright, and report "Updated" when they did so.

conda_workspaces.manifests.base.match_spec_to_toml(spec: MatchSpec) str | InlineTable[source]#

Return a lossless workspace TOML value for a conda MatchSpec.

This is a module-level function because MatchSpec is owned by conda and the serializer is shared by parsers, importers, mutations, and exporters.

Parser for conda.toml manifests and shared TOML helpers.

The CondaTomlParser handles conda.toml — the conda-native manifest format for both workspace configuration and task definitions.

Public helpers for parsing shared TOML workspace tables are reused by pixi_toml.py and pyproject_toml.py.

class conda_workspaces.manifests.toml.CondaTomlParser[source]#

Parse conda.toml manifests (workspace and tasks).

This is the conda-native format that mirrors pixi.toml structure but uses [workspace] exclusively (no [project] fallback).

can_handle(path: Path) bool[source]#

Return True if this parser can read path.

exporter_format = 'conda-toml'#

Canonical conda_environment_exporters plugin name. Empty disables exporter registration for that parser (see conda_workspaces.plugin).

filenames = ('conda.toml',)#
format_alias = 'conda'#
has_tasks(path: Path) bool[source]#

Return True if path contains task definitions.

has_workspace(path: Path) bool[source]#

Return True if path contains workspace configuration.

parse_data(data: dict[str, Any], path: Path) WorkspaceConfig[source]#

Parse already-loaded conda.toml data.

parse_tasks_data(data: dict[str, Any]) dict[str, Task][source]#

Parse conda tasks from an already loaded manifest mapping.

class conda_workspaces.manifests.toml.WorkspaceDependencyResolver(*, workspace_dependencies: dict[str, Any] | None = None, path: Path | None = None)[source]#

Resolve conda dependency tables with workspace inheritance.

[workspace.dependencies] is a root-level pool. Entries in regular dependency tables opt in with { workspace = true }; after parsing, downstream code only sees concrete MatchSpec objects.

error(message: str) NoReturn[source]#

Raise a manifest parse error when a path is available.

match_spec_from_fields(name: str, fields: dict[str, Any]) MatchSpec[source]#

Construct a MatchSpec and wrap parse errors with manifest context.

static match_spec_to_toml(spec: MatchSpec) str | InlineTable#

Return a lossless workspace TOML value for a conda MatchSpec.

This is a module-level function because MatchSpec is owned by conda and the serializer is shared by parsers, importers, mutations, and exporters.

parse_dependency(name: str, spec: Any, *, allow_inheritance: bool, table_name: str) MatchSpec[source]#

Parse one dependency entry.

parse_dependency_table(raw: dict[str, Any], *, allow_inheritance: bool = True, table_name: str = '[dependencies]') dict[str, MatchSpec][source]#

Parse a dependency table into MatchSpec objects.

reject_source_fields(name: str, spec: Any, table_name: str) None[source]#

Reject pixi source-package fields when inheritance would consume them.

source_spec_fields: ClassVar[set[str]] = {'branch', 'extras', 'flags', 'git', 'path', 'rev', 'subdirectory', 'tag'}#
spec_field_aliases: ClassVar[dict[str, str]] = {'build': 'build', 'build-number': 'build_number', 'build_number': 'build_number', 'channel': 'channel', 'features': 'features', 'file-name': 'fn', 'fn': 'fn', 'license': 'license', 'license-family': 'license_family', 'license_family': 'license_family', 'md5': 'md5', 'sha256': 'sha256', 'subdir': 'subdir', 'track-features': 'track_features', 'track_features': 'track_features', 'url': 'url', 'version': 'version'}#
spec_fields(name: str, spec: Any, *, strict_unsupported: bool) dict[str, Any][source]#

Return conda MatchSpec keyword fields for one TOML dependency.

toml_spec_fields: ClassVar[dict[str, str]] = {'build': 'build', 'build_number': 'build-number', 'channel': 'channel', 'features': 'features', 'fn': 'file-name', 'license': 'license', 'license_family': 'license-family', 'md5': 'md5', 'sha256': 'sha256', 'subdir': 'subdir', 'track_features': 'track-features', 'url': 'url', 'version': 'version'}#
conda_workspaces.manifests.toml.parse_archive_config(ws: dict[str, Any]) ArchiveConfig[source]#

Parse [workspace.archive] into an ArchiveConfig.

conda_workspaces.manifests.toml.parse_channels(raw: list[Any]) list[Channel][source]#

Parse a channels list, handling both strings and dicts.

conda_workspaces.manifests.toml.parse_environment(name: str, raw: Any, path: Path, resolver: WorkspaceDependencyResolver | None = None) Environment[source]#

Parse a single environment entry.

Environments can be specified as: - A list of feature names: env = ["feat1", "feat2"] - A dict with keys: env = {features = [...]}

conda_workspaces.manifests.toml.parse_feature(name: str, feat_data: dict[str, Any], parser: ManifestParser, resolver: WorkspaceDependencyResolver | None = None) Feature[source]#

Parse a single [feature.<name>] table into a Feature.

Shared by PixiTomlParser and PyprojectTomlParser — the per-feature logic is identical once the data dict is resolved.

conda_workspaces.manifests.toml.parse_features_and_envs(source: dict[str, Any], config: WorkspaceConfig, path: Path, parser: ManifestParser) None[source]#

Parse features and environments from source into config.

Adds the default feature (from top-level deps/activation/system-reqs), all named features, and all environments. Shared by PixiTomlParser and PyprojectTomlParser.

conda_workspaces.manifests.toml.parse_pypi_dependencies(raw: dict[str, Any]) dict[str, PyPIDependency][source]#

Parse PyPI dependency specs.

conda_workspaces.manifests.toml.parse_target_overrides(target_data: dict[str, Any], owner: Feature | Environment, resolver: WorkspaceDependencyResolver | None = None, *, table_path: str = 'target') None[source]#

Parse target dependency overrides into a feature or environment.

conda_workspaces.manifests.toml.tasks_to_toml(tasks: dict[str, Task]) str[source]#

Serialize a full task dict to conda.toml TOML string.

Parser for pixi.toml workspace manifests.

Reads [workspace] (or [project] for legacy manifests), [dependencies], [pypi-dependencies], [feature.*], [environments], and [target.*] tables from pixi.toml.

class conda_workspaces.manifests.pixi_toml.PixiTomlParser[source]#

Parse pixi.toml manifests (workspace and tasks).

can_handle(path: Path) bool[source]#

Return True if this parser can read path.

exporter_format = 'pixi-toml'#

Canonical conda_environment_exporters plugin name. Empty disables exporter registration for that parser (see conda_workspaces.plugin).

filenames = ('pixi.toml',)#
format_alias = 'pixi'#
has_tasks(path: Path) bool[source]#

Return True if path contains task definitions.

has_workspace(path: Path) bool[source]#

Return True if path contains workspace configuration.

parse_data(data: dict[str, Any], path: Path) WorkspaceConfig[source]#

Parse already-loaded pixi-style manifest data.

parse_tasks_data(data: dict[str, Any]) dict[str, Task][source]#

Parse pixi tasks from an already loaded manifest mapping.

Parser for pyproject.toml workspace manifests.

Reads workspace configuration from pyproject.toml, trying these tables in order:

  1. [tool.conda.workspace] – conda-native table

  2. [tool.pixi.workspace] – pixi compatibility

class conda_workspaces.manifests.pyproject_toml.PyprojectTomlParser[source]#

Parse workspace and task config from pyproject.toml.

Tries these tool tables in priority order:

  1. [tool.conda.*] – conda-native tables

  2. [tool.pixi.*] – pixi compatibility

add_task(path: Path, name: str, task: Task) None[source]#

Persist a top-level task definition into path.

can_handle(path: Path) bool[source]#

Return True if this parser can read path.

export(envs: Iterable[Environment]) str[source]#

Serialize envs as a pyproject.toml with [tool.conda.*].

Same content as ManifestParser.export() — workspace table, dependencies, optional pypi-dependencies, optional per-platform overrides — but wrapped under [tool.conda] so the output drops straight into PEP 621 / pyproject.toml alongside [project], [build-system], and peer tables.

exporter_format = 'pyproject-toml'#

Canonical conda_environment_exporters plugin name. Empty disables exporter registration for that parser (see conda_workspaces.plugin).

filenames = ('pyproject.toml',)#
format_alias = 'pyproject'#
has_tasks(path: Path) bool[source]#

Return True if path contains task definitions.

has_workspace(path: Path) bool[source]#

Return True if path contains workspace configuration.

merge_export(existing_path: Path, exported: str) str[source]#

Splice exported’s [tool.conda] into existing_path.

pyproject.toml is a shared packaging manifest owned by the Python ecosystem; the default “overwrite the file wholesale” behaviour of ManifestParser.merge_export() would silently destroy [project] / [build-system] / [tool.ruff] / etc. Instead we parse the existing document, replace its [tool.conda] subtree with the one export() just produced, and serialise the result.

This is the export-side companion to write_workspace_stub(), which does the same kind of nested-table merge for conda workspace init. Existing [tool.pixi] content is preserved untouched — users who mix both tools stay functional.

merge_export_text(existing: str, exported: str) str[source]#

Splice an export into one already captured pyproject generation.

parse_data(data: dict[str, Any], path: Path) WorkspaceConfig[source]#

Parse already-loaded pyproject manifest data.

parse_tasks_data(data: dict[str, Any]) dict[str, Task][source]#

Parse embedded tasks from an already loaded manifest mapping.

remove_task(path: Path, name: str) None[source]#

Remove the top-level task named name from path.

tool_section_for_tasks(doc: tomlkit.TOMLDocument) Table[source]#

Return the tool sub-table that owns tasks.

Uses the same precedence as parse_tasks: non-empty tool.conda wins, then non-empty tool.pixi, then falls back to tool.conda for new manifests.

write_workspace_stub(base_dir: Path, name: str, channels: list[str], platforms: list[str]) tuple[Path, str][source]#

Add [tool.conda.workspace] to base_dir/pyproject.toml.

Unlike the default ManifestParser.write_workspace_stub() (which refuses to touch an existing file), pyproject.toml is a shared packaging manifest owned by the Python ecosystem — PEP 621 [project], [build-system], and other tooling tables routinely coexist with ours. We read the existing document if any, add our configuration under the nested [tool.conda] table, and report "Updated" so the CLI can distinguish an append from a create. An existing [tool.conda] or [tool.pixi] raises ManifestExistsError.

Shared logic for normalizing raw task dicts into Task model objects.

conda_workspaces.manifests.normalize.normalize_args(raw: list[Any] | None) list[TaskArg][source]#

Convert raw arg definitions into TaskArg objects.

Accepted shapes: - ["name"] (required arg, no default) - [{"arg": "name", "default": "value"}] - [{"arg": "name", "default": "value", "choices": ["a", "b"]}]

conda_workspaces.manifests.normalize.normalize_depends_on(raw: list[Any] | str | None) list[TaskDependency][source]#

Convert the various depends-on formats into TaskDependency objects.

Accepted shapes: - ["foo", "bar"] (simple list of task names) - [{"task": "foo", "args": ["x"]}, ...] (full dict form) - [{"task": "foo"}, {"task": "bar"}] (pixi alias shorthand)

conda_workspaces.manifests.normalize.normalize_override(raw: dict[str, Any]) TaskOverride[source]#

Parse a raw dict into a TaskOverride.

conda_workspaces.manifests.normalize.normalize_task(name: str, raw: str | list[Any] | dict[str, Any]) Task[source]#

Convert a single raw task value into a Task object.

Handles all the shorthand forms: - "command string" (simple string command) - ["dep1", "dep2"] or [{"task": ...}] (alias / dependency-only) - {cmd: ..., depends-on: ..., ...} (full dict definition)

conda_workspaces.manifests.normalize.parse_feature_tasks(data: dict[str, Any], tasks: dict[str, Task]) None[source]#

Parse [feature.<name>.tasks] and their target overrides.

Merges feature-scoped tasks into tasks in place. Shared by PixiTomlParser and PyprojectTomlParser.

conda_workspaces.manifests.normalize.parse_tasks_and_targets(data: dict[str, Any]) dict[str, Task][source]#

Parse [tasks] and [target.<platform>.tasks] from a data dict.

Shared by CondaTomlParser, PixiTomlParser, and PyprojectTomlParser — the core parsing logic is identical across all three formats once the root data dict is resolved.