Environments#

Environment creation, removal, and inspection via conda’s APIs.

Environment manager — create, update, and remove workspace environments.

Uses conda’s Solver API to install packages into workspace-local environments under .conda/envs/<name>/. Each environment is a standard conda prefix that can be activated with conda activate.

class conda_workspaces.envs.PackageRow[source]#

JSON-compatible details for an installed package.

build: str#
name: str#
version: str#
conda_workspaces.envs.activate_d_scripts(prefix: Path) → set[str][source]#

Return the filenames under $PREFIX/etc/conda/activate.d/.

Returns an empty set when the directory does not exist. Used to detect new activation hooks installed into an environment, e.g. to warn that a conda workspace shell session needs to be re-spawned.

conda_workspaces.envs.clean_all(ctx: WorkspaceContext) → None[source]#

Remove all workspace-local environments.

conda_workspaces.envs.get_environment_info(ctx: WorkspaceContext, env_name: str) → dict[str, str | int | bool][source]#

Return basic info about an installed environment.

conda_workspaces.envs.install_environment(ctx: WorkspaceContext, resolved: ResolvedEnvironment, *, force_reinstall: bool = False, dry_run: bool = False, prune: bool = False, update_names: set[str] | None = None) → Path[source]#

Create or update a workspace-local environment.

Uses conda’s Solver API directly instead of shelling out, which avoids the overhead of a subprocess and gives full control over the solve/install transaction.

Version-only PyPI dependencies are translated to conda names and merged into the same solver call as conda dependencies, relying on conda-pypi and conda-rattler-solver to resolve and install them in a single pass. Local path PyPI dependencies are built and installed after the conda transaction.

When dry_run is true, solving and transaction rendering still run, but the prefix and its activation metadata remain unchanged. The returned path is the prefix used for solving.

When prune is true, requested specs absent from resolved are removed in a separate transaction before the remaining specs are installed. Conda-libmamba requires add and remove requests to use separate solver instances.

When update_names is supplied, the prefix must already exist. Only those declared and installed conda roots are passed to the solver, while other installed records remain frozen unless satisfying the requested update requires a dependency change.

Raises SolveError if dependency resolution fails.

conda_workspaces.envs.list_installed_environments(ctx: WorkspaceContext) → list[str][source]#

Return names of environments that are currently installed.

conda_workspaces.envs.list_installed_packages(ctx: WorkspaceContext, env_name: str) → list[PackageRow][source]#

Return installed package details sorted by package name.

conda_workspaces.envs.remove_anchored_directory(parent_descriptor: int, name: str, expected_identity: tuple[int, int]) → None[source]#

Delete one directory tree through descriptors without following leaves.

shutil.rmtree only gained its public dir_fd argument in Python 3.11. This uses public os descriptor APIs so supported Python 3.10 platforms keep the same anchored deletion guarantee.

conda_workspaces.envs.remove_environment(ctx: WorkspaceContext, env_name: str, *, expected_envs_identity: tuple[int, int] | None = None, expected_prefix_identity: tuple[int, int] | None = None) → None[source]#

Remove a prefix without following a replaced directory generation.

conda_workspaces.envs.validate_activation_metadata(prefix: Path, resolved: ResolvedEnvironment) → None[source]#

Validate activation inputs without changing an environment prefix.

Solver installs and exact lockfile installs share this validation so a multi-environment preflight can reject unsafe metadata before any prefix transaction begins.

conda_workspaces.envs.validate_path_dependencies(resolved: ResolvedEnvironment) → None[source]#

Validate local PyPI project inputs without building or installing them.

Building requires Python in the target prefix, so exact-install preflights validate the stable inputs and conda-pypi entry points up front, then leave the actual build for execution after the conda packages are installed.

Read and select a workspace lock#

CondaLockLoader inspects saved environments and reconstructs exact conda records without package-cache access when metadata_only=True is requested. Use select() to extract source entries, including their metadata, instead of composing a new lock from reconstructed records.

from pathlib import Path

from conda.common.serialize.yaml import dumps
from conda_workspaces.lockfile import CondaLockLoader

loader = CondaLockLoader(Path("conda.lock"))
print(loader.available_environments)
print(loader.platforms_for("test"))
selected = loader.select({"test": ["linux-64"]})
Path("selected.lock").write_text(dumps(selected), encoding="utf-8")

Selection preserves root, environment and package metadata and removes unused records. It rejects selected external references, missing packages, inconsistent identities or hashes, and invalid channels. The loader redacts URL credentials as on other read paths. Callers requiring unchanged authenticated URLs must reject credential-bearing input before construction.

package_platform_for(target, name) returns the concrete conda subdir inferred from a saved target name and its package records. It returns None for a logical target with only noarch packages or no packages when the backing subdir is unknown. Source selection remains possible for that target, while an export requiring a concrete platform needs additional information. Pass a known subdir to env_for(..., package_platform=subdir, metadata_only=True) to keep the logical target separate from the package platform.

class conda_workspaces.lockfile.CondaLockLoader(path: PathType, *, data: dict[str, Any] | None = None)[source]#

Environment specifier + loader for conda.lock.

conda.lock is a derivative of rattler-lock v6 (pixi.lock); this loader shares the rattler-lock v6 conversion helper from conda_lockfiles.rattler_lock.v6 by performing an in-memory version: 1 -> 6 swap before handing the data off. The on-disk file keeps version: 1 unchanged.

Used by conda env create --file conda.lock (single platform via env) and by conda workspace install (multi-platform via env_for).

property available_environments: tuple[str, ...]#

Return the environment names declared in this lockfile.

env_for(platform: str, name: str = 'default', *, package_platform: str | None = None, metadata_only: bool = False) → Environment[source]#

Return the conda Environment for platform and name.

Raises PlatformMismatchError if platform is not in the lockfile or name does not identify a declared environment. package_platform supplies the backing conda subdir when platform is a rich workspace platform name. The returned environment keeps the backing subdir as its conda platform and records the logical lock key separately for round-trip serialization. metadata_only reconstructs exact records from the lockfile without accessing the package cache.

package_platform_for(platform: str, name: str = 'default') → str | None[source]#

Find a target’s conda subdir, or None without platform-specific records.

platforms_for(name: str = 'default') → tuple[str, ...][source]#

Return the platforms declared for lockfile environment name.

select(selections: Mapping[str, Iterable[str]]) → dict[str, Any][source]#

Copy selected solutions and their source metadata without solving.

Check a saved lock against its manifest#

check_lockfile_satisfiability(config, lockfile_data, current_platform) checks workspace declarations and the package requirements for one target. Call it for every declared logical target to check all saved solutions. Its LockfileStatus.reason explains the first mismatch.

Pass environment="test" to check package requirements only for that environment on the requested target. All workspace environment names, ordered channels and declared target names remain checked. This lets callers scope virtual package configuration independently for each environment and target without copying or removing declarations. An unknown environment raises EnvironmentNotFoundError.

The checker uses conda’s virtual package plugins and the active override configuration. Callers requiring host-independent results must configure target virtual packages before each call. A successful check means the lockfile satisfies the manifest. It does not check for newer packages or verify package archives.

conda_workspaces.lockfile.load_lockfile_data(content: str | bytes) → dict[str, Any][source]#

Parse in-memory lockfile YAML with the same safe loader as disk reads.

conda_workspaces.lockfile.check_lockfile_satisfiability(config: WorkspaceConfig, lockfile_data: dict[str, Any], current_platform: str, *, environment: str | None = None) → LockfileStatus[source]#

Check whether lockfile_data satisfies the manifest’s requirements.

Returns a LockfileStatus with status=UP_TO_DATE when the lockfile covers every environment, platform, channel, and dependency declared in config. Returns status=OUT_OF_DATE with a human-readable reason otherwise.

Set environment to check package requirements only for that environment on current_platform. Environment names, channels and declared platforms are still checked across the complete workspace.