py2app - convert python scripts into standalone Mac OS X applications
py2app is a Python distutils suite which provides several
useful features for distributing Python applications and libraries
on the Mac OS X platform. The py2app suite contains the following
packages:
- py2app:
- A distutils command that converts Python scripts into
executable Mac OS X applications, able to run without requiring
a Python installation. It has been used to create standalone
application bundles and plug-ins for open source, commercial,
and in-house projects. It is known compatible with GUI
frameworks such as wxPython, Tkinter, pygame, PyQt, and
PyObjC, but it should work in the context of any framework with
little or no effort. It may also be used in conjunction with
PyObjC to write plug-ins for Cocoa applications, screen
savers, preference panes, etc. This is a complete replacement
for the bundlebuilder tool included in the Python standard library.
- bdist_mpkg:
- Creates Mac OS X installer .mpkg files from Python libraries.
Installer packages are familiar to Mac OS X users, provide a
safe and easy way to authenticate as root to install privileged
files such as headers and scripts. Once a bdist_mpkg
distribution has been created, it may be installed to other
machines with a similar Python environment without requiring a
compiler. Installer packages can be used to do distributed
installations with tools such as Apple Remote Desktop or they
could be integrated into a custom Mac OS X installation DVD
with a tool such as Slipy.
- macholib:
- Reads and writes the Mach-O object file format. Used by py2app
to build a dependency graph of dyld and framework dependencies for your
application, and then to copy them into your application and rewrite
their load commands to be @executable_path relative. The end
result is that your application is going to be completely standalone
beyond a default install of Mac OS X. You no longer have to worry
about linking all of your dependencies statically, using
install_name_tool, etc. It's all taken care of!
- modulegraph:
- A replacement for the Python standard library modulefinder. Stores
the module dependency tree in a graph data structure and allows for
advanced filtering and analysis capabilities, such as GraphViz dot
output. This is used internally by py2app, but the code should
be completely cross-platform. It is hoped that this package will be
adopted by application packaging software for other platforms in the
future, such as py2exe and cx_Freeze.
- altgraph:
- This is a fork of Istvan Albert's graphlib, and it used internally
by both macholib and modulegraph. It contains several small
feature and performance enhancements over the original graphlib.
Any components of the py2app suite may be distributed under the MIT
or PSF open source licenses.
Note that the installer for PyObjC 1.2 ships with py2app 0.1.7.
An installer for py2app 0.1.8 may be downloaded from:
The source to py2app 0.1.8 may be downloaded here:
py2app maintains a public subversion source code repository currently
located at:
As of version 0.1.6, py2app uses the extra_path feature of
distutils, which changes the installation layout.
If you have py2app 0.1.5 or earlier installed, you must manually
remove the following directories from your site-packages directory
(probably /Library/Python/2.3) if they exist before upgrading:
- py2app
- macholib
- bdist_mpkg
- modulegraph
- altgraph
How do I use py2app?
py2app is a distutils command that is used in a similar manner to
py2exe. For your application, you must create a Python script
(conventionally named setup.py) that looks like the following:
#!/usr/bin/env python
"""
setup.py - script for building MyApplication
Usage:
% python setup.py py2app
"""
from distutils.core import setup
import py2app
setup(
app=['MyApplication.py'],
)
When running this script as directed, py2app will do the following:
Process the command line for arguments. The arguments accepted by the py2app
command can be enumerated using the following command line:
% python setup.py py2app --help
Global options:
... (these are available from any distutils command)
Options for 'py2app' command:
... (these are specific to py2app)
usage:
... (this is a generic distutils usage message)
Note that any of the options accepted on the command line may also be used
in your setup.py script! For example:
#!/usr/bin/env python
"""
setup.py - script for building MyApplication
"""
from distutils.core import setup
import py2app
# Note that you must replace hypens '-' with underscores '_'
# when converting option names from the command line to a script.
# For example, the --argv-emulation option is passed as
# argv_emulation in an options dict.
py2app_options = dict(
# Map "open document" events to sys.argv.
# Scripts that expect files as command line arguments
# can be trivially used as "droplets" using this option.
# Without this option, sys.argv should not be used at all
# as it will contain only Mac OS X specific stuff.
argv_emulation=True,
# This is a shortcut that will place MyApplication.icns
# in the Contents/Resources folder of the application bundle,
# and make sure the CFBundleIcon plist key is set appropriately.
iconfile='MyApplication.icns',
)
setup(
app=['MyApplication.py'],
options=dict(
# Each command is allowed to have its own
# options, so we must specify that these
# options are py2app specific.
py2app=py2app_options,
)
)
Issue the distutils build command
- If your application needs any Extensions, then these will be
built at this time, and the build directory will be added to sys.path.
Analyze the application for Python dependencies
- Compile MyApplication.py to Python bytecode and analyze it for
import statements.
- Build a dependency graph of everything it finds (with modulegraph).
- It will look for modules and packages in the same manner as Python would
if you ran python MyApplication.py. Namely, it will look first in the
same directory as MyApplication.py, and then it will search sys.path.
If for some reason you need it to look somewhere else, simply modify your
setup.py script such that it modifies sys.path before calling the
distutils setup(...) function.
Make sense of the dependencies
- Using special library-specific tweaks called recipes, it will modify
this dependency graph as needed. For example, it will perform such tasks
as eliminating unwanted dependencies (pydoc's import of Tkinter) and
including "plugins" for certain libraries that do not use the normal import
statements (PIL, docutils). See the section below on recipes for
more information about this process.
Create the application bundle
- An application bundle will be created in the dist directory with the name
of your application.
- Based upon information in py2app and information you passed to
setup(...), an Info.plist will be created in the application bundle
with metadata appropriate to your application.
- A __boot__.py script will be created in the Contents/Resources folder
of the application bundle containing py2app specific bootstrapping code
to get your application running.
- The main script of your application will be copied as-is to the
Contents/Resources/Python folder. This may change in the future,
but it is currently in source form and is not obfuscated in any way
other than its location.
- Packages that were explicitly included with the --packages option are
placed in Contents/Resources/Python/site-packages.
- A zip file containing all other dependencies is created at
Contents/Resources/Python/site-packages.zip.
- Extensions that could not be included in the zip file are copied to
appropriate locations in Contents/Resources/Python/lib-dynload.
Make the application bundle standalone
- Since a typical Python application may have C library dependencies, such as
the Python interpreter itself, wxWidgets, etc. a second dependency resolution
pass occurs on the application bundle.
- Scan the application bundle for all Mach-O files (executables, shared libraries,
plugins, extensions, etc.).
- Read the load commands from every Mach-O file (using macholib) and build
a dependency graph.
- Copy in every dependent dylib (shared library) and framework that is not already
in the application bundle. Note that dylibs and frameworks in vendor locations
(/System and /usr - except for /usr/local) are NOT included in your
application bundle. This can include the Python interpreter, if you are using
a Python interpreter shipped with Mac OS X. Thus your application may be
"tightly bound" to a particular major version of Mac OS X if you are using
the vendor Python.
- Rewrite the Mach-O load commands such that the libraries know that
they have moved inside of an application bundle (i.e. using
@executable_path relative ids).
- Strip every Mach-O file of extraneous information (debugging symbols, etc.)
to save space. This may be disabled with --no-strip.
What recipes does py2app come with?
- docutils:
Locates and includes all plugins that ship with docutils (languages,
parsers, readers, writers)
- pydoc:
Removes several dependencies that are only used when running the pydoc
web server or Tkinter GUI (Tkinter, tty, BaseHTTPServer, mimetools, select,
threading, ic, getopt).
- pygame:
Includes the whole pygame package as-is, so that it will locate its data
files correctly. This recipe may be improved in the future if pygame
undergoes appropriate modifications.
- PIL:
Locates and includes all image plugins (Python modules that end with
ImagePlugin.py), removes unwanted dependencies on Tkinter.
- pyOpenGL:
Includes the whole pyOpenGL package as-is, so that it can read its version
file during __init__.py. This recipe may be improved in the future if
PyOpenGL undergoes appropriate modifications.
- py2app:
Includes the whole py2app package as-is, so that it has copies of the
executable and plugin templates. This recipe may be improved in the future
if py2app undergoes appropriate modifications.
- sip:
If ANY extension that uses sip is detected, include all extensions that use
sip. This is necessary because sip generates C code to do its imports,
and is thus not trackable by bytecode analysis. The only package known to use
sip is PyQt, so what this means is that if you use any of PyQt, then
all of it will be included.
Note that recipes are developed on an
as-needed basis, and coverage of every single Python library is not possible.
If you have trouble with a particular library, please let us know.
The following packages are known to need recipes, but none currently exist:
- PEAK:
The workaround is to include PEAK using the packages option.
- Anything that uses Pango or GTK+:
These C libraries require data files and environment variables set up.
A workaround exists, but one has not yet been written and tested.
- wxPython 2.4.x:
A data_files option to include a resource file must be added to
setup.py:
#!/usr/bin/env python
"""
setup.py - workaround for wxPython 2.4.x
Usage:
% python setup.py py2app
"""
from distutils.core import setup
import py2app
setup(
app=['test.py'],
data_files=[('../Frameworks', [
'/usr/local/lib/libwx_mac-2.4.0.rsrc',
]
)],
)
XXX
- What's the recommended py2app development model?
- What does py2app install?
- How does py2app differ from py2exe or cx_Freeze?
- When should I subclass the py2app command?
- Known issues with py2app
XXX
- What exactly does bdist_mpkg do?
- What options does bdist_mpkg accept?
- When should I subclass the bdist_mpkg command?