Skip to content

Commit 3be1411

Browse files
committed
hooks: cleanup: port old hooks to PyInstaller.isolated
Replace uses of old `exec_statement()` and `eval_statement()` hook utility functions with the `PyInstaller.isolated` framework that has been available since PyInstaller 5.0. In following hooks, the old `exec/eval_statement` was replaced with equivalent isolated function: - win32com - wx.lib.activex - enchant - sentry_sdk - ffpyplayer In `OpenGL` hook, use standard `collect_submodules` hook utility function to collect submodules of `OpenGL.arrays` instead of trying to reinvent the wheel. Remove the `gst._gst` hook, which was catering `pygst`, the python-2.7-era GStreamer bindings.
1 parent 2854c12 commit 3be1411

7 files changed

Lines changed: 85 additions & 126 deletions

File tree

_pyinstaller_hooks_contrib/pre_safe_import_module/hook-win32com.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# SPDX-License-Identifier: GPL-2.0-or-later
1111
#-----------------------------------------------------------------------------
1212
"""
13-
PyWin32 package 'win32com' extends it's __path__ attribute with win32comext
13+
PyWin32 package 'win32com' extends its __path__ attribute with win32comext
1414
directory and thus PyInstaller is not able to find modules in it. For example
1515
module 'win32com.shell' is in reality 'win32comext.shell'.
1616
@@ -21,21 +21,25 @@
2121

2222
import os
2323

24-
from PyInstaller.utils.hooks import logger, exec_statement
25-
from PyInstaller.compat import is_win, is_cygwin
24+
from PyInstaller import compat
25+
from PyInstaller import isolated
26+
from PyInstaller.utils.hooks import logger
27+
28+
29+
@isolated.decorate
30+
def _get_win32com_file():
31+
try:
32+
import win32com
33+
return win32com.__file__
34+
except Exception:
35+
return None
2636

2737

2838
def pre_safe_import_module(api):
29-
if not (is_win or is_cygwin):
39+
if not compat.is_win or compat.is_cygwin:
3040
return
31-
win32com_file = exec_statement(
32-
"""
33-
try:
34-
from win32com import __file__
35-
print(__file__)
36-
except Exception:
37-
pass
38-
""").strip()
41+
42+
win32com_file = _get_win32com_file()
3943
if not win32com_file:
4044
logger.debug('win32com: module not available')
4145
return # win32com unavailable

_pyinstaller_hooks_contrib/stdhooks/hook-OpenGL.py

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,7 @@
1717
"""
1818

1919
from PyInstaller.compat import is_win, is_darwin
20-
from PyInstaller.utils.hooks import collect_data_files, exec_statement
21-
import os
22-
import glob
23-
24-
25-
def opengl_arrays_modules():
26-
"""
27-
Return list of array modules for OpenGL module.
28-
e.g. 'OpenGL.arrays.vbo'
29-
"""
30-
statement = 'import OpenGL; print(OpenGL.__path__[0])'
31-
opengl_mod_path = exec_statement(statement)
32-
arrays_mod_path = os.path.join(opengl_mod_path, 'arrays')
33-
files = glob.glob(arrays_mod_path + '/*.py')
34-
modules = []
35-
36-
for f in files:
37-
mod = os.path.splitext(os.path.basename(f))[0]
38-
# Skip __init__ module.
39-
if mod == '__init__':
40-
continue
41-
modules.append('OpenGL.arrays.' + mod)
42-
43-
return modules
20+
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
4421

4522

4623
# PlatformPlugin performs a conditional import based on os.name and
@@ -54,7 +31,7 @@ def opengl_arrays_modules():
5431
hiddenimports = ['OpenGL.platform.glx']
5532

5633
# Arrays modules are needed too.
57-
hiddenimports += opengl_arrays_modules()
34+
hiddenimports += collect_submodules('OpenGL.arrays')
5835

5936
# PyOpenGL 3.x uses ctypes to load DLL libraries. PyOpenGL windows installer
6037
# adds necessary dll files to

_pyinstaller_hooks_contrib/stdhooks/hook-enchant.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,32 @@
1717

1818
import os
1919

20-
from PyInstaller.compat import is_darwin
21-
from PyInstaller.utils.hooks import exec_statement, collect_data_files, \
22-
collect_dynamic_libs, get_installer
20+
from PyInstaller import isolated
21+
from PyInstaller import compat
22+
from PyInstaller.utils.hooks import collect_data_files, collect_dynamic_libs, get_installer
2323

2424
# TODO Add Linux support
2525
# Collect first all files that were installed directly into pyenchant
2626
# package directory and this includes:
2727
# - Windows: libenchat-1.dll, libenchat_ispell.dll, libenchant_myspell.dll, other
2828
# dependent dlls and dictionaries for several languages (de, en, fr)
29-
# - Mac OS X: usually libenchant.dylib and several dictionaries when installed via pip.
29+
# - macOS: usually libenchant.dylib and several dictionaries when installed via pip.
3030
binaries = collect_dynamic_libs('enchant')
3131
datas = collect_data_files('enchant')
3232
excludedimports = ['enchant.tests']
3333

34-
# On OS X try to find files from Homebrew or Macports environments.
35-
if is_darwin:
34+
# On macOS try to find files in Homebrew or Macports environments.
35+
if compat.is_darwin:
3636
# Note: env. var. ENCHANT_PREFIX_DIR is implemented only in the development version:
3737
# https://github.com/AbiWord/enchant
3838
# https://github.com/AbiWord/enchant/pull/2
3939
# TODO Test this hook with development version of enchant.
40-
libenchant = exec_statement("""
41-
from enchant._enchant import e
42-
print(e._name)
43-
""").strip()
40+
@isolated.decorate
41+
def _get_enchant_lib():
42+
from enchant._enchant import e # ctypes.CDLL
43+
return e._name
44+
45+
libenchant = _get_enchant_lib()
4446

4547
installer = get_installer('enchant')
4648
if installer != 'pip':
@@ -49,10 +51,12 @@
4951
binaries.append((libenchant, '.'))
5052

5153
# Collect enchant backends from Macports. Using same file structure as on Windows.
52-
backends = exec_statement("""
54+
@isolated.decorate
55+
def _get_enchant_backends():
5356
from enchant import Broker
54-
for provider in Broker().describe():
55-
print(provider.file)""").strip().split()
57+
return [str(provider.file) for provider in Broker().describe()]
58+
59+
backends = _get_enchant_backends()
5660
binaries.extend([(b, 'enchant/lib/enchant') for b in backends])
5761

5862
# Collect all available dictionaries from Macports. Using same file structure as on Windows.

_pyinstaller_hooks_contrib/stdhooks/hook-ffpyplayer.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,19 @@
1010
# SPDX-License-Identifier: GPL-2.0-or-later
1111
# ------------------------------------------------------------------
1212

13-
from PyInstaller.utils.hooks import eval_statement, collect_submodules
13+
from PyInstaller import isolated
14+
from PyInstaller.utils.hooks import collect_submodules
1415

1516
hiddenimports = collect_submodules("ffpyplayer")
1617
binaries = []
17-
# ffpyplayer has an internal variable tells us where the libraries it was using
18-
for bin in eval_statement("import ffpyplayer; print(ffpyplayer.dep_bins)"):
19-
binaries += [(bin, '.')]
18+
19+
20+
# ffpyplayer has an internal variable that list locations of libraries it is using.
21+
@isolated.decorate
22+
def _get_ffpyplayer_binary_dirs():
23+
import ffpyplayer
24+
return ffpyplayer.dep_bins
25+
26+
27+
for binary_dir in _get_ffpyplayer_binary_dirs():
28+
binaries += [(binary_dir, '.')] # Copy DLLs from these locations into top-level application directory.

_pyinstaller_hooks_contrib/stdhooks/hook-gst._gst.py

Lines changed: 0 additions & 45 deletions
This file was deleted.

_pyinstaller_hooks_contrib/stdhooks/hook-sentry_sdk.py

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,31 @@
99
#
1010
# SPDX-License-Identifier: GPL-2.0-or-later
1111
# ------------------------------------------------------------------
12-
import json
13-
from PyInstaller.utils.hooks import exec_statement
14-
15-
hiddenimports = ["sentry_sdk.integrations.stdlib",
16-
"sentry_sdk.integrations.excepthook",
17-
"sentry_sdk.integrations.dedupe",
18-
"sentry_sdk.integrations.atexit",
19-
"sentry_sdk.integrations.modules",
20-
"sentry_sdk.integrations.argv",
21-
"sentry_sdk.integrations.logging",
22-
"sentry_sdk.integrations.threading"]
23-
24-
statement = """
25-
import json
26-
import sentry_sdk.integrations as si
27-
28-
integrations = []
29-
if hasattr(si, '_AUTO_ENABLING_INTEGRATIONS'):
12+
13+
from PyInstaller import isolated
14+
15+
hiddenimports = [
16+
"sentry_sdk.integrations.stdlib",
17+
"sentry_sdk.integrations.excepthook",
18+
"sentry_sdk.integrations.dedupe",
19+
"sentry_sdk.integrations.atexit",
20+
"sentry_sdk.integrations.modules",
21+
"sentry_sdk.integrations.argv",
22+
"sentry_sdk.integrations.logging",
23+
"sentry_sdk.integrations.threading",
24+
]
25+
26+
27+
@isolated.decorate
28+
def _get_integration_modules():
29+
import sentry_sdk.integrations as si
30+
3031
# _AUTO_ENABLING_INTEGRATIONS is a list of strings with default enabled integrations
3132
# https://github.com/getsentry/sentry-python/blob/c6b6f2086b58ffc674df5c25a600b8a615079fb5/sentry_sdk/integrations/__init__.py#L54-L66
33+
integrations = getattr(si, '_AUTO_ENABLING_INTEGRATIONS', [])
3234

33-
def make_integration_name(integration_name: str):
34-
return integration_name.rsplit(".", maxsplit=1)[0]
35+
# The list contains fully-qualified class names; turn them into module names by removing the last component.
36+
return [integration_name.rsplit('.', maxsplit=1)[0] for integration_name in integrations]
3537

36-
integrations.extend(map(make_integration_name, si._AUTO_ENABLING_INTEGRATIONS))
37-
print(json.dumps(integrations))
38-
"""
3938

40-
hiddenimports.extend(json.loads(exec_statement(statement)))
39+
hiddenimports += _get_integration_modules()

_pyinstaller_hooks_contrib/stdhooks/hook-wx.lib.activex.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,18 @@
1010
# SPDX-License-Identifier: GPL-2.0-or-later
1111
# ------------------------------------------------------------------
1212

13-
from PyInstaller.utils.hooks import exec_statement
13+
from PyInstaller import isolated
1414

15-
# This needed because comtypes wx.lib.activex generates some stuff.
16-
exec_statement("import wx.lib.activex")
15+
16+
# This is necessary because on first import, `wx.lib.activex` generates .tlb file for `comtypes`, unless running in a
17+
# frozen application (in which case, the .tlb file is expected to exist already). So if we are building in a completely
18+
# clean python environment (for example, in a CI/CD pipeline), we need to ensure that .tlb file is generated.
19+
@isolated.decorate
20+
def _ensure_tlb_file_exists():
21+
try:
22+
import wx.lib.activex # noqa: F401
23+
except Exception:
24+
pass
25+
26+
27+
_ensure_tlb_file_exists()

0 commit comments

Comments
 (0)