""" Create an applet from a Python script. You can drag in packages, Info.plist files, icons, etc. It's expected that only one Python script is dragged in. """ from __future__ import print_function import os, sys from distutils.core import setup from plistlib import Plist import py2app import tempfile import shutil import imp import pprint from py2app.util import copy_tree from py2app import build_app try: set except NameError: from sets import Set as set if sys.version_info[0] == 3: raw_input = input HELP_TEXT = """ usage: py2applet --make-setup [options...] script.py [data files...] or: py2applet [options...] script.py [data files...] or: py2applet --help """ SETUP_TEMPLATE = '''""" This is a setup.py script generated by py2applet Usage: python setup.py py2app """ from setuptools import setup APP = %s DATA_FILES = %s OPTIONS = %s setup( app=APP, data_files=DATA_FILES, options={'py2app': OPTIONS}, setup_requires=['py2app'], ) ''' def get_option_map(): optmap = {} for option in build_app.py2app.user_options: opt_long, opt_short = option[:2] if opt_short: optmap['-' + opt_short] = opt_long.rstrip('=') return optmap def get_cmd_options(): options = set() for option in build_app.py2app.user_options: opt_long, opt_short = option[:2] if opt_long.endswith('=') and opt_short: options.add('-' + opt_short) return options def main(): if not sys.argv[1:]: print(HELP_TEXT) return scripts = [] data_files = [] packages = [] args = [] plist = {} iconfile = None parsing_options = True next_is_option = False cmd_options = get_cmd_options() is_make_setup = False for fn in sys.argv[1:]: if parsing_options: if next_is_option: args.append(fn) next_is_option = False continue elif fn == '--make-setup': is_make_setup = True continue elif fn.startswith('-'): args.append(fn) if fn in cmd_options: next_is_option = True continue parsing_options = False if not is_make_setup: fn = os.path.abspath(fn) if fn.endswith('.py'): if scripts: data_files.append(fn) else: scripts.append(fn) elif os.path.basename(fn) == 'Info.plist': plist = Plist.fromFile(fn) elif fn.endswith('.icns') and not iconfile: iconfile = os.path.abspath(fn) elif os.path.isdir(fn): sys.path.insert(0, [os.path.dirname(fn)]) try: path = imp.find_module(os.path.basename(fn))[0] except ImportError: path = '' del sys.path[0] if os.path.realpath(path) == os.path.realpath(fn): packages.append(os.path.basename(fn)) else: data_files.append(fn) else: data_files.append(fn) options = dict( packages=packages, plist=plist, iconfile=iconfile, ) for k,v in list(options.items()): if not v: del options[k] if is_make_setup: make_setup(args, scripts, data_files, options) else: build(args, scripts, data_files, options) def make_setup(args, scripts, data_files, options): optmap = get_option_map() cmd_options = get_cmd_options() while args: cmd = args.pop(0) if cmd in cmd_options: cmd = optmap[cmd] options[cmd.replace('-', '_')] = args.pop(0) elif '=' in cmd: cmd, val = cmd.split('=', 1) options[cmd.lstrip('-').replace('-', '_')] = val else: cmd = optmap.get(cmd, cmd) options[cmd.lstrip('-').replace('-', '_')] = True if os.path.exists('setup.py'): res = '' while res.lower() not in ('y', 'n'): res = raw_input('Existing setup.py detected, replace? [Y/n] ') if not res: break if res == 'n': print('aborted!') return f = open('setup.py', 'w') tvars = tuple(map(pprint.pformat, (scripts, data_files, options))) f.write(SETUP_TEMPLATE % tvars) f.flush() f.close() print('Wrote setup.py') def build(args, scripts, data_files, options): old_argv = sys.argv sys.argv = [sys.argv[0], 'py2app'] + args old_path = sys.path path_insert = set() for script in scripts: path_insert.add(os.path.dirname(script)) sys.path = list(path_insert) + old_path old_dir = os.getcwd() tempdir = tempfile.mkdtemp() os.chdir(tempdir) try: d = setup( app=scripts, data_files=data_files, options={'py2app': options}, ) for target in d.app: copy_tree( target.appdir, os.path.join( os.path.dirname(target.script), os.path.basename(target.appdir), ), preserve_symlinks=True, ) finally: os.chdir(old_dir) shutil.rmtree(tempdir, ignore_errors=True) sys.argv = old_argv sys.path = old_path if __name__ == '__main__': main()