Skip to content
an auto-update framework for frozen python apps
Pull request Compare This branch is 4 commits ahead, 95 commits behind cloudmatrix:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


esky: keep frozen apps fresh

Esky is an auto-update framework for frozen Python applications. It provides a simple API through which apps can find, fetch and install updates, and a bootstrapping mechanism that keeps the app safe in the face of failed or partial updates.

Esky is currently capable of freezing apps with py2exe, py2app, cxfreeze and bbfreeze. Adding support for other freezer programs should be straightforward; patches will be gratefully accepted.

The main interface is the 'Esky' class, which represents a frozen app. An Esky must be given the path to the top-level directory of the frozen app, and a 'VersionFinder' object that it will use to search for updates. Typical usage for an app automatically updating itself would look something like this:

if hasattr(sys,"frozen"):
    app = esky.Esky(sys.executable,"")

A simple default VersionFinder is provided that hits a specified URL to get a list of available versions. More sophisticated implementations will likely be added in the future, and you're encouraged to develop a custom VersionFinder subclass to meet your specific needs.

The real trick is freezing your app in a format sutiable for use with esky. You'll almost certainly want to use the "bdist_esky" distutils command, and should consult its docstring for full details; the following is an example of a simple script using esky:

from esky import bdist_esky
from distutils.core import setup


Invoking this setup script would create an esky for "appname" version 1.2.3:

#>  python bdist_esky
#>  ls dist/

The contents of this zipfile can be extracted to the filesystem to give a fully working application. If made available online then it can also be found, downloaded and used as an upgrade by older versions of the application.

When you find you need to move beyond the simple logic of Esky.auto_update() (e.g. to show feedback in the GUI) then the following properties and methods are available on the Esky class:

app.version: the current best available version.

app.active_version: the currently-executing version, or None
if the esky isn't for the current app.
app.find_update(): find the best available update, or None
if no updates are available.

app.fetch_version(v): fetch the specified version into local storage.

app.install_version(v): install and activate the specified version.

app.uninstall_version(v): (try to) uninstall the specified version; will
fail if the version is currently in use.
app.cleanup(): (try to) clean up various partly-installed
or old versions lying around the app dir.
app.reinitialize(): re-initialize internal state after changing
the installed version.

If updating an application that is not writable by normal users, esky has the ability to gain root privileges through the use of a helper program. The following methods control this behaviour:

app.has_root(): check whether esky currently has root privs.

app.get_root(): escalate to root privs by spawning helper app.

app.drop_root(): kill helper app and drop root privileges

When properly installed, the on-disk layout of an app managed by esky looks like this:

prog.exe                     - esky bootstrapping executable
appdata/                     - container for all the esky magic
  appname-X.Y.platform/      - specific version of the application
    prog.exe                 - executable(s) as produced by freezer module              - pure-python frozen modules
    pythonXY.dll             - python DLL
    esky-files/              - esky control files
      bootstrap/             - files not yet moved into bootstrapping env
      bootstrap-manifest.txt - list of files expected in bootstrap env
      lockfile.txt           - lockfile to block removal of in-use versions
    ...other deps...
  updates/                   - work area for fetching/unpacking updates

This is also the layout of the zipfiles produced by bdist_esky. The "appname-X.Y" directory is simply a frozen app directory with some extra control information generated by esky.

To install a new version "appname-X.Z", esky performs the following steps:
  • extract it into a temporary directory under "updates"
  • move all bootstrapping files into "appname-X.Z.platm/esky/bootstrap"
  • atomically rename it into the main directory as "appname-X.Z.platform"
  • move contents of "appname-X.Z.platform/esky/bootstrap" into the main dir
  • remove the "appname-X.Z.platform/esky/bootstrap" directory
To uninstall an existing version "appname-X.Y", esky does the following
  • remove files used by only that version from the bootstrap env
  • rename its "bootstrap-manifest.txt" file to "bootstrap-manifest-old.txt"

Where such facilities are provided by the operating system, this process is performed within a filesystem transaction. Nevertheless, the esky bootstrapping executable is able to detect and recover from a failed update should such an unfortunate situation arise.

To clean up after failed or partial updates, applications should periodically call the "cleanup" method on their esky. This removes uninstalled versions and generally tries to tidy up in the main application directory.

Something went wrong with that request. Please try again.