avatarDr. Mandar Karhade, MD. PhD.

Summary

PEP 517 is a Python Enhancement Proposal that standardizes the build process for Python packages, facilitating cross-platform compatibility and allowing developers to use various build systems.

Abstract

PEP 517 introduces a standard build frontend for Python packages, ensuring a consistent and reliable build process across different systems. It is particularly beneficial for developers creating complex packages that require cross-platform compatibility and advanced build features, such as compiling native extensions or building multiple package versions. While PEP 517 is recommended for its standardization benefits, it may be unnecessary for simple packages with straightforward build processes. The proposal also accommodates the use of alternative build tools besides setuptools, such as CMake or Meson, by defining a standardized build backend. Examples provided in the context illustrate scenarios for using PEP 517 and traditional build methods, highlighting the flexibility and applicability of PEP 517 in various packaging situations.

Opinions

  • The author suggests that PEP 517 is essential for developers who need to create complex, cross-platform Python packages, as it simplifies the build process and ensures compatibility.
  • PEP 517 is not always necessary; for simple packages with basic build requirements, the traditional setuptools and setup.py may suffice.
  • The author encourages adopting PEP 517 for standardization but cautions against

Should I build a package using PEP 517 — And How

PEP 517 is a standard build pattern for Python packages which are essential for packages cross-system builds

PEP 517 is a Python Enhancement Proposal (PEP) that defines a standard build frontend for Python packages. The proposal specifies build tool requirements that allow package builders to use any build system they prefer while ensuring a consistent, reliable build process. In this article, we’ll take a closer look at PEP 517, what it is, when to use it, and when not to use it, along with some examples.

Source: author

What is PEP 517?

PEP 517 is designed to make it easy for developers to create, distribute, and install Python packages. Consider it as a standard set of rules to develop a package so that the compatibility of the package becomes less of an issue. The proposal specifies that build tools should provide a standardized build backend that can be used by packaging tools like pip or setuptools. The goal of PEP 517 is to make it easier for developers to create high-quality, reliable Python packages that can be easily installed on any system.

When to use PEP 517?

PEP 517 is most useful for developers who need to create complex, cross-platform Python packages. If you are short on time, then there is your answer. If you’re building a simple package with a straightforward build process, you may not need to use PEP 517. However, if your package requires more advanced build features like compiling native extensions or building multiple versions of your package for different platforms, PEP 517 can make your life much easier. Would I recommend practicing PEP 517, yes! always lean towards the standard but don’t let it be a hurdle in your progress. Keep moving.

PEP 517 is also useful if you want to use a build tool other than setuptools, which is the default build tool for Python packages. If you prefer to use a different build system like CMake or Meson, you can use PEP 517 to define a standardized build backend that works with your preferred build tool.

When not to use PEP 517?

Honestly, there is never a situation not to use PEP 517, but lets reframe it as PEP 517 may not be necessary for simple Python packages with a straightforward build process.

  1. If your package doesn’t require any advanced build features
  2. you’re happy using setuptools as your build tool, you may not need to use PEP 517.
  3. if you’re building a package that is only intended for a specific platform or environment.

you may not need to use PEP 517. For example, if you’re building a package for a specific Linux distribution, you may be able to use that distribution’s packaging tools to create the package, rather than using PEP 517.

Examples

Let’s take a look at some examples of when to use PEP 517 and when not to use it.

Example 1: Simple Package

Suppose you’re building a simple Python package that doesn’t require any advanced build features. In this case, you probably don’t need to use PEP 517. You can simply use setuptools as your build tool and create a setup.py file to define your package. Here’s an example:

# Content of setup.py
from setuptools import setup, find_packages

setup(
    name='my_simple_package',
    version='0.1',
    packages=find_packages(),
    install_requires=[
        'requests',
    ],
)

This setup.py file defines a simple Python package called my_simple_package that depends on the requests library. To build the package, you can run the following command:

python setup.py sdist bdist_wheel

This will create a source distribution and a binary wheel distribution of your package that can be easily installed using pip.

Here is how the output will look like —

me@mymachine:~/workspace/packaging_example$ . /home/m/.cache/pypoetry/virtualenvs/packaging-Aqqc-AtY-py3.10/bin/activate
(packaging-py3.10) m@mymachine:~/workspace/packaging_example$ python setup.py sdist bdist_wheel
running sdist
running egg_info
creating my_simple_package.egg-info
writing my_simple_package.egg-info/PKG-INFO
writing dependency_links to my_simple_package.egg-info/dependency_links.txt
writing requirements to my_simple_package.egg-info/requires.txt
writing top-level names to my_simple_package.egg-info/top_level.txt
writing manifest file 'my_simple_package.egg-info/SOURCES.txt'
reading manifest file 'my_simple_package.egg-info/SOURCES.txt'
writing manifest file 'my_simple_package.egg-info/SOURCES.txt'
warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.md

running check
creating my_simple_package-0.1
creating my_simple_package-0.1/my_simple_package.egg-info
copying files to my_simple_package-0.1...
copying pyproject.toml -> my_simple_package-0.1
copying setup.py -> my_simple_package-0.1
copying my_simple_package.egg-info/PKG-INFO -> my_simple_package-0.1/my_simple_package.egg-info
copying my_simple_package.egg-info/SOURCES.txt -> my_simple_package-0.1/my_simple_package.egg-info
copying my_simple_package.egg-info/dependency_links.txt -> my_simple_package-0.1/my_simple_package.egg-info
copying my_simple_package.egg-info/requires.txt -> my_simple_package-0.1/my_simple_package.egg-info
copying my_simple_package.egg-info/top_level.txt -> my_simple_package-0.1/my_simple_package.egg-info
Writing my_simple_package-0.1/setup.cfg
creating dist
Creating tar archive
removing 'my_simple_package-0.1' (and everything under it)
running bdist_wheel
running build
/home/me/.cache/pypoetry/virtualenvs/packaging-Aqqc-AtY-py3.10/lib/python3.10/site-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
  warnings.warn(
installing to build/bdist.linux-x86_64/wheel
running install
running install_egg_info
Copying my_simple_package.egg-info to build/bdist.linux-x86_64/wheel/my_simple_package-0.1-py3.10.egg-info
running install_scripts
creating build/bdist.linux-x86_64/wheel/my_simple_package-0.1.dist-info/WHEEL
creating 'dist/my_simple_package-0.1-py3-none-any.whl' and adding 'build/bdist.linux-x86_64/wheel' to it
adding 'my_simple_package-0.1.dist-info/METADATA'
adding 'my_simple_package-0.1.dist-info/WHEEL'
adding 'my_simple_package-0.1.dist-info/top_level.txt'
adding 'my_simple_package-0.1.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel

Example 2: Building Cross-Platform Package (PEP 517)

Suppose you’re building a more complex Python package that requires compiling native extensions and building multiple versions of your package for different platforms. In this case, you may want to use PEP 517 to define a standardized build backend that works with your preferred build system.

For example, suppose you’re building a package that -

  1. includes a C extension
  2. and you prefer to use the Meson build system.

In this case, you can define a setup.cfg file that specifies the Meson build backend using PEP 517. Here’s an example:

[metadata]
name = my_package
version = 0.1.0
author = My Name
author_email = [email protected]
description = A simple Python package that prints "Hello, world!"
long_description = file: README.md
long_description_content_type = text/markdown
license = MIT
classifiers =
    Programming Language :: Python :: 3
    License :: OSI Approved :: MIT License
    Operating System :: OS Independent

[options]
packages = find:
install_requires =
    # None

[options.packages.find]
where = my_package

[build-system]
requires = ["setuptools>=38.6.0", "wheel", "meson>=0.57.0", "ninja>=1.10.0.post1"]
build-backend = "setuptools.build_meta"

[tool.meson]
# Specify build options here

This setup.cfg file specifies the build requirements for the package, including setuptools, wheel, Meson, and Ninja. It also specifies that setuptools should be used as the build backend. You can then define your build options in the [tool.meson] section of the file.

You will also need a few other files to make sure that the process runs smooth. The directory will look something like this. The package2 needs to remain same for 2 levels of the directory. I will be using package2 as my package name

package2/
    package2/
        __init__.py
        my_module.py
    README.md
    setup.cfg
    pyproject.toml

Here’s a brief overview of each of the files in this structure:

  • package2/package2/__init__.py: This is an empty file that marks the package2 directory as a Python package.
  • package2/package2/my_module.py: This is a simple Python module that defines a function my_function.
  • README.md: This is a file that provides some basic documentation for the package.
  • setup.cfg: This is a configuration file that provides metadata about the package, such as its name, version, author, and license.
  • pyproject.toml: This is a configuration file that specifies how the package should be built.

my_module.py

def my_function():
    return "Hello, world!"

README.md

# My Package

This is a simple Python package that provides a function to print "Hello, world!".

pyproject.toml

A note about pyproject.toml. If you are already using poetry then you don't need to worry about editing the existing pyproject.toml.

[build-system]
requires = [
    "setuptools >= 40.8.0",
    "wheel"
]
build-backend = "setuptools.build_meta"

Another note, if you are using Ubuntu, make sure to install virtualenv.

Now you are ready to build your package using PEP 517. To build your package using this setup.cfg file, you can use the following command:

pip install build
python -m build

Which should give output like

(packaging2-py3.10) me@mymachine:~/workspace/packaging2$ python -m build
* Creating virtualenv isolated environment...
* Installing packages in isolated environment... (poetry-core)
* Getting build dependencies for sdist...
* Building sdist...
* Building wheel from sdist
* Creating virtualenv isolated environment...
* Installing packages in isolated environment... (poetry-core)
* Getting build dependencies for wheel...
* Building wheel...

An alternate and easier way to do the same will be through poetry if you are using it already,

Run the command poetry build as shown below.

(packaging2-py3.10) me@mymachine:~/workspace/packaging2$ poetry build
Building packaging2 (0.1.0)
  - Building sdist
  - Built packaging2-0.1.0.tar.gz
  - Building wheel
  - Built packaging2-0.1.0-py3-none-any.whl

Example 3: Distribution Package

Suppose you’re building a package that is intended to be distributed as part of a Linux distribution. In this case, you may not need to use PEP 517. Instead, you can use the packaging tools provided by the distribution to create the package.

For example, suppose you’re building a package for Ubuntu. You can create a Debian package using the debuild tool provided by the Ubuntu packaging system. Here’s an example:

You can install these packages using the following command:

sudo apt-get install build-essential devscripts dh-python python-all-dev python-stdeb

Next, navigate to the root directory of your Python package. The setup.py is the same file that we saw earlier in this article. Now run the following command to create a Debian source package:

python setup.py --command-packages=stdeb.command sdist_dsc

This will create a source distribution of your package and a corresponding Debian source package in the deb_dist directory.

Finally, you can use debuild to build the Debian binary package from the source package. Run the following command:

cd deb_dist/<package-name>-<version>
debuild -us -uc -i -b

This will compile and package your Python package into a Debian binary package. Once the package has been built, you can install it using the dpkg tool:

sudo dpkg -i <package-name>_<version>_all.deb

This will use the Ubuntu packaging tools to build a Debian package of your Python package. During this whole process, we never actually used PEP 517 because it was simply not necessary.

Conclusion

PEP 517 provides a standard build frontend for Python packages, allowing developers to use any build system they prefer while ensuring a consistent, reliable build process. While PEP 517 is most useful for complex, cross-platform packages, it may not be necessary for simple packages or packages that are only intended for a specific platform or environment. By understanding when to use PEP 517 and when not to, you can ensure that your Python packages are built reliably and efficiently.

For my other life-saving content, please check out

Don’t forget to follow, like, and share the article. Thank you!

🔔 clap | follow | Subscribe 🔔

Become a member using my link: https://ithinkbot.com/membership

Python
Programming
Data Science
Learning To Code
Recommended from ReadMedium