Upload package#

Finally, you can deploy the package on the Python Package Index (PyPI) or another index, for example GitLab Package Registry or devpi.

For this you should register on Test PyPI. Test-PyPI is a separate instance that is intended for testing and experimentation. To set up an account there, go to https://test.pypi.org/account/register/. For more information, see Using TestPyPI.

Now you can create the ~/.pypirc file:


repository = https://test.pypi.org/legacy/
username = veit

See also

If you’d like to automate PyPI registration, please 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:

$ python -m pip install --upgrade pip build twine

All dependencies are now up-to-date!


Run this command before each release to ensure that all release tools are up to date.

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:

$ 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 you used to register on 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]


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:

  1. change the name argument in the setup.py file

  2. remove the dist directory

  3. regenerate the archives



You can use pip to install your package and check if it works. Create a new virtual environment and install your package on Test PyPI:

$ python3 -m venv test_env
$ source test_env/bin/activate
$ pip install -i https://test.pypi.org/simple/ minimal_example


If you have used a different package name, replace it with your package name in the command above.

pip should install the package from Test PyPI and the output should look something like this:

Looking in indexes: https://test.pypi.org/simple/
Collecting minimal_example

Installing collected packages: minimal_example
Successfully installed minimal_example-0.0.1

You can test whether your package has been installed correctly by importing the module and referencing the name property that was previously ntered in __init__.py:

$ python
Python 3.7.0 (default, Aug 22 2018, 15:22:29)

>>> import minimal_example
>>> minimal_example.name


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/*.


Also check whether the README.rst is displayed correctly on the test PyPI page.


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:


repository = https://test.pypi.org/legacy/
username = veit

username = __token__

With this configuration, the name/password combination is no longer used for uploading but an upload token.

Finally, you can publish your package on PyPI:

$ twine upload -r pypi -s dist/*


You cannot simply replace releases as you cannot re-upload packages with the same version number.


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 ===.

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
 3 on:
 4   release:
 5     types: [created]
 8  test:
10  package-and-deploy:
11    runs-on: ubuntu-latest
12    needs: [test]
13    steps:
14    - name: Checkout
15      uses: actions/checkout@v2
16      with:
17        fetch-depth: 0
18    - name: Set up Python
19      uses: actions/setup-python@v5
20      with:
21        python-version: '3.11'
22        cache: pip
23        cache-dependency-path: '**/pyproject.toml'
24    - name: Install dependencies
25      run: |
26        python -m pip install -U pip
27        python -m pip install -U setuptools build twine wheel
28    - name: Build
29      run: |
30        python -m build
31    - name: Publish
32      env:
33        TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
34        TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
35      run: |
36        twine upload dist/*
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.

See also

Trusted Publishers#

Trusted Publishers is an alternative method 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:

  1. 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:

  2. 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 Settings ‣ Environments for the repository. The name of our environment is release.

  3. Configure the workflow

    To do this, we now create the .github/workflows/publish.yml file in our repository:

     4  deploy:
     5    runs-on: ubuntu-latest
     6    environment: release
     7    permissions:
     8      id-token: write
     9    needs: [test]
    10    steps:
    11    - name: Checkout
    13    - name: Set up Python
    15    - name: Install dependencies
    17    - name: Build
    19    - name: Publish
    20      uses: pypa/gh-action-pypi-publish@release/v1
    Line 6

    This is needed because we have configured an environment in PyPI.

    Lines 7–8

    They are required for the OpenID Connect token authentication to work.

    Lines 19–20

    The package uses the github.com/pypa/gh-action-pypi-publish action to publish the package.