def _find_tcl_tk(hook_api): """ Get a platform-specific 2-tuple of the absolute paths of the top-level external data directories for both Tcl and Tk, respectively. Returns ------- list 2-tuple whose first element is the value of `${TCL_LIBRARY}` and whose second element is the value of `${TK_LIBRARY}`. """ bins = selectImports(hook_api.__file__) if is_darwin: # _tkinter depends on system Tcl/Tk frameworks. # For example this is the case of Python from homebrew. if not bins: # 'hook_api.binaries' can't be used because on Mac OS X _tkinter.so # might depend on system Tcl/Tk frameworks and these are not # included in 'hook_api.binaries'. bins = getImports(hook_api.__file__) # Reformat data structure from # set(['lib1', 'lib2', 'lib3']) # to # [('Tcl', '/path/to/Tcl'), ('Tk', '/path/to/Tk')] mapping = {} for l in bins: mapping[os.path.basename(l)] = l bins = [ ('Tcl', mapping['Tcl']), ('Tk', mapping['Tk']), ] # _tkinter depends on Tcl/Tk compiled as frameworks. path_to_tcl = bins[0][1] if 'Library/Frameworks' in path_to_tcl: tcl_tk = _find_tcl_tk_darwin_frameworks(bins) # Tcl/Tk compiled as on Linux other Unixes. # For example this is the case of Tcl/Tk from macports. else: tcl_tk = _find_tcl_tk_dir() else: tcl_tk = _find_tcl_tk_dir() return tcl_tk
def _find_tcl_tk(hook_api): """ Get a platform-specific 2-tuple of the absolute paths of the top-level external data directories for both Tcl and Tk, respectively. Returns ------- list 2-tuple whose first element is the value of `${TCL_LIBRARY}` and whose second element is the value of `${TK_LIBRARY}`. """ bins = selectImports(hook_api.__file__) if is_darwin: # _tkinter depends on system Tcl/Tk frameworks. # For example this is the case of Python from homebrew. if not bins: # 'hook_api.binaries' can't be used because on Mac OS X _tkinter.so # might depend on system Tcl/Tk frameworks and these are not # included in 'hook_api.binaries'. bins = getImports(hook_api.__file__) # Reformat data structure from # set(['lib1', 'lib2', 'lib3']) # to # [('Tcl', '/path/to/Tcl'), ('Tk', '/path/to/Tk')] mapping = {} for l in bins: mapping[os.path.basename(l)] = l bins = [ ('Tcl', mapping['Tcl']), ('Tk', mapping['Tk']), ] # _tkinter depends on Tcl/Tk compiled as frameworks. path_to_tcl = bins[0][1] if 'Library/Frameworks' in path_to_tcl and 'Python' not in path_to_tcl: # Edited: https://github.com/pyinstaller/pyinstaller/issues/3753#issuecomment-432464838 tcl_tk = _find_tcl_tk_darwin_frameworks(bins) # Tcl/Tk compiled as on Linux other Unixes. # For example this is the case of Tcl/Tk from macports. else: tcl_tk = _find_tcl_tk_dir() else: tcl_tk = _find_tcl_tk_dir() return tcl_tk
def find_tcl_tk_shared_libs(tkinter_ext_file): """ Find Tcl and Tk shared libraries against which the _tkinter module is linked. Returns ------- list list containing two tuples, one for Tcl and one for Tk library, where each tuple contains library name and its full path, i.e., [(tcl_lib, tcl_libpath), (tk_lib, tk_libpath)]. If a library is not found, the corresponding tuple elements are set to None. """ tcl_lib = None tcl_libpath = None tk_lib = None tk_libpath = None # Do not use bindepend.selectImports, as it ignores libraries seen # during previous invocations. _tkinter_imports = bindepend.getImports(tkinter_ext_file) def _get_library_path(lib): if not compat.is_win and not compat.is_cygwin: # non-Windows systems return the path of the library path = lib else: # We need to find the library path = bindepend.getfullnameof(lib) return path for lib in _tkinter_imports: # On some platforms, full path to the shared librars is returned. # So check only basename to prevent false positive matches due # to words tcl or tk being contained in the path. lib_name = os.path.basename(lib) lib_name_lower = lib_name.lower() # lower-case for comparisons if 'tcl' in lib_name_lower: tcl_lib = lib_name tcl_libpath = _get_library_path(lib) elif 'tk' in lib_name_lower: tk_lib = lib_name tk_libpath = _get_library_path(lib) return [(tcl_lib, tcl_libpath), (tk_lib, tk_libpath)]
def add_qt5_dependencies(hook_file): # Accumulate all dependencies in a set to avoid duplicates. hiddenimports = set() translations_base = set() plugins = set() # Find the module underlying this Qt hook: change # ``/path/to/hook-PyQt5.blah.py`` to ``PyQt5.blah``. hook_name, hook_ext = os.path.splitext(os.path.basename(hook_file)) assert hook_ext.startswith('.py') assert hook_name.startswith('hook-') module_name = hook_name[5:] namespace = module_name.split('.')[0] if namespace not in ('PyQt5', 'PySide2'): raise Exception('Invalid namespace: {0}'.format(namespace)) is_PyQt5 = namespace == 'PyQt5' # Exit if the requested library can't be imported. if ((is_PyQt5 and not pyqt5_library_info.version) or (not is_PyQt5 and not pyside2_library_info.version)): return [], [], [] # Look up the module returned by this import. module = get_module_file_attribute(module_name) logger.debug('add_qt5_dependencies: Examining %s, based on hook of %s.', module, hook_file) # Walk through all the static dependencies of a dynamically-linked library # (``.so``/``.dll``/``.dylib``). imports = set(getImports(module)) while imports: imp = imports.pop() # On Windows, find this library; other platforms already provide the # full path. if is_win: imp = getfullnameof( imp, # First, look for Qt binaries in the local Qt install. pyqt5_library_info.location['BinariesPath'] if is_PyQt5 else pyside2_library_info.location['BinariesPath']) # Strip off the extension and ``lib`` prefix (Linux/Mac) to give the raw # name. Lowercase (since Windows always normalized names to lowercase). lib_name = os.path.splitext(os.path.basename(imp))[0].lower() # Linux libraries sometimes have a dotted version number -- # ``libfoo.so.3``. It's now ''libfoo.so``, but the ``.so`` must also be # removed. if is_linux and os.path.splitext(lib_name)[1] == '.so': lib_name = os.path.splitext(lib_name)[0] if lib_name.startswith('lib'): lib_name = lib_name[3:] # Mac: rename from ``qt`` to ``qt5`` to match names in Windows/Linux. if is_darwin and lib_name.startswith('qt'): lib_name = 'qt5' + lib_name[2:] # match libs with QT_LIBINFIX set to '_conda', i.e. conda-forge builds if lib_name.endswith('_conda'): lib_name = lib_name[:-6] logger.debug('add_qt5_dependencies: raw lib %s -> parsed lib %s', imp, lib_name) # Follow only Qt dependencies. if lib_name in _qt_dynamic_dependencies_dict: # Follow these to find additional dependencies. logger.debug('add_qt5_dependencies: Import of %s.', imp) imports.update(getImports(imp)) # Look up which plugins and translations are needed. dd = _qt_dynamic_dependencies_dict[lib_name] lib_name_hiddenimports, lib_name_translations_base = dd[:2] lib_name_plugins = dd[2:] # Add them in. if lib_name_hiddenimports: hiddenimports.update([namespace + lib_name_hiddenimports]) plugins.update(lib_name_plugins) if lib_name_translations_base: translations_base.update([lib_name_translations_base]) # Change plugins into binaries. binaries = [] for plugin in plugins: more_binaries = qt_plugins_binaries(plugin, namespace=namespace) binaries.extend(more_binaries) # Change translation_base to datas. tp = (pyqt5_library_info.location['TranslationsPath'] if is_PyQt5 else pyside2_library_info.location['TranslationsPath']) datas = [] for tb in translations_base: src = os.path.join(tp, tb + '_*.qm') # Not all PyQt5 installations include translations. See # https://github.com/pyinstaller/pyinstaller/pull/3229#issuecomment-359479893 # and # https://github.com/pyinstaller/pyinstaller/issues/2857#issuecomment-368744341. if glob.glob(src): datas.append(( src, os.path.join( # The PySide2 Windows wheels place translations in a # different location. namespace, '' if not is_PyQt5 and is_win else 'Qt', 'translations'))) else: logger.warning( 'Unable to find Qt5 translations %s. These ' 'translations were not packaged.', src) # Change hiddenimports to a list. hiddenimports = list(hiddenimports) logger.debug( 'add_qt5_dependencies: imports from %s:\n' ' hiddenimports = %s\n' ' binaries = %s\n' ' datas = %s', hook_name, hiddenimports, binaries, datas) return hiddenimports, binaries, datas
pyside2_library_info.location['LibraryExecutablesPath'], pyside2_library_info.location['PrefixPath'] + '/') ]))) ] if compat.is_win: datas += [ x for x in collect_system_data_files( pyside2_library_info.location['PrefixPath'], '.') if os.path.basename(x[0]) == 'qt.conf' ] # Add Linux-specific libraries. if compat.is_linux: # The automatic library detection fails for `NSS # <https://packages.ubuntu.com/search?keywords=libnss3>`_, which is used by # QtWebEngine. In some distributions, the ``libnss`` supporting libraries # are stored in a subdirectory ``nss``. Since ``libnss`` is not statically # linked to these, but dynamically loads them, we need to search for and add # them. # # First, get all libraries linked to ``PyQt5.QtWebEngineWidgets``. for imp in getImports( get_module_file_attribute('PySide2.QtWebEngineWidgets')): # Look for ``libnss3.so``. if os.path.basename(imp).startswith('libnss3.so'): # Find the location of NSS: given a ``/path/to/libnss.so``, # add ``/path/to/nss/*.so`` to get the missing NSS libraries. nss_subdir = os.path.join(os.path.dirname(imp), 'nss') if os.path.exists(nss_subdir): binaries.append((os.path.join(nss_subdir, '*.so'), 'nss'))
def get_qt_webengine_binaries_and_data_files(qt_library_info): binaries = [] datas = [] # Output directory (varies between PyQt and PySide and among OSes; the difference is abstracted by # qt_library_info.qt_rel_dir) rel_data_path = qt_library_info.qt_rel_dir if compat.is_darwin: # On macOS, Qt shared libraries are provided in form of .framework bundles. However, PyInstaller collects shared # library from the bundle into top-level application directory, breaking the bundle structure. # # QtWebEngine and its underlying Chromium engine, however, have very strict data file layout requirements due to # sandboxing, and does not work if the helper process executable does not load the shared library from # QtWebEngineCore.framework (which also needs to contain all resources). # # Therefore, we collect the QtWebEngineCore.framework manually, in order to obtain a working QtWebEngineProcess # helper executable. But because that bypasses our dependency scanner, we need to collect the dependent # .framework bundles as well. And we need to override QTWEBENGINEPROCESS_PATH in rthook, because the # QtWebEngineWidgets python extension actually loads up the copy of shared library that is located in # sys._MEIPASS (as opposed to the manually-copied one in .framework bundle). Furthermore, because the extension # modules use Qt shared libraries in sys._MEIPASS, we also copy all contents of # QtWebEngineCore.framework/Resources into sys._MEIPASS to make resource loading in the main process work. # # Besides being ugly, this approach has three main ramifications: # 1. we bundle two copies of each Qt shared library involved: the copy used by main process, picked up by # dependency scanner; and a copy in manually-collected .framework bundle that is used by the helper process. # 2. the trick with copying contents of Resource directory of QtWebEngineCore.framework does not work in onefile # mode, and consequently QtWebEngine does not work in onefile mode. # 3. copying contents of QtWebEngineCore.framework/Resource means that its Info.plist ends up in sys._MEIPASS, # causing the main process in onedir mode to be mis-identified as "QtWebEngineProcess". # # In the near future, this quagmire will hopefully be properly sorted out, but in the mean time, we have to live # with what we have been given. data_path = qt_library_info.location['DataPath'] libraries = [ 'QtCore', 'QtWebEngineCore', 'QtQuick', 'QtQml', 'QtQmlModels', 'QtNetwork', 'QtGui', 'QtWebChannel', 'QtPositioning' ] for i in libraries: framework_dir = i + '.framework' datas += hooks.collect_system_data_files( os.path.join(data_path, 'lib', framework_dir), os.path.join(rel_data_path, 'lib', framework_dir), True) datas += [(os.path.join(data_path, 'lib', 'QtWebEngineCore.framework', 'Resources'), os.curdir)] else: # Windows and linux locales = 'qtwebengine_locales' resources = 'resources' # Translations datas.append(( os.path.join(qt_library_info.location['TranslationsPath'], locales), os.path.join(rel_data_path, 'translations', locales), )) # Resources; ``DataPath`` is the base directory for ``resources``, as per the # `docs <https://doc.qt.io/qt-5.10/qtwebengine-deploying.html#deploying-resources>`_. datas.append( (os.path.join(qt_library_info.location['DataPath'], resources), os.path.join(rel_data_path, resources)), ) # Helper process executable (QtWebEngineProcess), located in ``LibraryExecutablesPath``. dest = os.path.join( rel_data_path, os.path.relpath(qt_library_info.location['LibraryExecutablesPath'], qt_library_info.location['PrefixPath'])) datas.append( (os.path.join(qt_library_info.location['LibraryExecutablesPath'], 'QtWebEngineProcess*'), dest)) # Add Linux-specific libraries. if compat.is_linux: # The automatic library detection fails for `NSS <https://packages.ubuntu.com/search?keywords=libnss3>`_, which # is used by QtWebEngine. In some distributions, the ``libnss`` supporting libraries are stored in a # subdirectory ``nss``. Since ``libnss`` is not statically linked to these, but dynamically loads them, we need # to search for and add them. # First, get all libraries linked to ``QtWebEngineWidgets`` extension module. module_file = hooks.get_module_file_attribute( qt_library_info.namespace + '.QtWebEngineWidgets') module_imports = bindepend.getImports(module_file) for imp in module_imports: # Look for ``libnss3.so``. if os.path.basename(imp).startswith('libnss3.so'): # Find the location of NSS: given a ``/path/to/libnss.so``, add ``/path/to/nss/*.so`` to get the # missing NSS libraries. nss_glob = os.path.join(os.path.dirname(imp), 'nss', '*.so') if glob.glob(nss_glob): binaries.append((nss_glob, 'nss')) return binaries, datas
def add_qt_dependencies(hook_file): # Accumulate all dependencies in a set to avoid duplicates. hiddenimports = set() translations_base = set() plugins = set() # Find the module underlying this Qt hook: change ``/path/to/hook-PyQt5.blah.py`` to ``PyQt5.blah``. hook_name, hook_ext = os.path.splitext(os.path.basename(hook_file)) assert hook_ext.startswith('.py') assert hook_name.startswith('hook-') module_name = hook_name[5:] namespace = module_name.split('.')[0] # Retrieve Qt library info structure. qt_info = get_qt_library_info(namespace) # Exit if the requested library cannot be imported. # NOTE: qt_info.version can be empty list on older Qt5 versions (#5381). if qt_info.version is None: return [], [], [] # Look up the module returned by this import. module = hooks.get_module_file_attribute(module_name) logger.debug('add_qt%d_dependencies: Examining %s, based on hook of %s.', qt_info.qt_major, module, hook_file) # Walk through all the static dependencies of a dynamically-linked library (``.so``/``.dll``/``.dylib``). imports = set(bindepend.getImports(module)) while imports: imp = imports.pop() # On Windows, find this library; other platforms already provide the full path. if compat.is_win: # First, look for Qt binaries in the local Qt install. imp = bindepend.getfullnameof(imp, qt_info.location['BinariesPath']) # Strip off the extension and ``lib`` prefix (Linux/Mac) to give the raw name. # Lowercase (since Windows always normalizes names to lowercase). lib_name = os.path.splitext(os.path.basename(imp))[0].lower() # Linux libraries sometimes have a dotted version number -- ``libfoo.so.3``. It is now ''libfoo.so``, # but the ``.so`` must also be removed. if compat.is_linux and os.path.splitext(lib_name)[1] == '.so': lib_name = os.path.splitext(lib_name)[0] if lib_name.startswith('lib'): lib_name = lib_name[3:] # Mac OS: handle different naming schemes. PyPI wheels ship framework-enabled Qt builds, where shared libraries # are part of .framework bundles (e.g., ``PyQt5/Qt5/lib/QtCore.framework/Versions/5/QtCore``). In Anaconda # (Py)Qt installations, the shared libraries are installed in environment's library directory, and contain # versioned extensions, e.g., ``libQt5Core.5.dylib``. if compat.is_darwin: if lib_name.startswith('qt') and not lib_name.startswith( 'qt' + str(qt_info.qt_major)): # Qt library from a framework bundle (e.g., ``QtCore``); change prefix from ``qt`` to ``qt5`` or ``qt6`` # to match names in Windows/Linux. lib_name = 'qt' + str(qt_info.qt_major) + lib_name[2:] if lib_name.endswith('.' + str(qt_info.qt_major)): # Qt library from Anaconda, which originally had versioned extension, e.g., ``libfoo.5.dynlib``. # The above processing turned it into ``foo.5``, so we need to remove the last two characters. lib_name = lib_name[:-2] # Match libs with QT_LIBINFIX set to '_conda', i.e. conda-forge builds. if lib_name.endswith('_conda'): lib_name = lib_name[:-6] logger.debug('add_qt%d_dependencies: raw lib %s -> parsed lib %s', qt_info.qt_major, imp, lib_name) # Follow only Qt dependencies. _qt_dynamic_dependencies_dict = (_qt5_dynamic_dependencies_dict if qt_info.qt_major == 5 else _qt6_dynamic_dependencies_dict) if lib_name in _qt_dynamic_dependencies_dict: # Follow these to find additional dependencies. logger.debug('add_qt%d_dependencies: Import of %s.', qt_info.qt_major, imp) imports.update(bindepend.getImports(imp)) # Look up which plugins and translations are needed. dd = _qt_dynamic_dependencies_dict[lib_name] lib_name_hiddenimports, lib_name_translations_base = dd[:2] lib_name_plugins = dd[2:] # Add them in. if lib_name_hiddenimports: hiddenimports.update([namespace + lib_name_hiddenimports]) plugins.update(lib_name_plugins) if lib_name_translations_base: translations_base.update([lib_name_translations_base]) # Change plugins into binaries. binaries = [] for plugin in plugins: more_binaries = qt_plugins_binaries(plugin, namespace=namespace) binaries.extend(more_binaries) # Change translation_base to datas. tp = qt_info.location['TranslationsPath'] tp_dst = os.path.join(qt_info.qt_rel_dir, 'translations') datas = [] for tb in translations_base: src = os.path.join(tp, tb + '_*.qm') # Not all PyQt5 installations include translations. See # https://github.com/pyinstaller/pyinstaller/pull/3229#issuecomment-359479893 # and # https://github.com/pyinstaller/pyinstaller/issues/2857#issuecomment-368744341. if glob.glob(src): datas.append((src, tp_dst)) else: logger.warning( 'Unable to find Qt%d translations %s. These translations were not packaged.', qt_info.qt_major, src) # Change hiddenimports to a list. hiddenimports = list(hiddenimports) logger.debug( 'add_qt%d_dependencies: imports from %s:\n' ' hiddenimports = %s\n' ' binaries = %s\n' ' datas = %s', qt_info.qt_major, hook_name, hiddenimports, binaries, datas) return hiddenimports, binaries, datas
def _find_tcl_tk(hook_api): """ Get a platform-specific 2-tuple of the absolute paths of the top-level external data directories for both Tcl and Tk, respectively. Returns ------- list 2-tuple whose first element is the value of `${TCL_LIBRARY}` and whose second element is the value of `${TK_LIBRARY}`. """ bins = selectImports(hook_api.__file__) if is_darwin: # _tkinter depends on system Tcl/Tk frameworks. # For example this is the case of Python from homebrew. if not bins: # 'hook_api.binaries' can't be used because on Mac OS X _tkinter.so # might depend on system Tcl/Tk frameworks and these are not # included in 'hook_api.binaries'. bins = getImports(hook_api.__file__) if bins: # Reformat data structure from # set(['lib1', 'lib2', 'lib3']) # to # [('Tcl', '/path/to/Tcl'), ('Tk', '/path/to/Tk')] mapping = {} for lib in bins: mapping[os.path.basename(lib)] = lib bins = [ ('Tcl', mapping['Tcl']), ('Tk', mapping['Tk']), ] else: # Starting with macOS 11, system libraries are hidden. # Until we adjust library discovery accordingly, bins # will end up empty. But this implicitly indicates that # the system framework is used, so return None, None # to inform the caller. return None, None # _tkinter depends on Tcl/Tk compiled as frameworks. path_to_tcl = bins[0][1] # OS X system installation of Tcl/Tk. # [/System]/Library/Frameworks/Tcl.framework/Resources/Scripts/Tcl if 'Library/Frameworks/Tcl.framework' in path_to_tcl: #tcl_tk = _find_tcl_tk_darwin_system_frameworks(bins) tcl_tk = None, None # Do not gather system framework's data # Tcl/Tk compiled as on Linux other Unixes. # This is the case of Tcl/Tk from macports and Tck/Tk built into # python.org OS X python distributions. # python.org built-in tcl/tk is located at # /Library/Frameworks/Python.framework/Versions/3.x/lib/libtcl8.6.dylib else: tcl_tk = _find_tcl_tk_dir() else: tcl_tk = _find_tcl_tk_dir() return tcl_tk
def add_qt5_dependencies(hook_file): # Accumulate all dependencies in a set to avoid duplicates. hiddenimports = set() translations_base = set() plugins = set() # Find the module underlying this Qt hook: change # ``/path/to/hook-PyQt5.blah.py`` to ``PyQt5.blah``. hook_name, hook_ext = os.path.splitext(os.path.basename(hook_file)) assert hook_ext.startswith('.py') assert hook_name.startswith('hook-') module_name = hook_name[5:] namespace = module_name.split('.')[0] if namespace != 'PyQt5': raise Exception('Invalid namespace: {0}'.format(namespace)) # Look up the module returned by this import. module = get_module_file_attribute(module_name) logger.debug('add_qt5_dependencies: Examining %s, based on hook of %s.', module, hook_file) # Walk through all the static dependencies of a dynamically-linked library # (``.so``/``.dll``/``.dylib``). imports = set(getImports(module)) while imports: imp = imports.pop() # On Windows, find this library; other platforms already provide the # full path. if is_win: imp = getfullnameof(imp) # Strip off the extension and ``lib`` prefix (Linux/Mac) to give the raw # name. Lowercase (since Windows always normalized names to lowercase). lib_name = os.path.splitext(os.path.basename(imp))[0].lower() # Linux libraries sometimes have a dotted version number -- # ``libfoo.so.3``. It's now ''libfoo.so``, but the ``.so`` must also be # removed. if is_linux and os.path.splitext(lib_name)[1] == '.so': lib_name = os.path.splitext(lib_name)[0] if lib_name.startswith('lib'): lib_name = lib_name[3:] # Mac: rename from ``qt`` to ``qt5`` to match names in Windows/Linux. if is_darwin and lib_name.startswith('qt'): lib_name = 'qt5' + lib_name[2:] logger.debug('add_qt5_dependencies: raw lib %s -> parsed lib %s', imp, lib_name) # Follow only Qt dependencies. if lib_name in _qt_dynamic_dependencies_dict: # Follow these to find additional dependencies. logger.debug('add_qt5_dependencies: Import of %s.', imp) imports.update(getImports(imp)) # Look up which plugins and translations are needed. Avoid Python # 3-only syntax, since the Python 2.7 parser will raise an # exception. The original statment was: ## (lib_name_hiddenimports, lib_name_translations_base, ## *lib_name_plugins) = _qt_dynamic_dependencies_dict[lib_name] dd = _qt_dynamic_dependencies_dict[lib_name] lib_name_hiddenimports, lib_name_translations_base = dd[:2] lib_name_plugins = dd[2:] # Add them in. if lib_name_hiddenimports: hiddenimports.update([lib_name_hiddenimports]) plugins.update(lib_name_plugins) if lib_name_translations_base: translations_base.update([lib_name_translations_base]) # Change plugins into binaries. binaries = [] for plugin in plugins: more_binaries = qt_plugins_binaries(plugin, namespace=namespace) binaries.extend(more_binaries) # Change translation_base to datas. tp = pyqt5_library_info.location['TranslationsPath'] datas = [] for tb in translations_base: src = os.path.join(tp, tb + '_*.qm') # Not all PyQt5 installations include translations. See # https://github.com/pyinstaller/pyinstaller/pull/3229#issuecomment-359479893 # and # https://github.com/pyinstaller/pyinstaller/issues/2857#issuecomment-368744341. if glob.glob(src): datas.append( (src, os.path.join(namespace, 'Qt', 'translations')) ) else: logger.warning('Unable to find Qt5 translations %s. These ' 'translations were not packaged.', src) # Change hiddenimports to a list. hiddenimports = list(hiddenimports) logger.debug('add_qt5_dependencies: imports from %s:\n' ' hiddenimports = %s\n' ' binaries = %s\n' ' datas = %s', hook_name, hiddenimports, binaries, datas) return hiddenimports, binaries, datas