Commits

Ronald Oussoren committed 2a02456

Ensure py2app works in paths with unicode characters

This adds a number of tests that check if py2app and the resulting
bundle work when the source tree is in a directory with a non-latin1
unicode character in its name (in this case: \U{SNOWMAN}).

It continues by fixing the problems found by these new tests.

Comments (0)

Files changed (5)

doc/changelog.rst

   when using a system python), this rewrites the fix for issue #10
   mentioned earlier.
 
+- Ensure py2app works correctly when the sources are located in a 
+  directory with non-ascii characters in its name.
+
 
 py2app 0.5.2
 ------------

py2app/create_appbundle.py

 import os
 import plistlib
 import shutil
+import sys
 # XXX - plugins, prefpane, etc?
 from pkg_resources import resource_filename
 import py2app.apptemplate
         makedirs(d)
     plist.write(plistPath)
     srcmain = module.setup.main()
-    destmain = os.path.join(platdir, kw['CFBundleExecutable'])
+    if sys.version_info[0] == 2 and isinstance(kw['CFBundleExecutable'], unicode):
+        destmain = os.path.join(platdir, kw['CFBundleExecutable'].encode('utf-8'))
+    else:
+        destmain = os.path.join(platdir, kw['CFBundleExecutable'])
     open(os.path.join(contents, 'PkgInfo'), 'w').write(
         kw['CFBundlePackageType'] + kw['CFBundleSignature']
     )

py2app/create_pluginbundle.py

 import os
 import plistlib
 import shutil
+import sys
 from pkg_resources import resource_filename
 
 import py2app.bundletemplate
         makedirs(d)
     plist.write(plistPath)
     srcmain = module.setup.main()
-    destmain = os.path.join(platdir, kw['CFBundleExecutable'])
+    if sys.version_info[0] == 2 and isinstance(kw['CFBundleExecutable'], unicode):
+        destmain = os.path.join(platdir, kw['CFBundleExecutable'].encode('utf-8'))
+    else:
+        destmain = os.path.join(platdir, kw['CFBundleExecutable'])
     open(os.path.join(contents, 'PkgInfo'), 'w').write(
         kw['CFBundlePackageType'] + kw['CFBundleSignature']
     )

py2app_tests/test_basic_app.py

 
 class TestBasicApp (unittest.TestCase):
     py2app_args = []
+    app_dir = os.path.join(DIR_NAME, 'basic_app')
 
     # Basic setup code
     #
         p = subprocess.Popen([
                 sys.executable,
                     'setup.py', 'py2app'] + cls.py2app_args,
-            cwd = os.path.join(DIR_NAME, 'basic_app'),
+            cwd = cls.app_dir,
             stdout=subprocess.PIPE,
             stderr=subprocess.STDOUT,
             close_fds=True)
 
     @classmethod
     def tearDownClass(cls):
-        if os.path.exists(os.path.join(DIR_NAME, 'basic_app/build')):
-            shutil.rmtree(os.path.join(DIR_NAME, 'basic_app/build'))
+        if os.path.exists(os.path.join(cls.app_dir, 'build')):
+            shutil.rmtree(os.path.join(cls.app_dir, 'build'))
 
-        if os.path.exists(os.path.join(DIR_NAME, 'basic_app/dist')):
-            shutil.rmtree(os.path.join(DIR_NAME, 'basic_app/dist'))
+        if os.path.exists(os.path.join(cls.app_dir, 'dist')):
+            shutil.rmtree(os.path.join(cls.app_dir, 'dist'))
 
     def start_app(self):
         # Start the test app, return a subprocess object where
         # stdin and stdout are connected to pipes.
         path = os.path.join(
-                DIR_NAME,
-            'basic_app/dist/BasicApp.app/Contents/MacOS/BasicApp')
+                self.app_dir,
+            'dist/BasicApp.app/Contents/MacOS/BasicApp')
 
         p = subprocess.Popen([path],
                 stdin=subprocess.PIPE,
 class TestBasicSemiStandaloneApp (TestBasicApp):
     py2app_args = [ '--semi-standalone', ]
 
+
+class TestBasicAppUnicodePath (TestBasicApp):
+    if sys.version_info[0] == 2:
+        app_dir = os.path.join(DIR_NAME, 'basic_app ' + unichr(2744).encode('utf-8'))
+    else:
+        app_dir = os.path.join(DIR_NAME, 'basic_app ' + chr(2744))
+
+    @classmethod
+    def setUpClass(cls):
+        if os.path.exists(cls.app_dir):
+            shutil.rmtree(cls.app_dir)
+
+        assert not os.path.exists(cls.app_dir)
+        shutil.copytree(TestBasicApp.app_dir, cls.app_dir)
+
+        super(TestBasicAppUnicodePath, cls).setUpClass()
+
+    @classmethod
+    def tearDownClass(cls):
+        if os.path.exists(cls.app_dir):
+            shutil.rmtree(cls.app_dir)
+
+class TestBasicAliasAppUnicodePath (TestBasicAppUnicodePath):
+    py2app_args = [ '--alias', ]
+
+class TestBasicSemiStandaloneAppUnicodePath (TestBasicAppUnicodePath):
+    py2app_args = [ '--semi-standalone', ]
+
 if __name__ == "__main__":
     unittest.main()
 

py2app_tests/test_basic_plugin.py

 
 
 class TestBasicPlugin (unittest.TestCase):
+    plugin_dir = os.path.join(DIR_NAME, 'basic_plugin')
     py2app_args = []
 
     # Basic setup code
         
         p = subprocess.Popen(
             cmd,
-            cwd = os.path.join(DIR_NAME, 'basic_plugin'),
+            cwd = cls.plugin_dir,
             stdout=subprocess.PIPE,
             stderr=subprocess.STDOUT,
             close_fds=True)
 
     @classmethod
     def tearDownClass(cls):
-        if os.path.exists(os.path.join(DIR_NAME, 'basic_plugin/build')):
-            shutil.rmtree(os.path.join(DIR_NAME, 'basic_plugin/build'))
+        if os.path.exists(os.path.join(cls.plugin_dir, 'build')):
+            shutil.rmtree(os.path.join(cls.plugin_dir, 'build'))
 
-        if os.path.exists(os.path.join(DIR_NAME, 'basic_plugin/dist')):
-            shutil.rmtree(os.path.join(DIR_NAME, 'basic_plugin/dist'))
+        if os.path.exists(os.path.join(cls.plugin_dir, 'dist')):
+            shutil.rmtree(os.path.join(cls.plugin_dir, 'dist'))
 
         if os.path.exists('bundle_loader'):
             os.unlink('bundle_loader')
         # Start the test app, return a subprocess object where
         # stdin and stdout are connected to pipes.
         cmd = ['./bundle_loader',
-                    os.path.join(DIR_NAME,
-                                'basic_plugin/dist/BasicPlugin.bundle'),
+                    os.path.join(self.plugin_dir,
+                                'dist/BasicPlugin.bundle'),
         ]
         p = subprocess.Popen(cmd,
                 stdin=subprocess.PIPE,
 class TestBasicSemiStandalonePlugin (TestBasicPlugin):
     py2app_args = [ '--semi-standalone' ]
 
+
+class TestBasicPluginUnicodePath (TestBasicPlugin):
+    if sys.version_info[0] == 2:
+        plugin_dir = os.path.join(DIR_NAME, 'basic_plugin ' + unichr(2744).encode('utf-8'))
+    else:
+        plugin_dir = os.path.join(DIR_NAME, 'basic_plugin ' + chr(2744))
+
+    @classmethod
+    def setUpClass(cls):
+        if os.path.exists(cls.plugin_dir):
+            shutil.rmtree(cls.plugin_dir)
+
+        assert not os.path.exists(cls.plugin_dir)
+        shutil.copytree(TestBasicPlugin.plugin_dir, cls.plugin_dir)
+
+        super(TestBasicPluginUnicodePath, cls).setUpClass()
+
+    @classmethod
+    def tearDownClass(cls):
+        if os.path.exists(cls.plugin_dir):
+            shutil.rmtree(cls.plugin_dir)
+
+class TestBasicAliasPluginUnicodePath (TestBasicPluginUnicodePath):
+    py2app_args = [ '--alias' ]
+
+class TestBasicSemiStandalonePluginUnicodePath (TestBasicPluginUnicodePath):
+    py2app_args = [ '--semi-standalone' ]
+
 if __name__ == "__main__":
     unittest.main()