Publish package¶
Finally, you can deploy the package on the Python Package Index (PyPI) or another index, for example GitLab Package Registry or devpi.
For the Python Package Index, you must register with Test PyPI . Test PyPI is a separate instance that is intended for testing and experimenting. To set up an account there, go to https://test.pypi.org/account/register/. Further information can be found at Using TestPyPI.
Now you can create the ~/.pypirc
file:
[distutils]
index-servers=
test
[test]
repository = https://test.pypi.org/legacy/
username = veit
See also
If you’d like to automate PyPI registration, read Careful With That PyPI.
After you are registered, you can upload your Distribution Package with twine. To do this, however, you must first install twine with:
$ uv add --upgrade twine
Tip
Run this command before each release to ensure that all release tools are up to date.
After installing twine
you can upload all archives under /dist
to
the Python Package Index with:
Now you can create your Distribution Packages with:
$ cd /path/to/your/distribution_package
$ rm -rf build dist
$ python -m build
After installing Twine you can upload all archives in /dist
to the Python
Package Index with:
$ uv run twine upload -r test -s dist/*
-r
,--repository
The repository to upload the package.
In our case, the
test
section from the~/.pypirc
file is used.-s
,--sign
signs the files to be uploaded with GPG.
You will be asked for the password of your signature key that you used to register with Test PyPI . You should then see a similar output:
Uploading distributions to https://test.pypi.org/legacy/
Enter your username: veit
Enter your password:
Uploading example-0.0.1-py3-none-any.whl
100%|█████████████████████| 4.65k/4.65k [00:01<00:00, 2.88kB/s]
Uploading example-0.0.1.tar.gz
100%|█████████████████████| 4.25k/4.25k [00:01<00:00, 3.05kB/s]
Note
If you get an error message similar to
The user 'veit' isn't allowed to upload to project 'example'
you have to choose a unique name for your package:
change the
name
argument in thesetup.py
fileremove the
dist
directoryregenerate the archives
Check¶
Installation¶
You can use uv
to install your package from Test PyPI and check if it
works:
$ $ uv add -i https://test.pypi.org/simple/ mypack
Note
If you have used a different package name, replace it with your package name in the command above.
uv add
should install the package from Test PyPI and the output should
look something like this:
Resolved 8 packages in 5ms
Installed 7 packages in 36ms
+ mypack==0.1.0
You can test whether your package has been installed correctly by calling
main()
:
$ uv run mypack
Hello from mypack!
Note
The packages on Test-PyPI are only stored temporarily. If you want to
upload a package to the real Python Package Index (PyPI),
you can do so by creating an account on pypi.org and following the
same instructions, but using twine upload dist/*
.
README¶
Also check whether the README.rst
is displayed correctly on the test PyPI
page.
PyPI¶
Now register on the Python Package Index (PyPI) and make sure
that two-factor authentication
is activated by adding the following to the ~/.pypirc
file:
[distutils]
index-servers=
pypi
test
[test]
repository = https://test.pypi.org/legacy/
username = veit
[pypi]
username = __token__
With this configuration, the name/password combination is no longer used for uploading but an upload token.
See also
Finally, you can publish your package on PyPI:
$ uv run twine upload -r pypi -s dist/*
Note
You cannot simply replace releases as you cannot re-upload packages with the same version number.
Note
Do not remove old versions from the Python Package Index.This only causes
work for those who want to keep using that version and then have to switch
to old versions on GitHub. PyPI has a yank function that you can use instead. This
will ignore a particular version if it is not explicitly specified with
==
or ===
.
See also
GitHub Action¶
You can also create a GitHub action, which creates a package and uploads it to
PyPI at every time a release is created. Such a
.github/workflows/pypi.yml
file could look like this:
1name: Publish Python Package
2
3 on:
4 release:
5 types: [created]
6
7jobs:
8 test:
9 …
10 package-and-deploy:
11 runs-on: ubuntu-latest
12 needs: [test]
13 steps:
14 - name: Checkout
15 uses: actions/checkout@v4
16 with:
17 fetch-depth: 0
18 - name: Set up Python
19 uses: actions/setup-python@v5
20 with:
21 python-version-file: .python-version
22 cache-dependency-path: '**/pyproject.toml'
23 - name: Setup cached uv
24 uses: hynek/setup-cached-uv@v2
25 - name: Create venv and install twine
26 run: |
27 uv venv
28 echo "$PWD/.venv/bin" >> $GITHUB_PATH
29 uv add --upgrade twine
30 - name: Build
31 run: |
32 uv build
33 - name: Retrieve and publish
34 steps:
35 - name: Retrieve release distributions
36 uses: actions/download-artifact@v4
37 - name: Publish package distributions to PyPI
38 uses: pypa/gh-action-pypi-publish@release/v1
39 with:
40 username: __token__
41 password: ${{ secrets.PYPI_TOKEN }}
- Lines 3–5
This ensures that the workflow is executed every time a new GitHub release is created for the repository.
- Line 12
The job waits for the
test
job to pass before it is executed.- Line 31
Here
mypack
should be replaced by your package name.- Line 36
The GitHub action
actions/download-artifact
provides the built distribution packages.- Lines 38–41
The GitHub action
pypa/gh-action-pypi-publish
publishes the packages with the upload token on PyPI.
See also
Trusted Publishers¶
Trusted Publishers is a procedure for publishing packages on the PyPI. It is based on OpenID Connect and requires neither a password nor a token. Only the following steps are required:
Add a Trusted Publishers on PyPI
Depending on whether you want to publish a new package or update an existing one, the process is slightly different:
to update an existing package, see Adding a trusted publisher to an existing PyPI project
to publish a new package, there is a special procedure called Pending Publisher; see also Creating a PyPI project with a trusted publisher
You can also use it to reserve a package name before you publish the first version. This allows you to ensure that you can publish the package under the desired name.
To do this, you need to create a new Pending Publisher in pypi.org/manage/account/publishing/ with
Name of the PyPI project
GitHub repository owner
Name of the workflow, for example
publish.yml
Name of the environment (optional), for example
release
Create an environment for the GitHub actions
If we have specified an environment on PyPI, we must now also create it. This can be done in for the repository. The name of our environment is
release
.Configure the workflow
To do this, we now create the
.github/workflows/publish.yml
file in our repository:10 package-and-deploy: 11 runs-on: ubuntu-latest 12 + environment: release 13 + permissions: 14 + id-token: write 15 needs: [test] 16 steps:
- Line 12
The specification of a GitHub environment is optional, but strongly recommended.
- Lines 13–14
The
write
authorisation is required for Trusted Publishing.- Zeilen 42–44
username
andpassword
are no longer required for the GitHub actionpypa/gh-action-pypi-publish
.40 - name: Publish package distributions to PyPI 41 uses: pypa/gh-action-pypi-publish@release/v1 42- with: 43- username: __token__ 44- password: ${{ secrets.PYPI_TOKEN }}
Digital Attestations¶
Since 14 November 2024, PyPI also supports PEP 740 with Digital Attestations. PyPI uses the in-toto Attestation Framework to generate the Digital Attestations SLSA Provenance and PyPI Publish Attestation (v1).
The creation and publication takes place by default, provided that Trusted Publishing and the GitHub action pypa/gh-action-pypi-publish are used for publishing:
jobs:
pypi-publish:
name: Upload release to PyPI
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/{YOUR-PYPI-PROJECT-NAME}
permissions:
id-token: write
steps:
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
Note
Support for the automatic creation of digital attestations and publishing from other Trusted Publisher environments is planned.