Packaging Guide#

Normally, the recommended way to install Falcon into your project is using pip (or a compatible package/project manager, such as poetry, uv, and many others) that fetches package archives from PyPI.

However, the PyPI-based way of installation is not always applicable or optimal. For instance, when the system package manager of a Linux distribution is used to install Python-based software, it normally gets the dependencies from the same distribution channel, as the specific versions of the packages were carefully tested to work well together on the operating system in question.

This guide is primarily aimed at engineers who create and maintain Falcon packages for operating systems such as Linux and BSD distributions, as well as alternative Python distribution channels such as conda-forge.

Note

Unless noted otherwise in specific sections, this document is only applicable to Falcon 4.0.1 or newer.

If you run into any packaging issues, questions that this guide does not cover, or just find a bug in Falcon, please let us know!

Obtaining Release#

In order to package a specific Falcon release, you first need to obtain its source archive.

It is up to you which authoritative source to use. The most common alternatives are:

  • Source distribution (aka “sdist”) on PyPI.

    If you are unsure, the recommended way is to use our source distribution from PyPI (also available on GitHub releases, see below).

    You can query PyPA Warehouse’s JSON API in order to obtain the latest stable version of Falcon, fetch the sdist URL, and more.

    The API URL specifically for Falcon is https://pypi.org/pypi/falcon/json. Here is how you can query it using the popular requests:

    >>> import requests
    >>> resp = requests.get('https://pypi.org/pypi/falcon/json')
    >>> for url in resp.json()['urls']:
    ...     if url['packagetype'] == 'sdist':
    ...         print(f'Latest Falcon sdist: {url["url"]}')
    ...
    Latest Falcon sdist: https://files.pythonhosted.org/<...>/falcon-4.0.2.tar.gz
    

    (4.0.2 was the latest version at the time of this writing.)

  • GitHub release archive.

    Alternatively, you can download the archive from our Releases on GitHub. GitHub automatically archives the whole repository for every release, and attaches the tarball to the release page. In addition, our release automation also uploads the sdist (see above) to the release as well.

  • Clone GitHub repository.

    If your packaging workflow is based on a Git repository that tracks both the framework’s source code, and your patches or tooling scripts, you will probably want to clone our GitHGub repository instead.

    Every release has a corresponding annotated Git tag that shares the name with the package version on PyPI, e.g., 4.0.2.

Semantic Versioning#

Falcon strictly adheres to SemVer – incompatible API changes are only introduced in conjunction with a major version increment.

When updating your Falcon package, you should always carefully review the changelog for the new release that you targeting, especially if you are moving up to a new SemVer major version. (In that case, the release notes will include a “Breaking Changes” section.)

For a packager, another section worth checking is called “Changes to Supported Platforms”, where we announce support for new Python interpreter versions (or even new implementations), as well as deprecate or remove the old ones. While there seems to be no clear consensus on whether removing platform support constitutes a SemVer breaking change, Falcon assumes that it does (unless we have communicated otherwise in advance, e.g., the Falcon 4.x series only guarantees Python 3.10+ support).

Note

The SemVer guarantees primarily cover the publicly documented API from the framework user’s perspective, so even a minor release may contain important changes to the build process, tests, and project tooling.

Metadata and Dependencies#

It is recommend to synchronize the metadata such as the project’s description with recent releases on PyPI.

Falcon has no hard runtime dependencies except the standard Python library. So depending on how Python is packaged in your distribution (i.e., whether parts of the stdlib are potentially broken out to separate packages), Falcon should only depend on the basic installation of the targeted Python interpreter.

Note

Falcon has no third-party dependencies since 2.0, however, we were vendoring the python-mimeparse library (which also had a different licence, MIT versus Falcon’s Apache 2.0).

This is no longer a concern as the relevant functionality has been reimplemented from scratch in Falcon 4.0.0, also fixing some long standing behavioral quirks and bugs on the way. As a result, the Falcon 4.x series currently has no vendored dependencies.

Optional dependencies#

Falcon has no official list of optional dependencies, but if you want to provide “suggested packages” or similar, various media (de-) serialization libraries can make good candidates, especially those that have official media handlers such as msgpack (MessagePackHandler). JSONHandler can be easily customized using faster JSON implementations such as orjson, rapidjson, etc, so you can suggest those that are already packaged for your distribution.

Otherwise, various ASGI and WSGI application servers could also fit the bill.

See also Optional test dependencies for the list of third party libraries that we test against in our Continuous Integration (CI) tests.

Building Binaries#

The absolute minimum in terms of packaging is not building any binaries, but just distributing the Python modules found under falcon/. This is roughly equivalent to our pure-Python wheel on PyPI.

Tip

The easiest way to skip the binaries is to set the FALCON_DISABLE_CYTHON environment variable to a non-empty value in the build environment.

The framework would still function just fine, however, the overall performance would be somewhat (~30-40%) lower, and potentially much lower (an order of magnitude) for certain “hot” code paths that feature a dedicated implementation in Cython.

Note

The above notes on performance only apply to CPython.

In the unlikely case you are packaging Falcon for PyPy, we recommend simply sticking to pure-Python code.

In order to build a binary package, you will obviously need a compiler toolchain, and the CPython library headers. Hopefully your distribution already has Python tooling that speaks PEP 517 – this is how the framework’s build process is implemented (using the popular setuptools).

We also use cibuildwheel to build our packages that are later uploaded to PyPI, but we realize that its isolated, Docker-centric approach probably does not lend itself very well to packaging for a distribution of an operating system.

If your build process does not support installation of build dependencies in a PEP 517 compatible way, you will also have to install Cython yourself (version 3.0.8 or newer is recommended to build Falcon).

Big-endian support#

We regularly build and test binary wheels on the IBM Z platform (aka s390x) which is big-endian. We are not aware of any endianness-related issues.

32-bit support#

Falcon is not very well tested on 32-bit systems, and we do not provide any 32-bit binary wheels either. We even explicitly fall back to pure-Python code in some cases such as the multipart form parser (as the smaller Py_ssize_t would interfere with uploading of files larger than 2 GiB) if we detect a 32-bit flavor of CPython.

If you do opt to provide 32-bit Falcon binaries, make sure that you run extensive tests against the built package.

Building Documentation#

It is quite uncommon to also include offline documentation (or to provide a separate documentation package) as the user can simply browse our documentation at Read the Docs. Even if the package does not contain the latest version of Falcon, it is possible to switch to an older one using Read the Docs version picker.

If you do decide to ship the offline docs too, you can build it using docs/Makefile (you can also invoke sphinx-build directly).

Note

Building the HTML documentation requires the packages listed in requirements/docs.

Building man pages requires only Sphinx itself and the plugins referenced directly in docs/conf.py (currently myst-parser, sphinx-copybutton, and sphinx-design).

  • To build HTML docs, use make html.

    The resulting files will be built in docs/_build/html/.

  • To build man pages, use make man.

    The resulting man page file will be called docs/_build/man/falcon.1.

    You will need to rename this file to match your package naming standards, and copy it an appropriate man page directory (typically under /usr/share/man/ or similar).

Testing Package#

When your Falcon package is ready, it is a common (highly recommended!) practice to install it into your distribution, and run tests verifying that the package functions as intended.

As of Falcon 4.0+, the only hard test dependency is pytest.

You can simply run it against Falcon’s test suite found in the tests/ subdirectory:

pytest tests/

These tests will provide decent (98-99%), although not complete, code coverage, and should ensure that the basic wiring of your package is correct (however, see also the next chapter: Optional test dependencies).

Tip

You can run pytest from any directory, i.e., the below should work just fine:

/usr/local/foo-bin/pytest /bar/baz/falcon-release-dir/tests/

This pattern is regularly exercised in our CI gates, as cibuildwheel (see above) does not run tests from the project’s directory either.

Optional test dependencies#

As mentioned above, Falcon has no hard test dependencies except pytest, however, our test suite includes optional integration tests against a selection of third-party libraries.

When building wheels with cibuildwheel, we install a small subset of the basic optional test dependencies, see the requirements/cibwtest file in the repository. Furthermore, when running our full test suite in the CI, we exercise integration with a larger number of optional libraries and applications servers (see the requirements/tests file, as well as various ASGI/WSGI server integration test definitions in tox.ini).

Ideally, if your distribution also provides packages for any of the above optional test dependencies, it may be a good idea to install them into your test environment as well. This will help verifying that your Falcon package is compatible with the specific versions of these packages in your distribution.

Thank You#

If you are already maintaining Falcon packages, thank you!

Although we do not have the bandwidth to maintain Falcon packages for any distribution channel beyond PyPI ourselves, we are happy to help if you run into any problems. File an issue on GitHub, or just send us a message!