#----------------------------------------------------------------------------- # Copyright (c) 2021-2023, PyInstaller Development Team. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # # The full license is in the file COPYING.txt, distributed with this software. # # SPDX-License-Identifier: Apache-2.0 #----------------------------------------------------------------------------- # # The run-time hook provides a custom module iteration function for our PyiFrozenFinder, which allows # `pkgutil.iter_modules()` to return entries for modules that are embedded in the PYZ archive. The non-embedded modules # (binary extensions, modules collected as only source .py files, etc.) are enumerated using the `fallback_finder` # provided by `PyiFrozenFinder` (which typically would be the python's `FileFinder`). def _pyi_rthook(): import pkgutil import pyimod02_importers # PyInstaller's bootstrap module # This could, in fact, be implemented as `iter_modules()` method of the `PyiFrozenFinder`. However, we want to # avoid importing `pkgutil` in that bootstrap module (i.e., for the `pkgutil.iter_importer_modules()` call on the # fallback finder). def _iter_pyi_frozen_finder_modules(finder, prefix=''): # Fetch PYZ TOC tree from pyimod02_importers pyz_toc_tree = pyimod02_importers.get_pyz_toc_tree() # Finder has already pre-computed the package prefix implied by the search path. Use it to find the starting # node in the prefix tree. if finder._pyz_entry_prefix: pkg_name_parts = finder._pyz_entry_prefix.split('.') else: pkg_name_parts = [] tree_node = pyz_toc_tree for pkg_name_part in pkg_name_parts: tree_node = tree_node.get(pkg_name_part) if not isinstance(tree_node, dict): # This check handles two cases: # a) path does not exist (`tree_node` is `None`) # b) path corresponds to a module instead of a package (`tree_node` is a leaf node (`str`)). tree_node = {} break # Dump the contents of the tree node. for entry_name, entry_data in tree_node.items(): is_pkg = isinstance(entry_data, dict) yield prefix + entry_name, is_pkg # If our finder has a fall-back finder available, iterate its modules as well. By using the public # `fallback_finder` attribute, we force creation of the fallback finder as necessary. # NOTE: we do not care about potential duplicates here, because `pkgutil.iter_modules()` itself # keeps track of yielded names for purposes of de-duplication. if finder.fallback_finder is not None: yield from pkgutil.iter_importer_modules(finder.fallback_finder, prefix) pkgutil.iter_importer_modules.register( pyimod02_importers.PyiFrozenFinder, _iter_pyi_frozen_finder_modules, ) _pyi_rthook() del _pyi_rthook