Run a script with inline dependencies#
This tutorial walks through running a Python script that declares its dependencies using PEP 723 inline metadata.
Prerequisites#
conda 25.1 or later
conda-rattler-solver installed
conda-exec installed (
conda install conda-exec)
conda 25.1 or later
conda-rattler-solver installed
conda-exec installed (
conda install conda-exec)conda-pypi installed (
conda install conda-pypi)
Step 1: Write a script with conda dependencies#
Create a file called hello.py:
# /// script
# [tool.conda]
# channels = ["conda-forge"]
# dependencies = ["zlib"]
# ///
print("Hello from a conda-exec script!")
The # /// script and # /// markers delimit the metadata block. Inside,
TOML-formatted comments declare the script’s dependencies under
[tool.conda].
Step 2: Run it#
conda exec hello.py
On the first run, conda-exec:
Detects that
hello.pyis a file (not a package name)Parses the PEP 723 metadata block
Solves and creates a cached environment with the declared dependencies
Runs the script with
pythonfrom that environment
Creating environment for script... done (4.1s)
Hello from a conda-exec script!
Subsequent runs reuse the cached environment and start instantly.
Step 3: Add PyPI dependencies#
conda-exec supports the standard PEP 723 dependencies field for PyPI
packages. These are resolved through the
conda-pypi channel,
which requires conda-pypi to be installed.
Warning
PyPI dependencies require conda-pypi to be installed. Without it, conda-exec cannot resolve packages from PyPI. Install it with conda install conda-pypi.
Create a file called fetch.py:
# /// script
# dependencies = ["requests", "rich"]
# ///
import requests
from rich import print
response = requests.get("https://httpbin.org/ip")
print(response.json())
conda exec fetch.py
conda-exec adds the conda-pypi channel automatically when PyPI
dependencies are declared.
Step 4: Mix conda and PyPI dependencies#
The real power comes from combining both in a single script:
# /// script
# requires-python = ">=3.12"
# dependencies = ["requests"]
#
# [tool.conda]
# channels = ["conda-forge", "bioconda"]
# dependencies = ["samtools>=1.19"]
# ///
import subprocess
import requests
result = subprocess.run(["samtools", "--version"], capture_output=True, text=True)
print(f"samtools: {result.stdout.splitlines()[0]}")
print(f"requests: {requests.__version__}")
conda exec analysis.py
All dependencies (conda and PyPI) are resolved together in a single environment solve.
Step 5: Pass arguments to the script#
Arguments after the script path are passed through:
conda exec hello.py --verbose output.txt
Use -- to separate conda-exec options from script arguments:
conda exec --with numpy hello.py -- --flag value
Step 6: Make it directly executable#
Add a shebang line so the script can run without typing conda exec:
#!/usr/bin/env ce
# /// script
# [tool.conda]
# channels = ["conda-forge"]
# dependencies = ["zlib"]
# ///
print("Hello from a conda-exec script!")
chmod +x hello.py
./hello.py
The ce command is the standalone entry point for conda-exec. It works as
a shebang target on all platforms.
What happened?#
conda-exec created a cached environment at
~/.conda/exec/envs/script--<hash>/. The cache key is computed from
the script’s dependency metadata (not the file path or contents), so
changing only the code without changing the dependencies reuses the same
cached environment.
Note
Two scripts with identical dependency metadata share the same cached environment, even if they live in different directories or have different filenames. The cache key depends only on the declared dependencies, channels, and Python version constraint.