Distributing a python application to MacOS X

Today I needed to create an app file for MacOS X with a simple Python script. This is what I learned:

  • Use py2app, it's your friend
  • After installing py2app with easy_install you need to fix it. Otherwise it may not work for your hardware architecture. See Section 2 of this blog post
  • Now, you can safely run python setup.py py2app and you will get a file with the .app extension in the dist directory
  • When you install that (copy into your Applications folder) and try to run it you will get this error (open the Console or do a tail -f /var/log/system.log) to see the error:

22/03/11 10:33:21    com.apple.launchd.peruser.502[580]    ([0x0-0x98098].org.pythonmac.unspecified.myscript1613]) Exited with exit code: 255
22/03/11 10:34:53    [0x0-0x9b09b].org.pythonmac.unspecified.myscript[1645]    Traceback (most recent call last):
22/03/11 10:34:53    [0x0-0x9b09b].org.pythonmac.unspecified.myscript[1645]      File "/Applications/xml2rfc.app/Contents/Resources/__boot__.py", line 103, in <module>
22/03/11 10:34:53    [0x0-0x9b09b].org.pythonmac.unspecified.myscript[1645]        _argv_emulation()
22/03/11 10:34:53    [0x0-0x9b09b].org.pythonmac.unspecified.myscript[1645]      File "/Applications/xml2rfc.app/Contents/Resources/__boot__.py", line 101, in _argv_emulation
22/03/11 10:34:53    [0x0-0x9b09b].org.pythonmac.unspecified.myscript[1645]        _get_argvemulator().mainloop()
22/03/11 10:34:53    [0x0-0x9b09b].org.pythonmac.unspecified.myscript[1645]      File "/Applications/xml2rfc.app/Contents/Resources/__boot__.py", line 40, in mainloop
22/03/11 10:34:53    [0x0-0x9b09b].org.pythonmac.unspecified.myscript[1645]        stoptime = Evt.TickCount() + timeout
22/03/11 10:34:53    [0x0-0x9b09b].org.pythonmac.unspecified.myscript[1645]    AttributeError: 'module' object has no attribute 'TickCount'

In order to avoid it you need to use a different Python from your system Python. In my case it only worked with a 32 bits Python like python-2.7.1-macosx10.3.dmg

Once you have the script installed you can run it from the shell in /Applications/myscript/MacOS/myscript.py

For the record, this is my setup.py file:

import sys
from setuptools import setup

mainscript = 'myscript.py'

if sys.platform == 'darwin':
    extra_options = dict(
        setup_requires=['py2app'],
        app=[mainscript],
        # Cross-platform applications generally expect sys.argv to
        # be used for opening files.
        options=dict(py2app=dict(argv_emulation=True)),
        )
elif sys.platform == 'win32':
    extra_options = dict(
        setup_requires=['py2exe'],
        app=[mainscript],
        )
else:
    extra_options = dict(
        # Normally unix-like platforms will use "setup.py install"
        # and install the main script as such
        scripts=[mainscript],
        )

setup(
    name='myscript',
    version='0.1.0',
    **extra_options
)

It's taken from a py2app example