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 follows the specifications published by the Python Packaging Authority (PyPA). Historically, most Python projects used the distutils or setuptools utilities for packaging and defined package information in the setup.py file. However, options for creating native Python packages have evolved over time:
To package Python software, whether the project uses setup.py or pyproject.toml, use pyproject RPM macros from the pyproject-rpm-macros package. See Packaging Python projects with pyproject RPM macros.
Packaging a Python project into an RPM provides the following advantages compared to native Python packages:
-
Dependencies on Python and non-Python packages are possible and strictly enforced by the
DNFpackage 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. A spec file description for a Python package Copy linkLink copied to clipboard!
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 program written in Python
%global python3_pkgversion 3.12
Name: python-pello
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
# 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
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
%install
# The macro only supported projects with setup.py
%py3_install
%check
%{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/
%changelog
* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 1.0.2-1
- First pello package
- 1
- By defining the
python3_pkgversionmacro, 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 value3or 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 ispelloand, therefore, the name of the Source RPM (SRPM) ispython-pello. - 3
- The
BuildRequiresdirective specifies what packages are required to build and test this package. InBuildRequires, always include items providing tools necessary for building Python packages:python3-devel(orpython3.11-develorpython3.12-devel) and the relevant packages needed by the specific software that you package, for example,python3-setuptools(orpython3.11-setuptoolsorpython3.12-setuptools) or the runtime and testing dependencies needed to run the tests in the%checksection. - 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 to3for the default Python version 3.9 unless you set it to an explicit version, for example,3.11(see footnote 1). - 5
- The
%py3_buildand%py3_installmacros run thesetup.py buildandsetup.py installcommands, respectively, with additional arguments to specify installation locations, the interpreter to use, and other details.
Using the setup.py build and setup.py install commands from the setuptools package is deprecated. For modern projects, use the pyproject RPM macros from the pyproject-rpm-macros package. For more information, see Packaging Python projects with pyproject RPM macros.
- The
%checksection should run the tests of the packaged project. The exact command depends on the project itself, but it is possible to use the%pytestmacro to run thepytestcommand in an RPM-friendly way.
3.2. Common macros for Python 3 RPMs Copy linkLink copied to clipboard!
In a Python RPM 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 A 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 |
3.3. Using automatically generated dependencies for Python RPMs Copy linkLink copied to clipboard!
The following procedure describes how to use automatically generated dependencies when packaging a Python project as an RPM.
Prerequisites
-
A
specfile for the RPM exists. For more information, see Aspecfile 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-infoThe RPM build process automatically generates virtual
pythonX.Ydistprovides 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.Ydistvirtual 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
%prepsection 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%descriptiondeclaration.
3.4. Packaging Python projects with pyproject RPM macros Copy linkLink copied to clipboard!
Package Python projects that use a pyproject.toml or a setup.py file into RPM packages by using pyproject RPM macros from the CodeReady Linux Builder (CRB) repository.
3.4.1. pyproject RPM macros for Python packaging Copy linkLink copied to clipboard!
Package Python projects into RPM packages by using pyproject RPM macros. These macros work with projects that define metadata in setup.py, pyproject.toml, or both, and invoke a standards-based build backend through a standardized protocol.
Many upstream Python projects no longer rely on a setup.py script. Instead, they store project metadata in pyproject.toml and use a build backend, such as setuptools, flit-core, hatchling, or poetry-core. The pyproject RPM macros invoke any compliant build backend through a standardized protocol instead of calling setup.py directly.
The pyproject RPM macros reuse upstream metadata whenever possible. For example, they can generate BuildRequires from the build-system requirements in pyproject.toml instead of listing the same dependencies manually in the spec file.
The pyproject-rpm-macros package is available in the CodeReady Linux Builder (CRB) repository. The %pyproject_buildrequires macro adds a generated BuildRequires on that package during package builds. CRB content is unsupported by Red Hat. The macros are updated as needed to follow emerging Python packaging standards.
Use pyproject RPM macros to package Python projects, whether the upstream project defines metadata in setup.py, pyproject.toml, or both.
3.4.2. Preparing to use pyproject RPM macros Copy linkLink copied to clipboard!
Package Python projects by using pyproject RPM macros in an RPM spec file. The %pyproject_buildrequires macro adds a generated BuildRequires on the pyproject-rpm-macros package, so you do not install that package manually on the build host. Enable the CodeReady Linux Builder (CRB) repository on the build host so the dependency is available during package builds.
Red Hat does not support content in the CodeReady Linux Builder repository.
Prerequisites
- You have root permissions on the build host.
- The system is registered with Red Hat Subscription Manager.
Procedure
Enable the CodeReady Linux Builder repository on the build host:
# subscription-manager repos --enable codeready-builder-for-rhel-9-x86_64-rpmsIn the RPM
specfile, addBuildRequires: python3-develand use the%pyproject_buildrequiresmacro in the%generate_buildrequiressection:BuildRequires: python3-devel %generate_buildrequires %pyproject_buildrequires
3.4.3. Common pyproject RPM macros for Python 3 RPMs Copy linkLink copied to clipboard!
In a Python RPM spec file, use pyproject RPM macros to build projects that define metadata in setup.py, pyproject.toml, or both.
You can redefine which Python 3 version the macros use by defining the python3_pkgversion macro at the top of your spec file, for example %global python3_pkgversion 3.12. Omit this macro to build for the default Python 3 version.
Place %pyproject_buildrequires in the %generate_buildrequires section of the spec file. RPM runs the macro in that section to generate build requirements from upstream metadata before the build starts.
| Macro | Description |
|---|---|
| %pyproject_buildrequires |
Expands build requirements from upstream metadata. Use options such as |
| %pyproject_wheel | Builds a wheel package by using the upstream-specified build backend. |
| %pyproject_install | Installs the built wheel package into the build root. |
| %pyproject_save_files |
Saves the list of installed files for use in the |
| %pyproject_check_import |
Verifies that the installed package can be imported during the |
| %pyproject_files |
Expands to the file list saved by |
The table lists commonly used macros. For all macros and their options, see the /usr/share/doc/pyproject-rpm-macros/README.md file provided by the pyproject-rpm-macros package.
3.4.4. A spec file description for a Python package with pyproject RPM macros Copy linkLink copied to clipboard!
Use an annotated RPM spec file example to package a Python project that defines metadata in setup.py, pyproject.toml, or both. The example shows pyproject RPM macros for build requirements, wheel builds, installation, and automated file lists.
Example 3.1. An example spec file for a Python library built with pyproject RPM macros
Name: python-pello
Version: 1.0.2
Release: 1%{?dist}
Summary: Example Python library
License: MIT
URL: https://example.com/Pello
Source: %{url}/archive/v%{version}/Pello-%{version}.tar.gz
BuildArch: noarch
%description %_description
Pello is an example package with an executable that prints Hello World! on the command line.
%package -n python3-pello
Summary: %{summary}
%description -n python3-pello
Pello is an example package with an executable that prints Hello World! on the command line.
%prep
%autosetup -p1 -n Pello-%{version}
%generate_buildrequires
%pyproject_buildrequires
%build
%pyproject_wheel
%install
%pyproject_install
%pyproject_save_files -l pello
%check
%pyproject_check_import
%pytest
%files -n python3-pello -f %{pyproject_files}
-
The
%pyproject_buildrequiresmacro in the%generate_buildrequiressection creates build requirements from upstream metadata. -
The
%pyproject_wheelmacro builds a wheel package by using the upstream-specified build backend. -
The
%pyproject_installmacro installs the wheel into the build root. -
The
%pyproject_save_files -l pellomacro records installed files. The-loption verifies that a license file is present. Thepelloargument lists the importable module namedpello. -
The
%checksection runs%pyproject_check_importto verify importability and%pytestto run tests. -
The
%filessection uses-f %{pyproject_files}to include the file list generated by%pyproject_save_files.
For commonly used macros, see Common pyproject RPM macros for Python 3 RPMs. For all macros and their options, see the /usr/share/doc/pyproject-rpm-macros/README.md file provided by the pyproject-rpm-macros package.