Use PyPI dependencies in scripts#
PEP 723 defines top-level dependencies as PyPI dependencies. conda-exec
can resolve those dependencies by using conda-pypi, which exposes PyPI
packages to conda’s solver through the conda-pypi channel.
Use this workflow when a script needs a package that is available on PyPI but is not packaged, not available at the version you need, or not convenient in your conda channels.
Install conda-pypi#
PyPI dependencies require conda-pypi in the environment that provides
conda exec:
conda install -n base -c conda-forge conda-pypi
Scripts that only use [tool.conda].dependencies do not need
conda-pypi.
Declare PyPI dependencies#
Put PyPI package requirements in the standard PEP 723 dependencies
field:
# /// script
# dependencies = ["httpx>=0.27", "rich"]
# ///
import httpx
from rich import print
response = httpx.get("https://httpbin.org/json")
print(response.json())
Run the script:
conda exec fetch.py
When conda-exec sees top-level dependencies, it checks that
conda-pypi is importable and appends the conda-pypi channel to the
solve.
Prefer conda packages for conda-native dependencies#
If a dependency is available from conda-forge, bioconda, defaults, or
another trusted conda channel, prefer [tool.conda].dependencies:
# /// script
# [tool.conda]
# channels = ["conda-forge"]
# dependencies = ["numpy>=2", "pandas", "pyarrow"]
# ///
Conda packages can express non-Python shared libraries, compiled extensions, run exports, and platform constraints in a way PyPI metadata cannot always model for conda environments.
Use top-level dependencies for packages you intentionally want from PyPI.
Mix conda and PyPI packages#
You can combine both dependency sources in one script:
# /// script
# requires-python = ">=3.12"
# dependencies = ["rich"]
#
# [tool.conda]
# channels = ["conda-forge", "bioconda"]
# dependencies = ["samtools>=1.19", "pysam"]
# ///
conda-exec builds one combined spec list:
[tool.conda].dependenciestop-level PyPI
dependenciesany
--withspecs from the command linean automatic
pythonspec if needed
The resulting environment is solved once for that dependency input, then cached.
Pin PyPI dependencies#
Use normal PEP 508 requirement strings:
# /// script
# dependencies = [
# "httpx>=0.27,<0.28",
# "rich>=13",
# ]
# ///
If you need exact repeatability, generate lock data after choosing your constraints:
conda exec --lock fetch.py
Set the Python version#
Use requires-python when PyPI dependencies need a particular Python
range:
# /// script
# requires-python = ">=3.12,<3.14"
# dependencies = ["httpx", "rich"]
# ///
conda-exec translates this to a conda python spec for the solve and
validates the resolved interpreter afterwards.
Add temporary conda packages#
Use --with for one-off conda packages without editing the script:
conda exec --with ipython fetch.py
This creates a different cached environment because CLI extras are part of the cache identity. It also bypasses discovered script lock data for that run because the dependency input has changed.
Use PyPI scripts in CI#
Install the optional integration before running the script:
- name: Install conda-exec
run: conda install -n base -c conda-forge -y conda-exec conda-pypi
- name: Run report script
run: conda exec scripts/report.py --output report.json
For stable CI, prefer bounded version ranges and commit generated lock data:
conda exec --lock scripts/report.py
Fix missing conda-pypi errors#
This error means the script has top-level PyPI dependencies, but
conda-pypi is not installed where conda-exec can import it:
conda exec: script declares PyPI dependencies but conda-pypi is not installed
Fix it by installing the integration:
conda install -n base -c conda-forge conda-pypi
Or move dependencies to [tool.conda].dependencies if you want conda
packages instead of PyPI packages.