I wanted to get my UML tool for Python, Pynsource, into the Ubuntu/Snapcraft app store for a long time. This is the story of how I made that happen.
It certainly wasn’t easy. It took me a week of work, struggling with the concepts, documentation and various bugs in the tooling to finally get my app listed.
Hopefully I can save you some time getting your Python command line app or wxPython GUI app published.
- Part I — How to build a simple Python (non-GUI) snap
- Part II — Build a wxPython GUI app, understanding
- Part III — Debugging snaps
- Part IV — Troubleshooting and Snapcraft Bugs
The aim is to get our Python app listed in the Ubuntu app store. This app can be a CLI (command line interface) non-GUI app that you use from the terminal, or a full blown GUI app.
Either way, you get you own app store page with screenshots and an install button. For example:
The resulting app store page for Pynsource is here. Any such published app should also appear listed in the official Ubuntu Software program, which you launch from inside Linux. I could find my app by searching by its app name or by other relevant keywords, in this case I typed ‘UML’ and saw:
I didn’t have to get approval for my app to appear — I just created a Snapcraft developer account and pushed up my snap. Fantastic!
Users view a page with juicy screenshots and descriptions, and that all important install button :-) How do they get to that page? You can give them the url to your app’s page on the Snapcraft.io store. Or they can search for your app in the Snapcraft store, or in the “Ubuntu Software” app inside Linux.
Clicking on the Snapcraft web page install button will launch the “Ubuntu Software” app installer inside Linux.
You can also install app store apps using the command line. For example to install Pynsource you would type
snap install pynsource. To list all apps installed in your Linux system, type
You don’t usually need to get approval to push your app to the Ubuntu app store if it has the default ‘strict’ permissions, which make it safe. If your app uses the more unrestricted ‘classic’ permissions then it needs to be approved. The final permission mode is unrestricted ‘devmode’ which is for developer use — more information on the three snap confinement permissions here.
Getting your app featured in the curated lists and categories you see in the Ubuntu Software main page is a little unclear. Certainly you can set your app’s category on its Snapcraft.io page e.g. “Development”. Whilst this correctly affects searches on Snapcraft.io it does not affect how your app is listed in the categories of the “Ubuntu Software” app inside Linux 18.04 because the latter uses the “Developer” category —a bit of a mismatch— this post talks about this issue.
Snapcraft and the snap store are Ubuntu’s new app store technologies. Snaps work on other Linux distros, too, which is exciting. There are a couple of other technologies that do the same thing e.g. Flatpack and AppImage, but I won’t be covering those.
A snap is an app — its your program and all its dependencies rolled into a kind of zip file
You shouldn’t need to change your source code in any way to create a .snap file. A
.snap file is a compressed mini filesystem which contains your app files and all its dependencies.
The difference between the traditional Ubuntu app store app and a snap app is that snaps are more resilient and will work across releases and also across Linux distros — because they bundle all dependencies inside themselves. The price you pay is that they tend to be bigger in size than regular apps.
Snaps are distro independent because they are built and run against a common runtime, or “core”, and bundle the necessary libraries required to run everywhere. They do not run in a virtual machine. But you do need a virtual machine to build them (see later).
The ultimate goal for a developer is to build a
.snap which is the file that you push up to the app store (or send people), you type
snapcraft to build the snap — assuming you have created a
The overall process is:
- install the
- create a file called
snapcraft.yamland build your .snap file
- reserve a name on the app store and push up your .snap file
- edit your official snap page to add images and a description.
According to the official Canonical statistics page for snap usage,
there are over three million snap installs per month,
with over two thousand snap apps available — as of March 2019.
All the big name apps like Libre Office, Visual Studio Code, GitKraken, PyCharm, Sublime, Postman, Slack, Skype, VLC, Handbrake, Spotify, Plex, Gimp etc. have snaps. View the Canonical/Ubuntu/Linux/Snapcraft app store here.
snapd deamon process updates apps for you, or you can explicitly run
snap refresh to get the latest version of all your snaps.
This is a high level overview of building a snap.
snap command and its update demon
snapd should already live in recent versions of Ubuntu. If you are not using Ubuntu, see this installation documentation which covers other Linux distros and Mac. For example to list your existing snaps (Linux apps that have been installed from the app store) type
You typically build Linux snaps on a Linux machine. You can build snap apps on a Mac machine, though you can’t actually run the resulting snap apps on a Mac. A snap app is a .snap file which needs to be installed in Linux before it can be run.
- Install snapcraft e.g.
snap install snapcrafton Linux or
brew install snapcrafton Mac
- Create a
snapcraft.yamlfile ← this is the hard bit ;-)
- Run the command
snapcraftwhich creates a .snap file
- Install your .snap file locally
sudo snap install --devmode — dangerous *.snap
- Run and test your snap by invoking its command name e.g.
- If you are happy, reserve a name on the snap store
snapcraft register yourapp
- Push and publish your snap e.g.
snapcraft push --release=edge *.snap
- Edit your snap app’s web page with images and a descriptions. You can also provide a description inside the
- Let everybody know the url of your snap or alternatively, let them know the terminal command command to install your snap e.g.
snap install yourapp.
The idea is to get your Python files into a .snap file, as well as any supporting libraries. And to specify a command to launch your program.
snapcraft looks at the
snapcraft.yaml file and launches a VM (virtual machine) containing a clean, headless Ubuntu. The VM contains some core libraries (hence the
core18 directive you put into the
snapcraft.yaml file). Packages are pulled off the internet and if necessary, built inside the VM.
snapcraft.yaml file is like a Makefile, and consist of various sections. A
part is a section in the
snapcraft.yaml file that builds something. There used to be an ability to refer to remote parts which are preconfigured parts (lists of install commands and package lists) which others have developed, but I believe this is no longer supported when you use the now recommended base: image approach, which are are following in this article . Plugins are extensions to the
snapcraft.yaml file format that mean you can create or utilise parts that do specialised things. The only plugins I have used are the
python plugin, and the
dump plugin which helps you copy files into the snap. Developers can develop their own plugin (which happen to be written in Python), but this would be a rare thing to do.
There are several build related directories inside the VM like
prime etc. The prime directory represents the final contents of the snap, and is ultimately the most important. You need to ensure everything that will be deployed ends up in the prime directory. The prime directory even has subdirectories like
You can shell into a VM and check the contents of these various build directories, including
prime by dropping into a bash shell after a snapcraft build with the command
snapcraft --shell-after or at any time with the
multipass shell YOUR_VM_NAME command. If you don’t know the name of your VM, list them with
You can run Python inside the VM when you are shelled into it, but you won’t be able to run GUI apps, because its a headless Linux install. And running your CLI app inside the VM is not quite the same as running it as a properly installed snap app because your Python paths etc. are not the same. You can hack around with PYTHONPATH prior to launching
python, to mitigate this.
A snap file is built from the exact contents of the
prime directory inside the virtual machine of your snapcraft project. A .snap file is actually secretly a directory — like an
.app file is on Mac OS (which on Mac, you can explore using finder’s Show Package Contents).
A .snap file is a just a compressed hierarchy of files, a filesystem if you will.
Most commands in the
snapcraft.yaml implicitly refer to the
prime directory inside the VM, which, as I’ve mentioned, ends up as the root of your snap filesystem inside the resulting .snap file. You can explicitly refer to the prime directory in the VM as
$SNAP — this is especially useful when constructing the launch command specification, as we shall see later.
Once your .snap file is built, it’s ready to install locally, run (to test it) and deploy (to the app store). The contents of the .snap can be listed using
unsquashfs -l *.snap|less which is great for seeing what files are where. Remember, this listing will exactly match the recursive contents of the virtual machine’s
prime directory. Each of your snap apps will be named differently and have their own virtual machine, and their own resulting final .snap.
Strangely, in my opinion, the snapcraft Python plugin assumes you have a
setup.py file and that your app’s functionality is installed via distutils. The official Snapcraft Python tutorial assumes this approach. If your apps don’t use a setup.py (mine typically don’t) then you need to explicitly copy your source code into the VM
prime directory using a
dump plugin command (which simply copies files). I’ll be using this technique throughout this article.
You can tell snapcraft to use your existing
requirements.txt as well as install Python packages more explicitly using apt install commands inside your
snapcraft.yaml. These Python site packages are pulled off the internet and built inside the virtual machine, and finally copied into the VM’s
prime directory, e.g. if you have
/root/prime/lib/python3.6/site-packages/pip/_vendor/requests gets created and ends up in the final . snap file.
The version of Python seems to depend on the base snap image you choose, which currently is core18 which means Ubuntu 18.04 which in turn means Python 3.6. You may be able to override the Python version — see more info below.
When a snap is properly installed and run by a user, the PYTHONPATH of the Python shipped in the snap correctly contains both the
$SNAP directory and the Python site packages directory. Your launch command specification in the
snapcraft.yamlcan add other directories within the snap to the
PATH if you need to.
Here is a simple Python program that prints a few messages, lists the contents of the current directory and makes an internet GET request. It’s a CLI app not a GUI app, thus is much simpler to build, and you install and run it in the Linux terminal. Let’s get it into the app store!
Its actually ok to put your junk test apps in the app store — they have the same importance as big important apps, so you might want to prefix your app name with something like ‘test’ to help reduce the pollution of the single snapcraft namespace.
You don’t need to change your source code in any way to create a .snap file app. Here is a
snapcraft.yaml file for the above:
The list of
stage-packages within the
andy-py-cli part above (which is also tagged with the
python plugin) are just the libraries that Python depends on. The above list should work perfectly well for Python 3.6 as of April 2019, though I had to spend a day chasing the
libslang2 dependency and adding it to the list. When there is a missing package your snap won’t run, and you will have to figure out the sometimes mysterious package library name to add to the list. Usually the error message will be the major clue and together with some googling you should be able to find the name of the missing entry.
I wish snap technology could utilise what PyInstaller does — and automatically find all the needed dependencies.
requirements.txt for this example, should contain a single line:
requests Your directory structure for this sample application is:
Make sure your Python app runs OK outside of snapcraft e.g.
python3 main.py then when happy, simply run
snapcraft. The .snap file will be generated. If you get errors, see the bugs and troubleshooting section later in this article. View the contents of your snap (optional) with
unsquashfs -l *.snap|less.
Then install and run the snap locally with
sudo snap install --devmode --dangerous *.snap
To publish you need to reserve your app store name and push to the app store. Use your Ubuntu login.
snapcraft register andy-py-cli
snapcraft push --release=edge *.snap
Ready to release!
Revision 1 of 'andy-py-cli' created.
Track Arch Channel Version Revision
latest amd64 stable - -
candidate - -
beta - -
edge 0.1 1
Now you can view your app store page which should now list this new app! And you can install your app on any Linux machine with
snap install --edge andy-py-cli. If I had released to the stable channel, the
--edge parameter would not have been needed.
Once I am happy to release to the stable channel, I don’t need to re-upload anything, I simply look at the Revision number of my app (see above output) which is 1 and say
snapcraft release andy-py-cli 1 stable which marks this release as stable.
Go on, try it — I’ve really pushed this app into the app store and you should be able to install and run it:
snap install andy-py-cli then
What a great way to distribute apps and utilities — no worrying about Python installations, versions or requirements, just a single command to install.
Github repo for this project is here. You won’t be able to publish this app yourself because I have already reserved the name
andy-py-cli so change the name of your app in your yaml file and register that name instead.