Browse Source

deploy-ng: set __file__ for all frozen modules (using import hooks)

Many thirdparty libraries rely on __file__ being set properly, for example to be able to locate data files, so it is easiest to just set this to something remotely sensible (like the executable path).
rdb 7 years ago
parent
commit
d00109452a

+ 23 - 0
direct/src/showutil/FreezeTool.py

@@ -86,6 +86,29 @@ else:
 
 # These are overrides for specific modules.
 overrideModules = {
+    # Used by the warnings module, among others, to get line numbers.  Since
+    # we set __file__, this would cause it to try and extract Python code
+    # lines from the main executable, which we don't want.
+    'linecache': """__all__ = ["getline", "clearcache", "checkcache"]
+
+def getline(filename, lineno, module_globals=None):
+    return ''
+
+def clearcache():
+    pass
+
+def getlines(filename, module_globals=None):
+    return []
+
+def checkcache(filename=None):
+    pass
+
+def updatecache(filename, module_globals=None):
+    pass
+
+def lazycache(filename, module_globals):
+    pass
+""",
 }
 
 # These are missing modules that we've reported already this session.

+ 50 - 0
direct/src/showutil/dist.py

@@ -69,6 +69,55 @@ PACKAGE_DATA_DIRS = {
     'matplotlib': {'matplotlib/mpl-data': 'mpl-data'},
 }
 
+# site.py for Python 2.
+SITE_PY2 = u"""
+import sys
+
+# Override __import__ to set __file__ for frozen modules.
+prev_import = __import__
+def __import__(*args, **kwargs):
+    mod = prev_import(*args, **kwargs)
+    if mod:
+        mod.__file__ = sys.executable
+    return mod
+
+# Add our custom __import__ version to the global scope, as well as a builtin
+# definition for __file__ so that it is available in the module itself.
+import __builtin__
+__builtin__.__import__ = __import__
+__builtin__.__file__ = sys.executable
+del __builtin__
+"""
+
+# site.py for Python 3.
+SITE_PY3 = u"""
+import sys
+from _frozen_importlib import _imp, FrozenImporter
+
+if sys.platform == 'win32':
+    # Make sure the preferred encoding is something we actually support.
+    import _bootlocale
+    enc = _bootlocale.getpreferredencoding().lower()
+    if enc != 'utf-8' and not _imp.is_frozen('encodings.%s' % (enc)):
+        def getpreferredencoding(do_setlocale=True):
+            return 'mbcs'
+        _bootlocale.getpreferredencoding = getpreferredencoding
+
+# Alter FrozenImporter to give a __file__ property to frozen modules.
+_find_spec = FrozenImporter.find_spec
+
+def find_spec(fullname, path=None, target=None):
+    spec = _find_spec(fullname, path=path, target=target)
+    if spec:
+        spec.has_location = True
+        spec.origin = sys.executable
+    return spec
+
+FrozenImporter.find_spec = find_spec
+"""
+
+SITE_PY = SITE_PY3 if sys.version_info >= (3,) else SITE_PY2
+
 
 class build_apps(setuptools.Command):
     description = 'build Panda3D applications'
@@ -387,6 +436,7 @@ class build_apps(setuptools.Command):
         def create_runtime(appname, mainscript, use_console):
             freezer = FreezeTool.Freezer(platform=platform, path=path)
             freezer.addModule('__main__', filename=mainscript)
+            freezer.addModule('site', filename='site.py', text=SITE_PY)
             for incmod in self.include_modules.get(appname, []) + self.include_modules.get('*', []):
                 freezer.addModule(incmod)
             for exmod in self.exclude_modules.get(appname, []) + self.exclude_modules.get('*', []):

+ 1 - 1
pandatool/src/deploy-stub/deploy-stub.c

@@ -361,7 +361,7 @@ int Py_FrozenMain(int argc, char **argv)
 #endif
 
     Py_FrozenFlag = 1; /* Suppress errors from getpath.c */
-    Py_NoSiteFlag = 1;
+    Py_NoSiteFlag = 0;
     Py_NoUserSiteDirectory = 1;
 
     if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')