Commits

Ronald Oussoren committed a670c1f

Add test for having a script where the filename doesn't end with '.py' or '.pyw'

(causes test failure with current released versions of modulegraph)

Comments (0)

Files changed (13)

doc/changelog.rst

 
 py2app 0.8 is a feature release
 
+- Issue #80: Add support for copying system plugins into the application
+  bundle.
+ 
+  Py2app now supports a new option *include_plugins*. The value of this
+  is a list of paths to plugins that should be copied into the application
+  bundle. 
+
+  Items in the list are either paths, or a tuple with the plugin type
+  and the path::
+
+      include_plugins=[
+        "MyPlugins/MyDocument.qlgenerator",
+        ("SystemConfiguration", "MyPlugins/MyConfig.plugin"),
+      ]
+
+  Py2app currently knows about the following plugin suffixes: 
+  ``.qlgenerator``, ``.mdimporter``, ``.xpc``, ``.service``, 
+  ``.prefPane``, ``.iaplugin`` and ``.action``. These plugins
+  can be added without specifying the plugin type.
+
 - Issue #83: Setup.py now refuses to install when the current
   platform is not Mac OS X. 
   
   --semi-standalone (-s)  depend on an existing installation of Python
   --alias (-A)            Use an alias to current source file (for development
                           only!)
-  --argv-emulation (-a)   Use argv emulation [disabled for plugins]. Does not work with python 3.x
+  --argv-emulation (-a)   Use argv emulation [disabled for plugins]. 
   --argv-inject           Inject some commands into the argv
   --use-pythonpath        Allow PYTHONPATH to effect the interpreter's
                           environment
                           explicitly imported.
   --extra-scripts         comma-separated list of additional scripts to include
                           in an application or plugin.
+  --include-plugins       comma-seperated list of additional plugins to include
+                          in an application

py2app/build_app.py

 from distutils.sysconfig import get_config_var
 PYTHONFRAMEWORK=get_config_var('PYTHONFRAMEWORK')
 
+
+PLUGIN_SUFFIXES = {
+        '.qlgenerator':    'QuickLook',
+        '.mdimporter':     'Spotlight',
+        '.xpc':            'XPCServices',
+        '.service':        'Services',
+        '.prefPane':       'PreferencePanes',
+        '.iaplugin':       'InternetAccounts',
+        '.action':         'Automator',
+}
+
 try:
     basestring
 except NameError:
         ("qt-plugins=", None, "set of Qt plugins to include in the application bundle (default None)"),
         ("matplotlib-backends=", None, "set of matplotlib backends to include (default: include entire package)"),
         ("extra-scripts=", None, "set of scripts to include in the application bundle, next to the main application script"),
+        ("include-plugins=", None, "List of plugins to include"),
         ]
 
     boolean_options = [
         self.qt_plugins = None
         self.matplotlib_backends = None
         self.extra_scripts = None
+        self.include_plugins = None
 
     def finalize_options (self):
         if not self.strip:
         self.qt_plugins = fancy_split(self.qt_plugins)
         self.matplotlib_backends = fancy_split(self.matplotlib_backends)
         self.extra_scripts = fancy_split(self.extra_scripts)
+        self.include_plugins = fancy_split(self.include_plugins)
 
 
         if self.datamodels:
             self.mkpath(os.path.dirname(dest))
             mapc(src, dest)
 
+    def iter_extra_plugins(self):
+        for item in self.include_plugins:
+            if isinstance(item, (list, tuple)):
+                subdir, path = item
+
+            else:
+                ext = os.path.splitext(item)[1]
+                try:
+                    subdir = PLUGIN_SUFFIXES[ext]
+                    path = item
+                except KeyError:
+                    raise DistutilsOptionError("Cannot determine subdirectory for plugin %s"%(item,))
+
+            yield path, os.path.join(subdir, os.path.basename(path))
+
     def iter_data_files(self):
         dist = self.distribution
         allres = chain(getattr(dist, 'data_files', ()) or (), self.resources)
                 traceback.print_exc()
                 raise
 
+        plugindir = os.path.join(appdir, 'Contents', 'Library')
+        for src, dest in self.iter_extra_plugins():
+            dest = os.path.join(plugindir, dest)
+            if src == dest:
+                continue
+
+            makedirs(os.path.dirname(dest))
+            try:
+                copy_resource(src, dest, dry_run=self.dry_run)
+            except:
+                import traceback
+                traceback.print_exc()
+                raise
+
         # symlink frameworks
         for src in self.iter_frameworks():
             dest = os.path.join(
             makedirs(os.path.dirname(dest))
             copy_resource(src, dest, dry_run=self.dry_run)
 
+        plugindir = os.path.join(appdir, 'Contents', 'Library')
+        for src, dest in self.iter_extra_plugins():
+            dest = os.path.join(plugindir, dest)
+            if src == dest:
+                continue
+
+            makedirs(os.path.dirname(dest))
+            copy_resource(src, dest, dry_run=self.dry_run)
+
 
         target.appdir = appdir
         return appdir

py2app_tests/basic_app2/main-script

+import sys
+
+
+def function():
+    import decimal
+
+def import_module(name):
+    try:
+        exec ("import %s"%(name,))
+        m = eval(name)
+    except ImportError:
+        print ("* import failed")
+
+    else:
+        #for k in name.split('.')[1:]:
+        #    m = getattr(m, k)
+        print (m.__name__)
+
+def print_path():
+    print(sys.path)
+
+while True:
+    line = sys.stdin.readline()
+    if not line:
+        break
+
+    try:
+        exec (line)
+    except SystemExit:
+        raise
+
+    except Exception:
+        print ("* Exception " + str(sys.exc_info()[1]))
+
+    sys.stdout.flush()
+    sys.stderr.flush()

py2app_tests/basic_app2/package1/__init__.py

+" package1 "

py2app_tests/basic_app2/package1/subpackage/__init__.py

+" package1.subpackage "

py2app_tests/basic_app2/package1/subpackage/module.py

+"package1.subpackage.module"

py2app_tests/basic_app2/package2/__init__.py

+""" package2 """

py2app_tests/basic_app2/package2/sub/__init__.py

+foo = 42

py2app_tests/basic_app2/package2/sub/data.dat

Empty file added.

py2app_tests/basic_app2/setup.py

+from setuptools import setup
+
+setup(
+    name='BasicApp',
+    app=['main-script'],
+)

py2app_tests/test_basic_app.py

 class TestBasicSemiStandaloneApp (TestBasicApp):
     py2app_args = [ '--semi-standalone', ]
 
+class TestBasicAppScriptName (unittest.TestCase):
+    app_dir = os.path.join(DIR_NAME, 'basic_app2')
+
+class TestBasicAliasAppScriptName (TestBasicAppScriptName):
+    py2app_args = [ '--alias', ]
+
+class TestBasicSemiStandaloneAppScriptName (TestBasicAppScriptName):
+    py2app_args = [ '--semi-standalone', ]
+
 class TestBasicAppWindowsLineEnd (TestBasicApp):
     app_dir = os.path.join(DIR_NAME, 'basic_app_winle')
 
     upload_docs=upload_docs,
     test=test,
 )
-if sys.platform != 'darwinx':
+if sys.platform != 'darwin':
     msg = "This distribution is only supported on MacOSX"
     from distutils.command import build, install
     from setuptools.command import develop, build_ext, install_lib, build_py