From 8832fa3c8d49f4b43dbd484e1434d9c45f79bed2 Mon Sep 17 00:00:00 2001 From: Rok Mandeljc Date: Fri, 5 Mar 2021 11:52:20 +0100 Subject: [PATCH] building: collect built-in extensions into lib-dynload sub-directory On macOS and linux, some of the python's built-ins have extension modules that originally reside in python3.X/lib-dynload directory. This directory is in sys.path, therefore the collected extensions have no parent directory and end up directly into _MEIPASS. This commit explicitly diverts such extensions into lib-dynload sub-directory in the _MEIPASS. In addition to decluttering the _MEIPASS on linux and macOS, this also prevents ctypes.CDLL() from picking up the extensions' shared libraries and causing inconsistent behavior between frozen and unfrozen application, which in some corner cases leads to issues with shadowing, such as #5583. --- PyInstaller/building/api.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/PyInstaller/building/api.py b/PyInstaller/building/api.py index 02d5c30a9fe..60e81be2d1e 100644 --- a/PyInstaller/building/api.py +++ b/PyInstaller/building/api.py @@ -42,6 +42,16 @@ from PyInstaller.utils.win32 import winmanifest, icon, versioninfo, winresource +def _is_libdynload_extension(typ, dst, src): + """ + Determines if the given TOC entry is an EXTENSION coming from + python's lib-dynload directory. + """ + return typ == 'EXTENSION' \ + and not os.path.dirname(os.path.normpath(dst)) \ + and os.path.basename(os.path.dirname(src)) == 'lib-dynload' + + class PYZ(Target): """ Creates a ZlibArchive that contains all pure Python modules. @@ -233,6 +243,11 @@ def assemble(self): # file is contained within python egg, it is added with the egg continue if typ in ('BINARY', 'EXTENSION', 'DEPENDENCY'): + if _is_libdynload_extension(typ, inm, fnm): + # Place extensions that originate from python's + # lib-dynload directory into _MEIPASS/lib-dynload + # instead of directly into _MEIPASS + inm = os.path.join('lib-dynload', inm) if self.exclude_binaries and typ == 'EXTENSION': self.dependencies.append((inm, fnm, typ)) elif not self.exclude_binaries or typ == 'DEPENDENCY': @@ -734,7 +749,13 @@ def assemble(self): or os.path.isabs(inm): raise SystemExit('Security-Alert: try to store file outside ' 'of dist-directory. Aborting. %r' % inm) - tofnm = os.path.join(self.name, inm) + if _is_libdynload_extension(typ, inm, fnm): + # Place extensions that originate from python's + # lib-dynload directory into _MEIPASS/lib-dynload + # instead of directly into _MEIPASS + tofnm = os.path.join(self.name, 'lib-dynload', inm) + else: + tofnm = os.path.join(self.name, inm) todir = os.path.dirname(tofnm) if not os.path.exists(todir): os.makedirs(todir)