Run scripts with dependencies#
conda-exec can run Python scripts that declare their dependencies inline using the PEP 723 metadata format.
Add a PEP 723 # /// script metadata block to the Python file itself,
usually near the top before imports or executable code. The lines inside
the block are TOML, with the leading # comment prefix stripped before
parsing.
The examples below show complete metadata blocks you can copy into a script.
Conda-only dependencies#
Use TOML syntax and the [tool.conda] table to declare conda packages:
# /// script
# [tool.conda]
# channels = ["conda-forge", "bioconda"]
# dependencies = ["samtools>=1.19", "htslib"]
# ///
conda exec script.py
PyPI dependencies#
Use the standard PEP 723 dependencies field for PyPI packages:
# /// script
# dependencies = ["requests>=2.31", "rich"]
# ///
conda exec script.py
Warning
PyPI dependencies require conda-pypi to be installed. Without it, conda-exec cannot resolve PyPI packages and will exit with an error.
conda-exec adds the conda-pypi channel automatically.
Install conda-pypi if you haven’t already:
conda install -n base -c conda-forge conda-pypi
Mixed conda and PyPI dependencies#
Combine both in a single script:
# /// script
# requires-python = ">=3.12"
# dependencies = ["requests"]
#
# [tool.conda]
# channels = ["conda-forge", "bioconda"]
# dependencies = ["samtools>=1.19"]
# ///
Both conda and PyPI requirements are included in one environment solve.
conda-pypi exposes PyPI packages through a conda channel so the solver can
handle the combined dependency set.
Pin the Python version#
Use requires-python to constrain which Python version is installed:
# /// script
# requires-python = ">=3.12"
# dependencies = ["click"]
# ///
This adds a python >=3.12 spec to the environment solve.
Add extra dependencies from the CLI#
Add dependencies and channels for one run from the command line:
conda exec --with numpy -c defaults script.py
CLI extras (--with and -c) are merged with the script’s declared
dependency input. They create a separate cache entry and bypass discovered
lock data for that run.
Scripts without metadata#
Note
Scripts without a # /// script block run directly with the current
Python interpreter. No environment is created, and no packages are installed.
Scripts without metadata are passed through as-is:
conda exec hello.py
Make scripts directly executable#
Add a shebang line to run scripts without typing conda exec or ce:
#!/usr/bin/env ce
# /// script
# [tool.conda]
# channels = ["conda-forge"]
# dependencies = ["numpy"]
# ///
import numpy as np
print(np.random.default_rng().random(5))
chmod +x demo.py
./demo.py
The ce standalone command is the recommended shebang target because it
works on all platforms. Using conda exec in a shebang does not work
because the kernel cannot split multi-word interpreter arguments portably.
Note
On macOS and recent Linux kernels, #!/usr/bin/env -S conda exec works
via the -S (split string) flag, but this is not portable to all systems.
Use #!/usr/bin/env ce for maximum compatibility.
Force re-creation#
If the cached script environment is stale:
conda exec --refresh script.py
Reproduce exact environments#
Use --lock when the resolved package versions should be recorded:
conda exec --lock script.py
With the default rattler-lock-v6 exporter, this writes
script.py.conda-exec.lock. Future conda exec script.py runs use the lock
data instead of solving from metadata when possible.
For single-file distribution, embed generated lock data in the script:
conda exec --lock --embed script.py
See Lock script environments for sidecar, embedded, and multi-platform lock workflows.