A guide to get you started writing python applications for all Linux distributions with flatpak.
I just got back from PyCon 2018 in Cleveland. It was a whirlwind tour of Python and I met some great people along the way. One of the projects that really peaked my interest was BeeWare which is a suite of tools that I plan on going over in a different blog post. The long-and-short of it is that BeeWare is a set of tools that helps you build native applications with Python. Sweet!
As a first timer at PyCon, I decided to try the PyCon Sprints. The BeeWare team was very welcoming to people of all skill levels so I was very interested in trying to contribute something the single day I was there. I chose to work on the Briefcase which is the tool that actually packages the applications you write in Toga. The linux support needed to be bumped up, so I picked up the linux support issue on GitHub. This led me to start investigating an interesting tool called Flatpak. Flatpak helps developers publish on every Linux distribution. This sounded like a perfect product for Briefcase so I started trying to build my first mini python package with flatpak. It was not as easy as I hoped, so I’m going to document what I did here so anyone can follow along.
Installing Flatpak
The first step in this journey is to install Flatpak. The flatpak website has some great installation instructions for every Linux distribution. I’ll be going through this for my platform (Ubuntu) but you can follow the steps for yourself. It’s a very simple process
1. Install Flatpak itself:
1 |
|
2. Install the Software Flatpak plugin
This allows you to install apps without the command-line:
1
sudo apt install gnome-software-plugin-flatpak
3. Add the flathub repository
This is where all the flatpak artifacts and runtimes are stored, you will need it.
1
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
4. Restart your computer.
1 |
|
5. Install flatpak-builder
1 |
|
Hello World With Flatpak
The flatpak tutorial for building your first Flatpak does a great job laying out the basic commands, but I’ll cover them again here.
1. Install an SDK
The SDK will have everything we will need to build Python later. Flatpak also has a List of available runtimes if this one doesn’t work for you.
1 |
|
2. Create an app
This will be a simple hello world:
1 |
|
3. Add a manifest
1 |
|
The manifest is the key to understanding flatpak. So I’m going to give a brief overview of the key-value pairs laid out here before moving on. This is where all the magic happens.
app-id
- A unique application identifier, it takes the form of a three-part identifier.runtime
- This is provided by flatpak and is a base runtime on which there are many build dependencies, in theory you could build your own, but generally you shouldn’t.runtime-version
- The version of theorg.freedesktop.Platform
runtimesdk
- The name of the SDK to use on top of this runtimecommand
- This will determine the entrypoint to our applicationmodules
- This lists all the steps to add all of our dependencies to this runtime.name
- The name of the module, which is required for each module.buildsystem
- The way to build this particular module,"simple"
just means that we will run specific commands to build the applicationbuild-commands
- A list of commands to executesources
- A list of all the sources for this module, in our case, only a singlefile
calledhello.sh
(which we created earlier)
A complete list of all options for the manifest can be found in the flatkpak documentation
4. Build the application
1 |
|
5. Run the application
1 |
|
Once you’ve done that, you should see the message printed out from our shell script! Hooray you’ve created your first application!
What’s next?
Unfortunately, that’s where the flatpak installation instructions start to falter. This is obviously the most simple thing you can do, and works correctly, but now the real questions start. I wanted to run my python script. In order to do that, I need to install Python in my sandbox. The “sandbox” is flatpak’s isolated environment for applications. If you’re familiar with docker you can think of the sandbox as the container layer.
Installing Python can be achieved by modifying the org.flatpak.Hello.json
. We need to add
another module. We will call it cpython
:
1 |
|
If you build this application:
1 |
|
You’ll notice that you’re actually building Python from source. While super powerful, this was
very confusing for me at first. As it turns out, flatpak recognizes that the source you specified
is a compressed file (which you told it with archive
), but what’s more surprising was that
flatpak recognized that the extracted components actually contained a GNU Build System inside it
(i.e. a configure
and Makefile
) and so it took it upon itself to run the installation. For the
python application, we don’t actually need any special flags, but you can tweak the configure and
make flags with the config-opts
and make-args
build options.
Flatpak successfully installed python 3.6.5 in /app
. You can test this with the following:
1 |
|
This should drop you into a python3 shell. Go ahead and look at sys.path
!
Now that python is in our application environment, let’s write a script and execute it from that environment.
1 |
|
In theory you could make this file executable and point it to /app/bin/python
, but I don’t think
that is very portable. So, I usually create a runner script and use that as my entrypoint for
flatpak. Something like the following:
1 |
|
Now let’s update our manifest to let flatpak know about these new files:
1 |
|
Here you can see we’ve added our hello.py
file to /app/hello.py
and runner.sh
to /app/bin/runner.sh
. We also need to update our command
in the main manifest to runner.sh
.
Now if we re-build and run:
1 |
|
You should see Hello, World from python sandbox.
You might also notice that we’ve never rebuilt
python. That’s because flatpak is smart enough to cache our states as we go along, so our
build times are only slow the first time. Then we can iterate quickly!
Packaging Dependencies
While Python is “batteries included” there are still lots of useful packages. Getting packages into our flatpak sandbox can be done, but we’re going to need to introduce a couple of new options to our manifest.
There are several ways to get a package that you want into your sandbox. You could download the
package locally and then just references in your sources
section with the file
type. This
really isn’t very portable as other people will not be able to build your application without
downloading the same files.
Another way that we could do it would be to add the source as an archive the same way we did with Python itself. If we did this, we would have to make sure to know the sha256 hash, which is good from a security perspective, but can be painful to update, but at least people don’t have to manually download a file before building.
The way I would prefer is to actually just use pip
to install my package. To do this, we’ll
introduce a new module that we’ll call pip-install
.
1 |
|
Here we’ve introduced two new concepts in the manifest, the build-options
and build-args
options. Flatpak attempts to sandbox your application every step of the way. It explicitly makes
it harder to access resources on your host machine like your filesystem or the network. So if
we want to access the network during the build step, we have to tell it to allow use (hence
--share=network
).
Before we rebuild the application, we’ll want to update our hello.py
to use our new package
1 |
|
This code will not be able to execute successfully yet. If you try to build and run this program now, (go ahead I’ll wait). You’ll end up with an error like this:
1 |
|
This is because, just like our build steps, our application is running in a sandbox. It cannot
talk to the network unless we allow it. Our previous --share=network
only applied to the
pip-install
module. If we want our application to have access to the network, we need to set
a new option in the manifest (noticing a pattern?)
1 |
|
Here we’ve added the finish-args
option. In it, we specify that we want to share the host
network. This will now allow us to perform a network request google and see that wonderful HTML,
CSS and JavaScript of Google’s homepage.
1 |
|
Packaging Your Package
Now that you know how to install python, run a python script, and install dependencies, you’re ready to apply the flatpak build process to your favorite Python Package and deploy it via flatpak. This is the easiest way I’ve found to do that. Let’s say that I have a python package with a directory structure like the following:
1 |
|
To achive a directory structure like this, we’ll first have to create a setup.py
file:
1 |
|
Then move our old hello.py
to hello/__main__.py
1 |
|
A very simple hello package with dependencies listed in the setup.py
. We are now going to add
this to our sandbox and install all our dependencies. Time for a new module! Replace the old
pip-install
module with this new app-install
module.
1 |
|
Hopefully this all makes sense. We’ve introduced a new source type (which is currently undocumented)
called dir
. This allows us to copy the hello
directory in its entirety into the sandbox, then
we simply copy the setup.py into the same directory and point pip at it! With a quick modification
to our runner.sh
script, we’ll have a package installed and running:
1 |
|
We will build and run it one more time:
1 |
|
Conclusion
Hopefully this was helpful for you getting started in flatpak. It is a very interesting technology
and I’m looking forward to see where it goes from here. In the meantime, I plan on taking what I’ve
learned and applying it to the briefcase project, so you can just run python setup.py linux
on
your next Native application and get a nice linux distribution.
Everything that I did can be found in my GitHub Repository for flatpak
As always you can reach me on twitter @loganasherjones if you have questions. Otherwise post a comment on Disqus. Thanks so much for reading!