Python with pip, uv and Poetry
This page sets up pip, uv or Poetry to install packages from Telesphorepo. To publish a package, see Publish from a project, under Publish your packages; what is different for Python is under Publish to this repository.
An admin first makes a repository with the format Python (PyPI) (see Make a repository). The examples use the server repo.example.com and a repository named python, so the index is this:
https://repo.example.com/pypi/python/simple/Put your own values in the blocks below. The Use this repository tab of the repository shows the same blocks with your values.
1Which tool?
2What does the repository hold?
3Who can download from the repository?
Install packages
Section titled “Install packages”-
Make a read token.
Anyone can download
Skip this step. Anyone can download.
Only signed-in people
Settings › Tokens › New read token. Type a name, then click Create token.
-
Point the tool at the repository.
pip, anyone can download
~/.config/pip/pip.conf [global]index-url = https://repo.example.com/pypi/python/simple/On Windows, the file is
%APPDATA%\pip\pip.ini.pip, only signed-in people
~/.config/pip/pip.conf [global]index-url = https://YOUR_USERNAME:YOUR_READ_TOKEN@repo.example.com/pypi/python/simple/pip reads the username and the token from the address. On Windows, the file is
%APPDATA%\pip\pip.ini. The file holds your token, so keep it in your home folder.uv
pyproject.toml [[tool.uv.index]]name = "python"url = "https://repo.example.com/pypi/python/simple/"default = trueuv, only signed-in people
Then give uv your username and the token. Paste the token at the hidden prompt.
Terminal window printf 'Read token: '; read -rs READ_TOKEN; echoexport UV_INDEX_PYTHON_USERNAME=YOUR_USERNAMEexport UV_INDEX_PYTHON_PASSWORD="$READ_TOKEN"The variables name the index in capitals, with
_for every character other than a letter or a digit. In CI, set them as secrets.Poetry, anyone can download
Terminal window poetry source add --priority=primary python https://repo.example.com/pypi/python/simple/Poetry, only signed-in people
Paste the token at the hidden prompt.
Terminal window poetry source add --priority=primary python https://repo.example.com/pypi/python/simple/printf 'Read token: '; read -rs READ_TOKEN; echopoetry config http-basic.python YOUR_USERNAME "$READ_TOKEN" -
Install a package.
pip
Terminal window pip install "acme-billing==1.2.0"uv
Terminal window uv add "acme-billing==1.2.0"Poetry
Terminal window poetry add "acme-billing==1.2.0"
Done when the tool installs the package with no error.
Your own packages
A hosted repository holds your own packages only. pip then cannot install their dependencies from PyPI through it. To install both through one index, ask your server admin for a group of your repository and a proxy of PyPI. Then point the tool at the group.
Public packages too
A group asks its members in order. The first member that has a project name owns it, and the group serves every file of that project from that member only. So a package on PyPI with the name of your private package cannot take its place, even with a higher version. Your admin puts your hosted repository before the proxy.
Publish to this repository
Section titled “Publish to this repository”Publish as in Publish from a project: run the setup line in the folder that holds pyproject.toml or setup.py, commit telesphorepo.toml, then run telesphorepo publish.
Done when the CLI prints Uploaded for each file, for example Uploaded acme_billing-1.2.0-py3-none-any.whl (acme-billing 1.2.0) to python.
The CLI builds the project into a temporary folder, then uploads every wheel and sdist of the build:
| Project | The CLI builds with |
|---|---|
[tool.poetry] in pyproject.toml |
poetry build |
a uv.lock |
uv build |
| anything else | python -m build. Install it with python -m pip install build |
To upload files that you built, name them: telesphorepo publish dist/*.whl. The CLI then builds nothing.
A file that is on the server already, with other bytes, answers 409 File already exists. Raise the version.
Publish with twine, uv or Poetry
These tools upload with a publish token. Make one in Settings › Tokens › New publish token: choose the repository, then confirm with your second factor. The token works for 15 minutes. The username is __token__, and the upload address is https://repo.example.com/pypi/python/.
twine asks for the token at its password prompt:
twine upload --repository-url https://repo.example.com/pypi/python/ -u __token__ dist/*uv reads the token from the environment:
printf 'Publish token: '; read -rs UV_PUBLISH_TOKEN; echoexport UV_PUBLISH_TOKENuv publish --publish-url https://repo.example.com/pypi/python/Poetry reads it from the environment too:
poetry config repositories.telesphorepo https://repo.example.com/pypi/python/printf 'Publish token: '; read -rs POETRY_HTTP_BASIC_TELESPHOREPO_PASSWORD; echoexport POETRY_HTTP_BASIC_TELESPHOREPO_USERNAME=__token__ POETRY_HTTP_BASIC_TELESPHOREPO_PASSWORDpoetry publish --build -r telesphorepoA password or a read token cannot upload.
The CI file for Python
Section titled “The CI file for Python”Publish from CI has the steps. When you choose GitHub Actions at setup, the CLI writes the file for the build tool of the project:
GitHub Actions with pip
on: push: branches: ["main"]
permissions: id-token: write contents: read
jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - uses: actions/setup-python@v6 with: python-version: 3.x - run: python -m pip install build - run: curl -fsSL https://repo.example.com/install.sh | sh - run: telesphorepo publishGitHub Actions with uv
on: push: branches: ["main"]
permissions: id-token: write contents: read
jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - uses: astral-sh/setup-uv@v7 - run: curl -fsSL https://repo.example.com/install.sh | sh - run: telesphorepo publishGitHub Actions with Poetry
on: push: branches: ["main"]
permissions: id-token: write contents: read
jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - uses: actions/setup-python@v6 with: python-version: 3.x - run: pipx install poetry - run: curl -fsSL https://repo.example.com/install.sh | sh - run: telesphorepo publishFor Jenkins, see Publish from CI.
If something goes wrong
Section titled “If something goes wrong”pip ignores an http:// index
pip uses an http:// index only on localhost, or when its host is trusted. Add trusted-host = repo.example.com under [global] in pip.conf. For real use, serve the server over HTTPS. See Serve over HTTPS.
pip cannot find a dependency, such as requests
The repository is hosted, and holds your own packages only. Ask your server admin for a group with a proxy of PyPI.
401 when you install
The repository is for signed-in people only. Check the username and the read token. For uv, the variables must name the index in capitals: UV_INDEX_PYTHON_USERNAME for the index python.
A group serves your private package, not the public one with the same name
The first member that has a project owns it. Rename your project, or ask your admin to change the order of the members.
409 File already exists
A file with that name is on the server with other bytes. Raise the version. The same bytes again are fine.
400 when you upload
The name and the version in the file name must match the metadata inside the file. Build the file again with your build tool.
The CLI says that poetry, uv or python is not on the PATH
The CLI builds with the tool of the project. Install it, or build yourself and run telesphorepo publish dist/*.
twine prints only the status line
Add --verbose. twine then prints the reason that the server gives.