Chapter 3. Packaging Python 3 RPMs
You can install Python packages on your system either from the upstream PyPI repository using the pip
installer, or using the DNF package manager. DNF uses the RPM package format, which offers more downstream control over the software.
The packaging format of native Python packages is defined by Python Packaging Authority (PyPA) Specifications. Most Python projects use the distutils
or setuptools
utilities for packaging, and defined package information in the setup.py
file. However, possibilities of creating native Python packages have evolved over time. For more information about emerging packaging standards, see pyproject-rpm-macros.
This chapter describes how to package a Python project that uses setup.py
into an RPM package. This approach provides the following advantages compared to native Python packages:
-
Dependencies on Python and non-Python packages are possible and strictly enforced by the
DNF
package manager. - You can cryptographically sign the packages. With cryptographic signing, you can verify, integrate, and test content of RPM packages with the rest of the operating system.
- You can execute tests during the build process.
3.1. SPEC file description for a Python package
A SPEC file contains instructions that the rpmbuild
utility uses to build an RPM. The instructions are included in a series of sections. A SPEC file has two main parts in which the sections are defined:
- Preamble (contains a series of metadata items that are used in the Body)
- Body (contains the main part of the instructions)
An RPM SPEC file for Python projects has some specifics compared to non-Python RPM SPEC files.
A name of any RPM package of a Python library must always include the python3-
, python3.11-
, or python3.12-
prefix.
Other specifics are shown in the following SPEC file example for the python3*-pello
package. For description of such specifics, see the notes below the example.
An example SPEC file for the pello program written in Python
%global python3_pkgversion 3.11 1 Name: python-pello 2 Version: 1.0.2 Release: 1%{?dist} Summary: Example Python library License: MIT URL: https://github.com/fedora-python/Pello Source: %{url}/archive/v%{version}/Pello-%{version}.tar.gz BuildArch: noarch BuildRequires: python%{python3_pkgversion}-devel 3 # Build dependencies needed to be specified manually BuildRequires: python%{python3_pkgversion}-setuptools # Test dependencies needed to be specified manually # Also runtime dependencies need to be BuildRequired manually to run tests during build BuildRequires: python%{python3_pkgversion}-pytest >= 3 %global _description %{expand: Pello is an example package with an executable that prints Hello World! on the command line.} %description %_description %package -n python%{python3_pkgversion}-pello 4 Summary: %{summary} %description -n python%{python3_pkgversion}-pello %_description %prep %autosetup -p1 -n Pello-%{version} %build # The macro only supported projects with setup.py %py3_build 5 %install # The macro only supported projects with setup.py %py3_install %check 6 %{pytest} # Note that there is no %%files section for the unversioned python module %files -n python%{python3_pkgversion}-pello %doc README.md %license LICENSE.txt %{_bindir}/pello_greeting # The library files needed to be listed manually %{python3_sitelib}/pello/ # The metadata files needed to be listed manually %{python3_sitelib}/Pello-*.egg-info/
- 1
- By defining the
python3_pkgversion
macro, you set which Python version this package will be built for. To build for the default Python version 3.9, either set the macro to its default value3
or remove the line entirely. - 2
- When packaging a Python project into RPM, always add the
python-
prefix to the original name of the project. The original name here ispello
and, therefore, the name of the Source RPM (SRPM) ispython-pello
. - 3
- BuildRequires specifies what packages are required to build and test this package. In BuildRequires, always include items providing tools necessary for building Python packages:
python3-devel
(orpython3.11-devel
orpython3.12-devel
) and the relevant projects needed by the specific software that you package, for example,python3-setuptools
(orpython3.11-setuptools
orpython3.12-setuptools
) or the runtime and testing dependencies needed to run the tests in the %check section. - 4
- When choosing a name for the binary RPM (the package that users will be able to install), add a versioned Python prefix. Use the
python3-
prefix for the default Python 3.9, thepython3.11-
prefix for Python 3.11, or thepython3.12-
prefix for Python 3.12. You can use the%{python3_pkgversion}
macro, which evaluates to3
for the default Python version 3.9 unless you set it to an explicit version, for example,3.11
(see footnote 1). - 5
- The %py3_build and %py3_install macros run the
setup.py build
andsetup.py install
commands, respectively, with additional arguments to specify installation locations, the interpreter to use, and other details. - 6
- The %check section should run the tests of the packaged project. The exact command depends on the project itself, but it is possible to use the %pytest macro to run the
pytest
command in an RPM-friendly way.
3.2. Common macros for Python 3 RPMs
In a SPEC file, always use the macros that are described in the following Macros for Python 3 RPMs table rather than hardcoding their values. You can redefine which Python 3 version is used in these macros by defining the python3_pkgversion
macro on top of your SPEC file (see Section 3.1, “SPEC file description for a Python package”). If you define the python3_pkgversion
macro, the values of the macros described in the following table will reflect the specified Python 3 version.
Macro | Normal Definition | Description |
---|---|---|
%{python3_pkgversion} | 3 |
The Python version that is used by all other macros. Can be redefined to |
%{python3} | /usr/bin/python3 | The Python 3 interpreter |
%{python3_version} | 3.9 | The major.minor version of the Python 3 interpreter |
%{python3_sitelib} | /usr/lib/python3.9/site-packages | The location where pure-Python modules are installed |
%{python3_sitearch} | /usr/lib64/python3.9/site-packages | The location where modules containing architecture-specific extension modules are installed |
%py3_build |
Runs the | |
%py3_install |
Runs the | |
%{py3_shebang_flags} | s |
The default set of flags for the Python interpreter directives macro, |
%py3_shebang_fix |
Changes Python interpreter directives to |
Additional resources
3.3. Using automatically generated dependencies for Python RPMs
The following procedure describes how to use automatically generated dependencies when packaging a Python project as an RPM.
Prerequisites
- A SPEC file for the RPM exists. For more information, see SPEC file description for a Python package.
Procedure
Make sure that one of the following directories containing upstream-provided metadata is included in the resulting RPM:
-
.dist-info
.egg-info
The RPM build process automatically generates virtual
pythonX.Ydist
provides from these directories, for example:python3.9dist(pello)
The Python dependency generator then reads the upstream metadata and generates runtime requirements for each RPM package using the generated
pythonX.Ydist
virtual provides. For example, a generated requirements tag might look as follows:Requires: python3.9dist(requests)
-
- Inspect the generated requires.
To remove some of the generated requires, use one of the following approaches:
-
Modify the upstream-provided metadata in the
%prep
section of the SPEC file. - Use automatic filtering of dependencies described in the upstream documentation.
-
Modify the upstream-provided metadata in the
-
To disable the automatic dependency generator, include the
%{?python_disable_dependency_generator}
macro above the main package’s%description
declaration.
Additional resources