Dependency resolution model#
conda-exec has two execution modes. When an environment is needed, each mode turns user input into one conda solve or one lockfile-based environment creation.
Tool mode starts from a package spec in the
TOOLposition.Script mode starts from PEP 723 metadata in a Python file.
Both modes create ordinary conda prefixes under the cache directory. A script without metadata or CLI dependency options is the exception: it runs directly with the current Python interpreter.
Tool mode#
For a command like:
conda exec --with pytest -c conda-forge "ruff>=0.4,<0.5" check .
conda-exec builds:
tool name:
ruffspecs:
["ruff>=0.4,<0.5", "pytest"]channels:
["conda-forge"]command arguments:
["check", "."]
The specs are parsed as conda match specs. The tool name is extracted from the first spec and is later used for binary discovery.
After solving and installing the environment, conda-exec looks for an
executable named ruff in the prefix’s platform-correct binary directory.
Script mode#
For a script with:
# /// script
# requires-python = ">=3.12"
# dependencies = ["rich"]
#
# [tool.conda]
# channels = ["conda-forge"]
# dependencies = ["numpy>=2"]
# ///
conda-exec builds:
conda specs from
[tool.conda].dependenciesPyPI specs from top-level
dependencieschannels from
[tool.conda].channelsa Python spec from
requires-python
If no spec starts with python, conda-exec adds one automatically. With
requires-python = ">=3.12", the added spec is python >=3.12. Without
requires-python, the added spec is a bare python.
After creating the environment, conda-exec finds that environment’s Python interpreter and runs the script with it.
PyPI dependencies#
Top-level PEP 723 dependencies are PyPI requirements. conda-exec supports
them through conda-pypi.
When PyPI dependencies are present:
conda-exec verifies that
conda-pypiis importable.The
conda-pypichannel is appended to the channel list.PyPI requirements are added to the same solve as conda specs.
This means mixed conda and PyPI scripts still create one environment and one cache entry.
If conda-pypi is missing, conda-exec fails before solving so the user gets
a clear setup error instead of a solver error.
Channel defaults#
Tool mode uses conda-forge when no -c/--channel flags are provided.
Script mode combines channels from metadata and the CLI:
channels declared in
[tool.conda].channelschannels passed with
-c/--channelconda-forgeif the combined list is empty
When PyPI dependencies are present, conda-pypi is appended after those
channels.
Cache keys and solving#
The cache key determines whether conda-exec solves or reuses an existing prefix.
Tool cache keys are derived from normalized specs and channels. Script metadata cache keys are derived from dependency metadata. Script lock cache keys are derived from lock content.
This creates predictable behavior:
changing command arguments does not create a new environment
changing package specs creates a new environment
changing script code alone does not create a new environment
changing script dependency metadata creates a new environment
changing generated lock content creates a new environment
--refresh removes the cache entry for the current dependency input and
forces a new solve or lock-based environment creation.
Lock data path#
When lock data is discovered and matches the current dependency input, conda-exec skips solving from metadata. It asks conda to create an environment from the lockfile format instead.
This path still creates a cached prefix. The difference is that package versions and builds come from lock content rather than a fresh solver decision.
Lock data is ignored when command-line flags change the dependency input,
such as --with or --channel, and when the user explicitly passes
--ignore-lock or --refresh.
Failure surfaces#
Resolution can fail at several distinct stages:
match spec parsing fails before solving
no solver backend is available
conda cannot solve the requested specs
conda-pypiis required but unavailablelock exporter or specifier plugins are unavailable
the environment solves but the expected binary is not present
conda-exec keeps these stages separate so errors can point to the right fix: install a missing plugin, adjust channels, change a spec, refresh a cache, or use a named environment for packages with unusual executables.