PyPI repositories work with the pip, uv, poetry, and twine clients.
The examples use the conventional names pypi-proxy and pypi-hosted.
Connecting to the proxy
Point the client at the repository index — the path ends with /simple/:
pip install --index-url https://<USERNAME>:<PASSWORD>@<GALLEON_HOST>/repository/pypi-proxy/simple/ six==1.16.0For uv, add the index to pyproject.toml
(credentials go in the UV_INDEX_GALLEON_USERNAME and UV_INDEX_GALLEON_PASSWORD variables):
[[tool.uv.index]]
name = "galleon"
url = "https://<GALLEON_HOST>/repository/pypi-proxy/simple/"
default = trueFor poetry
(credentials — POETRY_HTTP_BASIC_GALLEON_USERNAME and POETRY_HTTP_BASIC_GALLEON_PASSWORD):
poetry source add --priority=primary galleon https://<GALLEON_HOST>/repository/pypi-proxy/simple/Private packages
Packages from the hosted repository are wired as an additional index next to the proxy.
For pip — an extra index:
pip install --index-url https://<GALLEON_HOST>/repository/pypi-proxy/simple/ \
--extra-index-url https://<GALLEON_HOST>/repository/pypi-hosted/simple/ mypkgWith several indexes, pip may pick a package from any of them — private package names must be unique relative to the public PyPI (dependency confusion protection).
For uv — a second [[tool.uv.index]] block with the pypi-hosted address
and pinning the private package to it via [tool.uv.sources]:
[[tool.uv.index]]
name = "galleon-private"
url = "https://<GALLEON_HOST>/repository/pypi-hosted/simple/"
[tool.uv.sources]
mypkg = { index = "galleon-private" }Publishing
twine, uv publish, and poetry publish are supported.
Authentication is username and password only (or the “Name code” and “Pass code” of a token);
the single-secret __token__ format is not supported.
Publishing with twine (~/.pypirc):
[distutils]
index-servers = galleon
[galleon]
repository = https://<GALLEON_HOST>/repository/pypi-hosted/
username = <USERNAME>
password = <PASSWORD>python -m build
twine upload --repository galleon dist/*Publishing with uv:
UV_PUBLISH_USERNAME=<USERNAME> UV_PUBLISH_PASSWORD=<PASSWORD> \
uv publish --publish-url https://<GALLEON_HOST>/repository/pypi-hosted/ dist/*Specifics
Format specifics:
- Published versions are immutable: re-uploading a file is rejected with “File already exists”. For idempotent CI publishing use
uv publish --check-urlorpoetry publish --skip-existing— they succeed on byte-identical already-uploaded files. - Project names are normalized per the PyPI rules: any spelling (
Foo_Bar,foo.bar) leads to the single canonical pagefoo-bar. - Wheel metadata is served as separate files (PEP 658) —
uv lockgathers dependency information without downloading the packages themselves.