uv: uv fails to use extra index url
I’m passing an extra index URL, but uv seems to only find package versions from PyPI.org.
Here is a reproduction:
# Create an experimentation directory.
mkdir repro-uv-extra-urls
cd repro-uv-extra-urls
# Run a local PyPI-like server.
mkdir dists
pipx run pypiserver run dists --disable-fallback -p8000 -a. -P. &>/dev/null &
# Create a pyproject.toml for a project called "ruff", version 1000.
cat <<EOF >pyproject.toml
[project]
name = "ruff"
version = "1000"
description = "Ruff from the future."
authors = [{name = "Charlie Marsh", email = "charlie@marsh.com"}]
readme = "README.md"
requires-python = ">=3.8"
classifiers = ["Development Status :: 1 - Planning"]
EOF
# Create a README.md file.
cat <<EOF >README.md
# Ruff
Hello.
EOF
# Build Python distributions for this package.
pipx run --spec build pyproject-build
# Upload both wheel and sdist to our local PyPI-like index.
pipx run twine upload -u "" -p "" --repository-url http://localhost:8000 dist/*
# Assert dists were uploaded.
[ ! -f dists/ruff-1000-py3-none-any.whl ] && echo "Wheel not uploaded"
[ ! -f dists/ruff-1000.tar.gz ] && echo "Source distribution not uploaded"
# Create a venv.
uv venv --seed
# Assert uv fails to install ruff==1000
uv pip install --extra-index-url http://localhost:8000/simple ruff==1000 && echo "Working, not expected" || echo "Failing, as expected"
# Assert pip manages to install ruff==1000
.venv/bin/pip install --extra-index-url http://localhost:8000/simple ruff==1000 && echo "Working, as expected" || echo "Failing, not expected"
About this issue
- Original URL
- State: closed
- Created 5 months ago
- Reactions: 8
- Comments: 17 (7 by maintainers)
Commits related to this issue
- distribution-types: flip the ordering of index preference Previously, we would prioritize `--index-url` over all `--extra-index-url` values. But now, we prioritize all `--extra-index-url` values over... — committed to astral-sh/uv by BurntSushi 4 months ago
- distribution-types: flip the ordering of index preference Previously, we would prioritize `--index-url` over all `--extra-index-url` values. But now, we prioritize all `--extra-index-url` values over... — committed to astral-sh/uv by BurntSushi 4 months ago
- distribution-types: flip the ordering of index preference Previously, we would prioritize `--index-url` over all `--extra-index-url` values. But now, we prioritize all `--extra-index-url` values over... — committed to astral-sh/uv by BurntSushi 4 months ago
- tweak the order of index priority (#2083) Previously, `uv` would always prioritize the index given by `--index-url`. It would then try any indexes after that given by zero or more `--extra-index-ur... — committed to astral-sh/uv by BurntSushi 4 months ago
Please consider dependency confusion attacks: https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610
Use of
--extra-index-url
as they are presently used are a security vulnerability.PEP 708 is a yet-to-be-implemented approach to improving the security posture.
In short:
pypiserver
by default falls back to PyPI.org when it can’t find the specified project within its own distributions. If it finds the package within its own dists, it does not look into PyPI.org. So if I allow it to fall back, and I point uv at it, I get what I wanted: my private, local packages take precedence over packages on PyPI.org, even if more recent versions are on PyPI.org 🎉 Perfs are good 🙂Tagging latest maintainer @dee-me-tree-or-love in case that’s of interest to them 😄
I’ve quickly read PEP 708, and I definitely support it instead of ordered indexes. So you can discard my previous comments stating “what I want”. With PEP 708 I’ll be able to set private projects to “track” the same ones on PyPI.org. This should give me what I want.
pypiserver
actually has a “fallback” feature which is possibly already solving my use-case needs (though depending on its implementation could cancel the perfs offered by uv, I’ll see and report back).I’ll explain my use-case since uv might want to deviate from what pip does (for good reasons) 🙂
My projects follow a sponsorware strategy, where there’s a public version, and a private version with more features. Sponsorships above a certain amount per month grant access to these private repositories on GitHub. To simplify local development, as well as allowing contributors without access to these private versions, I use a local index to store built distributions of these private projects. Details here: https://pawamoy.github.io/pypi-insiders/. It means I can specify
some-project
in my dependencies, instead of hardcodinggit+https://token@github.com/org/private-repo
. pip is then able to fetch packages from both PyPI.org and my local index, if configured as such (pip’s config file, env var, cli flag, etc.).Current situation with pip:
some-project
above) in both indices (PyPI.org and http://localhost:XXXX).major.minor.patch
for public versions, andmajor.minor.patch.pmajor.pminor.ppatch
wherepmajor.pminor.ppatch
is the private version (based on the public one, as a fork), private versions are always “higher” in terms of versioning than the public ones. So pip fetches private versions from the local index, unless there’s an even higher public version on PyPI.org without its private equivalent (for example2.0.0
on PyPI.org, but only1.2.3.1.0.0
and no2.0.0.1.0.0
in the local index).This leads me to this desired situation with uv:
prefer-index = "http://localhost:8000/simple/"
in~/.config/uv/conf.toml
(totally invented) would be perfect. Thisprefer-index
setting would be no-op if it is not also passed as an extra-index-url. This way it lets me choose whether to fetch packages from my preferred index, or not when I need to test public versions:Note that in my use-case, there are no security concerns, as there is no concept of internal versus public projects, with the latter being able to shadow the former. Projects are the same in both indices, just with different versions. For use-cases actually involving internal packages, I do understand the need to enforce fetching specific packages from a specific index, and I believe the mentioned
prefer-index
setting above would solve that too?Maybe case 2 above shouldn’t be supported at all, and then we would just need to reverse the semantic of
extra-index-url
, without needing any new config option: giveextra-index-url
precedence over the main index. If you can keep the order of multiple extra-index-urls, then give them precedence according to this order. But if an extra index is not reachable, or does not contain a package, do not fail and fall back to the next one (maybe not secure enough though, as you never want to fall back for internal packages).Let me know if anything was unclear!
Thanks for the clear write-up!
Yes! Currently, I expect pip to look into every specified index to satisfy the dependency specification (without order/precedence).
I actually don’t know what the “right” behavior is here. My guess is that we’re “supposed” to look at both indexes (though the order in which indexes are searched is not guaranteed in pip).