Waffles - Readme

*One-clickable distribution of python programs.*


Waffles are a simple format for creating one-clickable scripts from arbitrarily structured python source trees. They are simple python files and require only a python interpreter to run. 

An example is the [blob swarm waffle]( *(not python3 ready! The packaged project doesn’t support python3)*. Just download it, then run it via 


The waffle format is mainly intended for giving users quick access to pure python programs and passing them around without any installation. For example it is a nice way to package games which use [pyglet]( for graphics. 

**Warning: Waffles are currently ALPHA QUALITY code and might break on you in strange and mysterious ways. Mysterious as in [Cthulhu](, not as in Caspar.**

To achieve that they store a tar.bz2 stream of all packages-to-include, unpack them into a cache dir and add the cache dir to the sys.path. For details see the [cache dir section](#cache-dir) below. 

*This technique is adapted from the [waf build tool]( The name ‘waffle’ was chosen to honor waf as the origin where I got the idea. Also I like waffles a lot, and waffles are baked into their form, too :)*


Waffle has no external dependencies except python. It works in both python2 and python3, but it doesn’t (yet) fix python2 programs for python3[^py3]. 

[^py3]: Waf has some nice fixups for python3, but I didn’t yet get to examine them closely enough to decide if I want to include them. Currently you need to make sure that your program runs on both py2 and py3, for example via switches like `if sys.hexversion>0x300000f:`. If you know how to fix this quickly without inferring external dependencies (all code should be in, please contact me - and in the best case send me a patch. 

To install it, just download and run the []( script — or get a release from our [downloads]( Alternatively get the waffles repository via 

    hg clone

In future a more stable way to do releases might be added… :)


`./ --help` will tell you what you need to know. At the time of writing it says: 

    $ ./ --help
    Usage: [options]
      -h, --help            show this help message and exit
      -o OUTPUT_FILE, --filename=OUTPUT_FILE
                            Set the output filename
                            Package folder to include (can be used multiple times)
                            Python module to include (can be used multiple times)
                            Execute this script
      --unpack-only         only unpack the tar.bz2 data, but don't execute

An example is 

    ./ -o -p ~/Quell/Programme/1w6-programme/textrpg/rpg_lib/ -s ~/Quell/Programme/1w6-programme/textrpg/

This creates a [waffle of the german textrpg tutorial]( and includes the [rpg_lib package]( which the executable []( needs. 

You can also just use your source tree as package, because waffle currently adds all package-dirs to the path. I intend to change that in future, though, and differenciate between sourcetrees and packages (please read the alpha warning again :) ). Also please remember to clean up your sourcetree before including it, else you’ll get all sorts of cruft in your waffle, which will make it harder to digest… bigger than necessary that is :)

An example of including a subtree is the creation of the blob swarm waffle: 

    ./ -p ~/Quell/Programme/fungus/ -s ~/Quell/Programme/fungus/ -o

*Note: Uses a changed fungus\ with `from fungus\_swarm import Scene` to start the swarm, because calling the scene directly relies on the \_\_file\_\_ parameter in the scene.*

`--unpack-only` exists for those who want to examine the contents of the tar.bz2 stream before running the script. If you want to do that for security reasons, please check the plaintext part of the waffle first, so you can be sure that it really only unpacks (and that noone tampered with the waffle file after creation). 

Package Data

Package data has always been some kind of a hassle for me, so I chose the cheapskate way for waffles: If you include a package, you also get all its data, so you can simply access the data relatively to the modules inside the package via 

    f = open(join(dirname(__file__), "path/to/datafile"))

(with os.path.join and os.path.dirname)

This doesn’t work for the main script to run, though, because that is *copied into the waffle*, so its \_\_file\_\_ attribute refers to the waffle file, not the file inside the package dir. So only use the data trick inside packages. If need be, add a simple runner which imports your real runner script and executes the needed functions.

{@id=cache-dir} The cache dir

The cache dir is the place where the packages are unpacked. It’s a pure cache, so it can safely be deleted. If it 
gets deleted, the waffle will just recreate the cache dir at next run (causing a slight delay). 

The name of the cache dir is .waffle-VERSION-MD5HASH, where VERSION is the version of the waffle_maker script and MD5HASH is the md5 hash of the tar.bz2 stream which is included in the waffle file. On Windows the preceding dot is left out. 

Its location of the cache dir gets selected from three choices: 

1. The first and preferred location is inside the users home directory. If that doesn’t work (for example because the user doesn’t have a home directory), it uses 
2. The directory the script resides in. If that isn’t writeable, it falls back to 
3. The /tmp directory. In this case it always deletes its cache dir when it gets run, so noone can create a bogus cache dir and chmod it to you to get you to execute his code.