
Python falls short in a few areas. For instance, Python isn’t the fastest language around, but third-party libraries like NumPy let you work around that. Where Python is most deficient, though, is packaging. That is, Python lacks a consistent internal mechanism for generating a standalone binary from an application. Go and Rust do this. Why can’t Python?
It mostly comes down to Python not having a culture of such use cases until relatively recently in its history. And so, only relatively recently did third-party modules start showing up that allow Python apps to be packaged as standalone binaries. PyInstaller — which I covered previously — is one such app. In this article we’ll look at an even more elegant and powerful utility for Python app packaging, BeeWare’s Briefcase.
[ Also on InfoWorld: Python virtualenv and venv do’s and don’ts ]
However, there are two caveats worth pointing out about Briefcase. First, Briefcase doesn’t do cross-platform packaging; you need to build on the platform you’re deploying for. Second, Briefcase works best with apps that make use of a GUI toolkit of some kind. We’ll go into detail about these issues below.
What is BeeWare Briefcase?
Briefcase is part of a general suite of tools by BeeWare for creating apps, with the different pieces complementing each other. For instance, BeeWare’s Kivy lets you create cross-platform GUI apps in Python that run not only on all the major OS platforms but also on the web. But here we’ll focus on Briefcase, which can be used with or without the other tools.
Briefcase packages apps for all the OSes it supports by way of a common format for apps on that platform:
- Microsoft Windows (MSI installer)
- macOS (
.app
format file) - Linux (AppImage)
- iOS (Xcode project)
- Android (Gradle project)
To deploy on iOS or Android, you’ll need the development kits for those platforms.
One thing Briefcase does not support is cross-platform deployment. For instance, if you’re a Windows user, you can’t build a macOS app; you’ll need macOS to do that. Other app bundlers for Python are similarly limited, so this restriction is by no means exclusive to Briefcase.
Briefcase is also not a “compiler” — it doesn’t transform Python programs into their native machine-code equivalents. Your apps won’t run any faster when deployed as Briefcase apps than they do normally.
Briefcase project setup
Briefcase requires you to set up a dedicated project directory with its own virtual environment. If you’re not familiar with “venvs” yet, as Python virtual environments are called, it’s worth getting up to speed on them, as state-of-the-art Python development revolves heavily around them.
After you set up a venv and pip install briefcase
into it, you’ll use Briefcase’s own command-line tooling to set up, manage, and deliver Briefcase-packaged projects. This is akin to the way tools like Poetry work: Most of your high-level interactions with the project are through the tool, so you don’t have to manually create files or edit configurations.
To kick off a new Briefcase project, open the CLI in your project directory, activate the virtual environment (assuming you’re not using an IDE’s CLI to do that automatically), and type briefcase new
. This creates scaffolding in your project directory for a Briefcase project.
You’ll need to answer some questions about the project at first, and for most of them you can just press Enter
to accept the default. But one of the questions you’ll be asked — the last one, in fact — matters greatly: the choice of GUI framework to use.
One of BeeWare’s other offerings is a UI toolkit called Toga, for creating GUIs in Python programs using platform-native UI components. If you want to jump into learning Toga while also working with Briefcase, there’s nothing stopping you. Or you could select “None” and create a “headless” app that runs from the command line, or you could use a third-party UI toolkit or windowing system such as Pyglet or PyQT.
Note that if you install no UI toolkit, the app will have no console interactivity whatsoever — i.e., it won’t open a console window and it won’t print anything to the console. This is useful if you’re deploying a program that doesn’t require console interaction — for instance, if it runs as a local web server and uses a web browser for interaction. But there is as of yet no option to allow Briefcase programs with no UI package installed to run with a console.
Briefcase project structure
A freshly initiated Briefcase app directory comes with several files pre-installed:
- The top level of the app directory contains the project’s license,
pyproject.toml
file, a sample README file in ReStructured Text format, and a.gitignore
file that comes pre-customized with common directories to omit from any Git repository created for the project. - The
src
directory contains the source code of your app, with two subdirectories: one that contains the app (it has the same name as your project directory) and one that contains the app’s metadata. - The app directory contains a
resources
directory, which is used to store resources like application icons.
Briefcase project commands
The briefcase
command is how you perform most of your interactions with a Briefcase project. We covered the new
command above, which is used to set up a Briefcase project in a given folder. But you’ll typically need to use many other commands during the lifecycle of a Briefcase app, and some of them can be a little counterintuitive.
Here are the most common Briefcase commands you’ll use:
dev
: When you’re inside an app directory, this command runs that app in dev mode. Dev mode lets you run the application with its full complement of installed libraries, but without needing to be formally packaged for delivery. Most of the time, when developing your application, you’ll test-run it with dev mode. If any dependencies have changed since the last time you randev
, use the-d
flag to update them.build
: Builds a copy of the application in the form needed to package it for distribution. This differs fromdev
in that you can build for different platforms if the scaffolding is installed.update
: Updates an application build. This is the quick way to make sure the build of your application has the most recent code, rather than usingbuild
, which regenerates many more files. Pass the-d
flag to update dependencies, and the-r
flag to update resources (that is, to copy resources from the dev version of your app to the build version).run
: Runs the built version of the app. This essentially simulates running the packaged and deployed version of the application. Pass the-u
flag to update any code before running.package
: Creates an application installer package from the built version of the app. The end result of this is an artifact you can give to others to install your program — e.g., an .MSI on Windows.
Here are some of the less commonly used Briefcase commands:
create
: Not to be confused withnew
,create
creates the scaffolding for an application installer — a way to build the app’s installer for a particular platform. When you set up an app withnew
, it comes with scaffolding for the platform you’re working on;create
lets you add scaffolding for another platform if needed.upgrade
: Upgrades the components used to package the app, such as the Wix framework.publish
: Publishes the packaged app to a publication channel such as an app store. (As of this writing, this feature doesn’t work yet.)
To sum up, this is the order in which you would use the Briefcase commands in the typical app lifecycle:
new
to create the appdev
to run the app as you work on itbuild
to create a version of the app to be packaged for distributionrun
to test-run the packaged version of the appupdate
to keep the packaged version of the app up-to-date with code changespackage
to deploy the packaged version of the app with an installer
Briefcase app creation
Creating a Python program as a Briefcase app is much the same as creating any other Python app. The main issues involve the project structure. The app’s entry point is __main__.py
in the app directory, which loads app.py
from the same directory and executes main()
. When you initialize a project, it will be populated with placeholder versions of some project files, which you can build out or replace as needed.
If you’re transforming an existing project to use Briefcase, make sure you structure it in such a way that its entry point is what Briefcase expects. For instance, if you didn’t store the code in a src
directory, you’ll need to move your code into src
and fix any incompatibilities in its paths and directory structures.
The other thing to keep in mind is how to handle third-party dependencies. The pyproject.toml
file in your project directory controls which dependencies to add to the project. If your project is named myproject
, then pyproject.toml
will contain a section named [tool.briefcase.app.myproject]
, with a requires
line that lists each requirement as they’d be specified in a requirements.txt
file. If your project needs, for instance, regex
and black
, you would set that line to requires = ["regex","black"]
. You’d then use briefcase dev -d
to update the dependencies for the development version of the project, and briefcase update -d
to update dependencies in the packaged version.
Briefcase app packaging and delivery
Once you run briefcase package
, you will see a redistributable for your program appear in a subdirectory of the project directory that corresponds to the platform you built for. For Microsoft Windows, for instance, the directory will be windows
, and the redistributable will be an .msi
file with the same name as your project. For Android and iOS, the results will be projects for Gradle and Xcode, respectively, and these will need to be compiled using those tools to be deployable to those platforms.