Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
loader: move ctypes hooks into a module
Move the ctypes hooks from bootstrap script into separate module (pymod04_ctypes). The main motivation for this is to prevent the modifications to global namespace from affecting the ctypes hooks code. Specifically, if user decides to use `sys` or `os` as variables in the global namespace, that should not effect the ctypes hook function _frozen_name(), which treats `sys` and `os` as names of modules (which were bound to those names when the bootstrap script was executed). Fixes #5797. This commit is a cleaned-up and modernized version of corresponding commit from #3038 (daf60a6). Co-authored-by: Hartmut Goebel <h.goebel@crazy-compilers.com>
- Loading branch information
Showing
4 changed files
with
112 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
#----------------------------------------------------------------------------- | ||
# Copyright (c) 2005-2021, PyInstaller Development Team. | ||
# | ||
# Distributed under the terms of the GNU General Public License with exception | ||
# for distributing bootloader. | ||
# | ||
# The full license is in the file COPYING.txt, distributed with this software. | ||
#----------------------------------------------------------------------------- | ||
|
||
""" | ||
Hooks to make ctypes.CDLL, .PyDLL, etc. look in sys._MEIPASS first. | ||
""" | ||
|
||
import sys | ||
|
||
|
||
def install(): | ||
"""Install the hooks. | ||
This must be done from a function as opposed to at module-level, | ||
because when the module is imported/executed, the import machinery | ||
is not completely set up yet. | ||
""" | ||
|
||
import os | ||
|
||
try: | ||
import ctypes | ||
except ImportError: | ||
# ctypes is not included in the frozen application | ||
return | ||
|
||
def _frozen_name(name): | ||
if name: | ||
frozen_name = os.path.join(sys._MEIPASS, os.path.basename(name)) | ||
if os.path.exists(frozen_name) and not os.path.isdir(frozen_name): | ||
name = frozen_name | ||
return name | ||
|
||
class PyInstallerImportError(OSError): | ||
def __init__(self, name): | ||
self.msg = ("Failed to load dynlib/dll %r. " | ||
"Most probably this dynlib/dll was not found " | ||
"when the application was frozen.") % name | ||
self.args = (self.msg,) | ||
|
||
class PyInstallerCDLL(ctypes.CDLL): | ||
def __init__(self, name, *args, **kwargs): | ||
name = _frozen_name(name) | ||
try: | ||
super().__init__(name, *args, **kwargs) | ||
except Exception as base_error: | ||
raise PyInstallerImportError(name) from base_error | ||
|
||
ctypes.CDLL = PyInstallerCDLL | ||
ctypes.cdll = ctypes.LibraryLoader(PyInstallerCDLL) | ||
|
||
class PyInstallerPyDLL(ctypes.PyDLL): | ||
def __init__(self, name, *args, **kwargs): | ||
name = _frozen_name(name) | ||
try: | ||
super().__init__(name, *args, **kwargs) | ||
except Exception as base_error: | ||
raise PyInstallerImportError(name) from base_error | ||
|
||
ctypes.PyDLL = PyInstallerPyDLL | ||
ctypes.pydll = ctypes.LibraryLoader(PyInstallerPyDLL) | ||
|
||
if sys.platform.startswith('win'): | ||
class PyInstallerWinDLL(ctypes.WinDLL): | ||
def __init__(self, name, *args, **kwargs): | ||
name = _frozen_name(name) | ||
try: | ||
super().__init__(name, *args, **kwargs) | ||
except Exception as base_error: | ||
raise PyInstallerImportError(name) from base_error | ||
|
||
ctypes.WinDLL = PyInstallerWinDLL | ||
ctypes.windll = ctypes.LibraryLoader(PyInstallerWinDLL) | ||
|
||
class PyInstallerOleDLL(ctypes.OleDLL): | ||
def __init__(self, name, *args, **kwargs): | ||
name = _frozen_name(name) | ||
try: | ||
super().__init__(name, *args, **kwargs) | ||
except Exception as base_error: | ||
raise PyInstallerImportError(name) from base_error | ||
|
||
ctypes.OleDLL = PyInstallerOleDLL | ||
ctypes.oledll = ctypes.LibraryLoader(PyInstallerOleDLL) | ||
|
||
|
||
# On Mac OS X insert sys._MEIPASS in the first position of the list of paths | ||
# that ctypes uses to search for libraries. | ||
# | ||
# Note: 'ctypes' module will NOT be bundled with every app because code in this | ||
# module is not scanned for module dependencies. It is safe to wrap | ||
# 'ctypes' module into 'try/except ImportError' block. | ||
if sys.platform.startswith('darwin'): | ||
try: | ||
from ctypes.macholib import dyld | ||
dyld.DEFAULT_LIBRARY_FALLBACK.insert(0, sys._MEIPASS) | ||
except ImportError: | ||
# Do nothing when module 'ctypes' is not available. | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Prevent the use of ``sys`` or ``os`` as variables in the global namespace | ||
in frozen script from affecting the ``ctypes`` hooks thar are installed | ||
during bootstrap. |