I've polished off some more things, written documentation (in the
email, as reST), and added new features.. today's snapshot is here:
http://undefined.org/python/PyMacApp-0.2.tgz
And now for your documentation reading pleasure..
-----------------------------------------------
PyMacApp: Flexible Executable Stub For Python
-----------------------------------------------
Introduction
------------
PyMacApp is a Cocoa executable stub for Python that accomplishes the
following
goals:
- A single binary can run on OS X 10.1 or later, with no recompilation
required under any circumstance. Therefore, bundles can be built on
systems
without Xcode and/or the SDKs installed.
- It must provide helpful and somewhat customizable errors to the user
in the
form of GUI alert panels.
- It must be able to link to one of many possible Python frameworks at
runtime. This allows the same executable bundle to run in just about
any environment, even if the developer does have to explicitly manage
a separate set of extensions per-framework until we resolve linking
issues.
- It must be able to gracefully handle Python exceptions, because they
may
prevent the application from starting.
- It must not use execve for performance reasons and to ease debugging.
- It should be integrated with BundleBuilder2.
- It should be integrated with Python/PyObjC Xcode templates in source
and/or
binary form.
- It should consider non-framework builds of Python, but there are no
plans
to test this scenario.
Using PyMacApp
--------------
PyMacApp has three required preconditions for use:
- PyMacApp must be the CFBundleExecutable for your application
according to
Info.plist. It is recommended to rename PyMacApp to agree with the
name of your application bundle.
- A ``PyRuntimeLocations`` key must be added to your Info.plist, it is
an
ordered array of strings. Here are some examples:
- Embedded Python 2.3 runtime, as created by BundleBuilder2's
--standalone option.
``@executable_path/../Frameworks/Python.framework/Versions/2.3/Python``
- User-installed Python 2.3 runtime.
``~/Library/Frameworks/Python.framework/Versions/2.3/Python``
- Vendor-installed Python 2.3 runtime, such as the one in OS X 10.3.
``/System/Library/Frameworks/Python.framework/Versions/2.3/Python``
- A main script must be placed in the Resources folder of your
application
bundle. By default, PyMacApp will first check Info.plist for a
``PyMainFileNames`` key, which should be an array of possible names as
strings. If this key is not present, or if no main script is found,
then
these script names are attempted (in this order):
- ``__main__.py``, ``__main__.pyc``, ``__main__.pyo``
- ``__realmain__.py``, ``__realmain__.pyc``, ``__realmain__.pyo``
- ``Main.py``, ``Main.pyc``, ``Main.pyo``
Handling Errors Gracefully
--------------------------
PyMacApp offers alert panels for several common errors that can happen.
The
following errors are low level, and their responses can not be
overridden:
- The ``PyRuntimeLocations`` key is not present in the Info.plist.
- No main script could be located.
- A link error occurred when attempting to load the Python runtime or
one
of its symbols.
- The main script exited with an exception, but there was an error
acquiring
the name of the exception type.
In addition to this, several common high level errors can be handled by
an
error handling script. There is an optional Info.plist key analagous to
``PyMainFileNames`` called ``PyErrorScripts`, and the additional script
names
attempted are: ``__error__``, ``__error__.py``, and ``__error__.sh``.
Note
that the script must be executable (chmod +x), or error handling will
not
work. Also note that the script may be written in any language (with
the
appropriate "hash-bang" line), or may even be a compiled executable.
An error handling script will always receive the user displayable name
of the
application, such as "PyMacApp" as the first argument. If no other
arguments
are received, then there was an error locating a suitable Python
runtime.
Otherwise, the second argument is the name of the Python exception type
(e.g. "ImportError") and the third argument is the string value of the
exception instance (e.g. "No module named foobar").
The stdout of the script is parsed under the following conditions:
- The newline character is '\n'
- The first line of output is required, and will be the title of the
error
dialog (displayed in bold).
- Any additional lines of text are optional, and will be the body of
the error
dialog (not displayed in bold).
Additionally, if the last line of the script is in the form
``ERRORURL: http://example.com/detailederror/ View Detailed Error
Report``
then the "Open Console" button will be replaced with a
"View Detailed Error Report" button that when clicked, will open the
user's default web browser to http://example.com/detailederror/. If a
title is not provided (just the URL), then the button will be named
"Visit Website". It is recommended that this is used to direct a user
to
instructions on how to solve their problem (i.e. download Python, file
a bug,
etc.).
This is an example ``__error__.sh`` script::
#!/bin/sh
if ( test -n "$2" ) ; then
echo "$1 Error"
echo "An unexpected error has occurred during execution of
the main script"
echo ""
echo "$2: $3"
echo ""
echo "See the Console for a detailed traceback."
else
echo "$1 Error"
echo "MacPython 2.3 is required to run this application.";
echo "ERRORURL:
http://homepages.cwi.nl/~jack/macpython/index.html Visit the MacPython
Website";
fi
Reference
---------
PYTHONPATH
This environment variable is set to
``"Contents/Resources:Contents/Resources/PyObjC:$PYTHONPATH"``
CFBundleExecutable
A required Info.plist key, see "Using PyMacApp" and Apple's
documentation.
PyRuntimeLocations
A required Info.plist key, see "Using PyMacApp".
PyExecutableName
An optional Info.plist key representing the name of the python
executable.
It will be merged with the found Python runtime location and will
affect
sys.executable and sys.prefix. The default, ``python``, is
sensible.
PyErrorScripts
An optional Info.plist key, see "Handling Errors Gracefully".
Known Bugs
----------
- It is not currently possible to use a Py_TRACE_REFS Python runtime as
the
PyObject structure does not have the reference count at the head. It
is not
obvious how this could be solved.