Skip to main content

You've found one of our experimental articles! Have ideas or feedback for how we can further improve this article? Let us know in the discussion.

Building and testing Python

This guide shows you how to build, test, and publish a Python package.

GitHub-hosted runners have a tools cache with pre-installed software, which includes Python and PyPy. You don't have to install anything! For a full list of up-to-date software and the pre-installed versions of Python and PyPy, see "Specifications for GitHub-hosted runners".


You should be familiar with YAML and the syntax for GitHub Actions. For more information, see "Learn GitHub-Actions."

We recommend that you have a basic understanding of Python, PyPy, and pip. For more information, see:

Using the Python starter workflow

GitHub provides a Python starter workflow that should work for most Python projects. This guide includes examples that you can use to customize the starter workflow. For more information, see the Python starter workflow.

To get started quickly, add the starter workflow to the .github/workflows directory of your repository.

Specifying a Python version

To use a pre-installed version of Python or PyPy on a GitHub-hosted runner, use the setup-python action. This action finds a specific version of Python or PyPy from the tools cache on each runner and adds the necessary binaries to PATH, which persists for the rest of the job. If a specific version of Python is not pre-installed in the tools cache, the setup-python action will download and set up the appropriate version from the python-versions repository.

Using the setup-python action is the recommended way of using Python with GitHub Actions because it ensures consistent behavior across different runners and different versions of Python. If you are using a self-hosted runner, you must install Python and add it to PATH. For more information, see the setup-python action.

The table below describes the locations for the tools cache in each GitHub-hosted runner.

Tool Cache Directory/opt/hostedtoolcache/*/Users/runner/hostedtoolcache/*C:hostedtoolcachewindows*
Python Tool Cache/opt/hostedtoolcache/Python/*/Users/runner/hostedtoolcache/Python/*C:hostedtoolcachewindowsPython*
PyPy Tool Cache/opt/hostedtoolcache/PyPy/*/Users/runner/hostedtoolcache/PyPy/*C:hostedtoolcachewindowsPyPy*

If you are using a self-hosted runner, you can configure the runner to use the setup-python action to manage your dependencies. For more information, see using setup-python with a self-hosted runner in the setup-python README.

GitHub supports semantic versioning syntax. For more information, see "Using semantic versioning" and the "Semantic versioning specification."

Using multiple Python versions

This example uses a matrix to run the job on multiple Python versions: 2.7, 3.6, 3.7, 3.8, and 3.9. For more information about matrix strategies and contexts, see "Workflow syntax for GitHub Actions" and "Context and expression syntax for GitHub Actions."

Using a specific Python version

You can configure a specific version of python. For example, 3.8. Alternatively, you can use semantic version syntax to get the latest minor release. This example uses the latest minor release of Python 3.

Excluding a version

If you specify a version of Python that is not available, setup-python fails with an error such as: ##[error]Version 3.4 with arch x64 not found. The error message includes the available versions.

You can also use the exclude keyword in your workflow if there is a configuration of Python that you do not wish to run. For more information, see "Workflow syntax for GitHub Actions."

Using the default Python version

We recommend using setup-python to configure the version of Python used in your workflows because it helps make your dependencies explicit. If you don't use setup-python, the default version of Python set in PATH is used in any shell when you call python. The default version of Python varies between GitHub-hosted runners, which may cause unexpected changes or use an older version than expected.

GitHub-hosted runnerDescription
UbuntuUbuntu runners have multiple versions of system Python installed under /usr/bin/python and /usr/bin/python3. The Python versions that come packaged with Ubuntu are in addition to the versions that GitHub installs in the tools cache.
WindowsExcluding the versions of Python that are in the tools cache, Windows does not ship with an equivalent version of system Python. To maintain consistent behavior with other runners and to allow Python to be used out-of-the-box without the setup-python action, GitHub adds a few versions from the tools cache to PATH.
macOSThe macOS runners have more than one version of system Python installed, in addition to the versions that are part of the tools cache. The system Python versions are located in the /usr/local/Cellar/python/* directory.

Installing dependencies

GitHub-hosted runners have the pip package manager installed. You can use pip to install dependencies from the PyPI package registry before building and testing your code. This example installs or upgrades the pip package installer and the setuptools and wheel packages.

When using GitHub-hosted runners, you can also cache dependencies to speed up your workflow. For more information, see "Caching dependencies to speed up workflows."

Requirements file

After you update pip, a typical next step is to install dependencies from requirements.txt.

Caching Dependencies

When using GitHub-hosted runners, you can cache and restore the dependencies using the setup-python action. By default, the setup-python action searches for the dependency file (requirements.txt for pip or Pipfile.lock for pipenv) in the whole repository.

If you have a custom requirement or need finer controls for caching, you can use the cache action. Pip caches dependencies in different locations, depending on the operating system of the runner. The path you'll need to cache may differ from the Ubuntu example shown here, depending on the operating system you use. For more information, see Caching dependencies to speed up workflows and the cache action.

Testing your code

You can use the same commands that you use locally to build and test your code.

Testing with pytest and pytest-cov

This example installs or upgrades pytest and pytest-cov. Tests are then run and output in JUnit format while code coverage results are output in Cobertura. For more information, see JUnit and Cobertura.

Using Flake8 to lint code

This example installs or upgrades flake8 and uses it to lint all files. For more information, see Flake8.

Running tests with tox

With GitHub Actions, you can run tests with tox and spread the work across multiple jobs. You'll need to invoke tox using the -e py option to choose the version of Python in your PATH, rather than specifying a specific version. For more information, see tox.

Packaging workflow data as artifacts

You can upload artifacts to view after a workflow completes. For example, you may need to save log files, core dumps, test results, or screenshots. For more information, see "Persisting workflow data using artifacts."

This example demonstrates how you can use the upload-artifact action to archive test results from running pytest. For more information, see the upload-artifact action.

Publishing to package registries

You can configure your workflow to publish your Python package to a package registry once your CI tests pass. This example demonstrates how you can use GitHub Actions to upload your package to PyPI each time you publish a release.

For this example, you will need to create two PyPI API tokens. You can use secrets to store the access tokens or credentials needed to publish your package. For more information, see "Creating and using encrypted secrets."

For more information about the starter workflow, see python-publish.

bouncing octocatLoading...