Todo: 集成多平台 解决因SaiNiu线程抢占资源问题 本地提交测试环境打包 和 正式打包脚本与正式环境打包bat 提交Python32环境包 改进多日志文件生成情况修改打包日志细节

This commit is contained in:
2025-09-18 15:52:03 +08:00
parent 8b9fc925fa
commit 7cfc0c22b7
7608 changed files with 2424791 additions and 25 deletions

View File

@@ -0,0 +1,37 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2013-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
#-----------------------------------------------------------------------------
def _pyi_rthook():
import os
import sys
# The directory names must match TCL_ROOTNAME and TK_ROOTNAME constants defined in `PyInstaller.utils.hooks.tcl_tk`.
tcldir = os.path.join(sys._MEIPASS, '_tcl_data')
tkdir = os.path.join(sys._MEIPASS, '_tk_data')
# Notify "tkinter" of data directories. On macOS, we do not collect data directories if system Tcl/Tk framework is
# used. On other OSes, we always collect them, so their absence is considered an error.
is_darwin = sys.platform == 'darwin'
if os.path.isdir(tcldir):
os.environ["TCL_LIBRARY"] = tcldir
elif not is_darwin:
raise FileNotFoundError('Tcl data directory "%s" not found.' % tcldir)
if os.path.isdir(tkdir):
os.environ["TK_LIBRARY"] = tkdir
elif not is_darwin:
raise FileNotFoundError('Tk data directory "%s" not found.' % tkdir)
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,34 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2005-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
#-----------------------------------------------------------------------------
# This Django rthook was tested with Django 1.8.3.
def _pyi_rthook():
import django.utils.autoreload
_old_restart_with_reloader = django.utils.autoreload.restart_with_reloader
def _restart_with_reloader(*args):
import sys
a0 = sys.argv.pop(0)
try:
return _old_restart_with_reloader(*args)
finally:
sys.argv.insert(0, a0)
# Override restart_with_reloader() function, otherwise the app might complain that some commands do not exist;
# e.g., runserver.
django.utils.autoreload.restart_with_reloader = _restart_with_reloader
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,41 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2015-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
#-----------------------------------------------------------------------------
def _pyi_rthook():
import atexit
import os
import sys
import tempfile
pixbuf_file = os.path.join(sys._MEIPASS, 'lib', 'gdk-pixbuf', 'loaders.cache')
# If we are not on Windows, we need to rewrite the cache -> we rewrite on macOS to support --onefile mode
if os.path.exists(pixbuf_file) and sys.platform != 'win32':
with open(pixbuf_file, 'rb') as fp:
contents = fp.read()
# Create a temporary file with the cache and cleverly replace the prefix we injected with the actual path.
fd, pixbuf_file = tempfile.mkstemp()
with os.fdopen(fd, 'wb') as fp:
libpath = os.path.join(sys._MEIPASS, 'lib').encode('utf-8')
fp.write(contents.replace(b'@executable_path/lib', libpath))
try:
atexit.register(os.unlink, pixbuf_file)
except OSError:
pass
os.environ['GDK_PIXBUF_MODULE_FILE'] = pixbuf_file
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,21 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2015-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
#-----------------------------------------------------------------------------
def _pyi_rthook():
import os
import sys
os.environ['GI_TYPELIB_PATH'] = os.path.join(sys._MEIPASS, 'gi_typelibs')
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,21 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2015-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
#-----------------------------------------------------------------------------
def _pyi_rthook():
import os
import sys
os.environ['GIO_MODULE_DIR'] = os.path.join(sys._MEIPASS, 'gio_modules')
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,37 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2015-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
#-----------------------------------------------------------------------------
def _pyi_rthook():
import os
import sys
# Prepend the frozen application's data dir to XDG_DATA_DIRS. We need to avoid overwriting the existing paths in
# order to allow the frozen application to run system-installed applications (for example, launch a web browser via
# the webbrowser module on Linux). Should the user desire complete isolation of the frozen application from the
# system, they need to clean up XDG_DATA_DIRS at the start of their program (i.e., remove all entries but first).
pyi_data_dir = os.path.join(sys._MEIPASS, 'share')
xdg_data_dirs = os.environ.get('XDG_DATA_DIRS', None)
if xdg_data_dirs:
if pyi_data_dir not in xdg_data_dirs:
xdg_data_dirs = pyi_data_dir + os.pathsep + xdg_data_dirs
else:
xdg_data_dirs = pyi_data_dir
os.environ['XDG_DATA_DIRS'] = xdg_data_dirs
# Cleanup aux variables
del xdg_data_dirs
del pyi_data_dir
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,32 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2013-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
#-----------------------------------------------------------------------------
def _pyi_rthook():
import os
import sys
# Without this environment variable set to 'no' importing 'gst' causes 100% CPU load. (Tested on macOS.)
os.environ['GST_REGISTRY_FORK'] = 'no'
gst_plugin_paths = [sys._MEIPASS, os.path.join(sys._MEIPASS, 'gst-plugins')]
os.environ['GST_PLUGIN_PATH'] = os.pathsep.join(gst_plugin_paths)
# Prevent permission issues on Windows
os.environ['GST_REGISTRY'] = os.path.join(sys._MEIPASS, 'registry.bin')
# Only use packaged plugins to prevent GStreamer from crashing when it finds plugins from another version which are
# installed system wide.
os.environ['GST_PLUGIN_SYSTEM_PATH'] = ''
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,27 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2015-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
#-----------------------------------------------------------------------------
def _pyi_rthook():
import os
import sys
os.environ['GTK_DATA_PREFIX'] = sys._MEIPASS
os.environ['GTK_EXE_PREFIX'] = sys._MEIPASS
os.environ['GTK_PATH'] = sys._MEIPASS
# Include these here, as GTK will import pango automatically.
os.environ['PANGO_LIBDIR'] = sys._MEIPASS
os.environ['PANGO_SYSCONFDIR'] = os.path.join(sys._MEIPASS, 'etc') # TODO?
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,99 @@
#-----------------------------------------------------------------------------
# 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
#-----------------------------------------------------------------------------
def _pyi_rthook():
import inspect
import os
import sys
import zipfile
# Use sys._MEIPASS with normalized path component separator. This is necessary on some platforms (i.e., msys2/mingw
# python on Windows), because we use string comparisons on the paths.
SYS_PREFIX = os.path.normpath(sys._MEIPASS)
BASE_LIBRARY = os.path.join(SYS_PREFIX, "base_library.zip")
# Obtain the list of modules in base_library.zip, so we can use it in our `_pyi_getsourcefile` implementation.
def _get_base_library_files(filename):
# base_library.zip might not exit
if not os.path.isfile(filename):
return set()
with zipfile.ZipFile(filename, 'r') as zf:
namelist = zf.namelist()
return set(os.path.normpath(entry) for entry in namelist)
base_library_files = _get_base_library_files(BASE_LIBRARY)
# Provide custom implementation of inspect.getsourcefile() for frozen applications that properly resolves relative
# filenames obtained from object (e.g., inspect stack-frames). See #5963.
#
# Although we are overriding `inspect.getsourcefile` function, we are NOT trying to resolve source file here!
# The main purpose of this implementation is to properly resolve relative file names obtained from `co_filename`
# attribute of code objects (which are, in turn, obtained from in turn are obtained from `frame` and `traceback`
# objects). PyInstaller strips absolute paths from `co_filename` when collecting modules, as the original absolute
# paths are not portable/relocatable anyway. The `inspect` module tries to look up the module that corresponds to
# the code object by comparing modules' `__file__` attribute to the value of `co_filename`. Therefore, our override
# needs to resolve the relative file names (usually having a .py suffix) into absolute module names (which, in the
# frozen application, usually have .pyc suffix).
#
# The `inspect` module retrieves the actual source code using `linecache.getlines()`. If the passed source filename
# does not exist, the underlying implementation end up resolving the module, and obtains the source via loader's
# `get_source` method. So for modules in the PYZ archive, it ends up calling `get_source` implementation on our
# `PyiFrozenLoader`. For modules in `base_library.zip`, it ends up calling `get_source` on python's own
# `zipimport.zipimporter`; to properly handle out-of-zip source files, we therefore need to monkey-patch
# `get_source` with our own override that translates the in-zip .pyc filename into out-of-zip .py file location
# and loads the source (this override is done in `pyimod02_importers` module).
#
# The above-described fallback takes place if the .pyc file does not exist on filesystem - if this ever becomes
# a problem, we could consider monkey-patching `linecache.updatecache` (and possibly `checkcache`) to translate
# .pyc paths in `sys._MEIPASS` and `base_library.zip` into .py paths in `sys._MEIPASS` before calling the original
# implementation.
_orig_inspect_getsourcefile = inspect.getsourcefile
def _pyi_getsourcefile(object):
filename = inspect.getfile(object)
filename = os.path.normpath(filename) # Ensure path component separators are normalized.
if not os.path.isabs(filename):
# Check if given filename matches the basename of __main__'s __file__.
main_file = getattr(sys.modules['__main__'], '__file__', None)
if main_file and filename == os.path.basename(main_file):
return main_file
# If the relative filename does not correspond to the frozen entry-point script, convert it to the absolute
# path in either `sys._MEIPASS/base_library.zip` or `sys._MEIPASS`, whichever applicable.
#
# The modules in `sys._MEIPASS/base_library.zip` are handled by python's `zipimport.zipimporter`, and have
# their __file__ attribute point to the .pyc file in the archive. So we match the behavior, in order to
# facilitate matching via __file__ attribute and use of loader's `get_source`, as per the earlier comment
# block.
#
# The modules in PYZ archive are handled by our `PyFrozenLoader`, which now sets the module's __file__
# attribute to point to where .py files would be. Therefore, we can directly merge SYS_PREFIX and filename
# (and if the source .py file exists, it will be loaded directly from filename, without the intermediate
# loader look-up).
pyc_filename = filename + 'c'
if pyc_filename in base_library_files:
return os.path.normpath(os.path.join(BASE_LIBRARY, pyc_filename))
return os.path.normpath(os.path.join(SYS_PREFIX, filename))
elif filename.startswith(SYS_PREFIX):
# If filename is already an absolute file path pointing into application's top-level directory, return it
# as-is and prevent any further processing.
return filename
# Use original implementation as a fallback.
return _orig_inspect_getsourcefile(object)
inspect.getsourcefile = _pyi_getsourcefile
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,24 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2015-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
#-----------------------------------------------------------------------------
def _pyi_rthook():
import os
import sys
root = os.path.join(sys._MEIPASS, 'kivy_install')
os.environ['KIVY_DATA_DIR'] = os.path.join(root, 'data')
os.environ['KIVY_MODULES_DIR'] = os.path.join(root, 'modules')
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,46 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2013-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
#-----------------------------------------------------------------------------
# matplotlib will create $HOME/.matplotlib folder in user's home directory. In this directory there is fontList.cache
# file which lists paths to matplotlib fonts.
#
# When you run your onefile exe for the first time it's extracted to for example "_MEIxxxxx" temp directory and
# fontList.cache file is created with fonts paths pointing to this directory.
#
# Second time you run your exe new directory is created "_MEIyyyyy" but fontList.cache file still points to previous
# directory which was deleted. And then you will get error like:
#
# RuntimeError: Could not open facefile
#
# We need to force matplotlib to recreate config directory every time you run your app.
def _pyi_rthook():
import atexit
import os
import shutil
import _pyi_rth_utils.tempfile # PyInstaller's run-time hook utilities module
# Isolate matplotlib's config dir into temporary directory.
# Use our replacement for `tempfile.mkdtemp` function that properly restricts access to directory on all platforms.
configdir = _pyi_rth_utils.tempfile.secure_mkdtemp()
os.environ['MPLCONFIGDIR'] = configdir
try:
# Remove temp directory at application exit and ignore any errors.
atexit.register(shutil.rmtree, configdir, ignore_errors=True)
except OSError:
pass
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,55 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2017-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
#-----------------------------------------------------------------------------
def _pyi_rthook():
import sys
import multiprocessing
import multiprocessing.spawn
from subprocess import _args_from_interpreter_flags
# Prevent `spawn` from trying to read `__main__` in from the main script
multiprocessing.process.ORIGINAL_DIR = None
def _freeze_support():
# We want to catch the two processes that are spawned by the multiprocessing code:
# - the semaphore tracker, which cleans up named semaphores in the `spawn` multiprocessing mode
# - the fork server, which keeps track of worker processes in the `forkserver` mode.
# Both of these processes are started by spawning a new copy of the running executable, passing it the flags
# from `_args_from_interpreter_flags` and then "-c" and an import statement.
# Look for those flags and the import statement, then `exec()` the code ourselves.
if (
len(sys.argv) >= 2 and sys.argv[-2] == '-c' and sys.argv[-1].startswith(
('from multiprocessing.resource_tracker import main', 'from multiprocessing.forkserver import main')
) and set(sys.argv[1:-2]) == set(_args_from_interpreter_flags())
):
exec(sys.argv[-1])
sys.exit()
if multiprocessing.spawn.is_forking(sys.argv):
kwds = {}
for arg in sys.argv[2:]:
name, value = arg.split('=')
if value == 'None':
kwds[name] = None
else:
kwds[name] = int(value)
multiprocessing.spawn.spawn_main(**kwds)
sys.exit()
multiprocessing.freeze_support = multiprocessing.spawn.freeze_support = _freeze_support
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,178 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2013-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
#-----------------------------------------------------------------------------
# To make pkg_resources work with frozen modules, we need to set the 'Provider' class for PyiFrozenLoader.
# This class decides where to look for resources and other stuff.
#
# 'pkg_resources.NullProvider' is dedicated to abitrary PEP302 loaders, such as our PyiFrozenLoader. It uses method
# __loader__.get_data() in methods pkg_resources.resource_string() and pkg_resources.resource_stream().
#
# We provide PyiFrozenProvider, which subclasses the NullProvider and implements _has(), _isdir(), and _listdir()
# methods, which are needed for pkg_resources.resource_exists(), resource_isdir(), and resource_listdir() to work. We
# cannot use the DefaultProvider, because it provides filesystem-only implementations (and overrides _get() with a
# filesystem-only one), whereas our provider needs to also support embedded resources.
#
# The PyiFrozenProvider allows querying/listing both PYZ-embedded and on-filesystem resources in a frozen package. The
# results are typically combined for both types of resources (e.g., when listing a directory or checking whether a
# resource exists). When the order of precedence matters, the PYZ-embedded resources take precedence over the
# on-filesystem ones, to keep the behavior consistent with the actual file content retrieval via _get() method (which in
# turn uses PyiFrozenLoader's get_data() method). For example, when checking whether a resource is a directory via
# _isdir(), a PYZ-embedded file will take precedence over a potential on-filesystem directory. Also, in contrast to
# unfrozen packages, the frozen ones do not contain source .py files, which are therefore absent from content listings.
def _pyi_rthook():
import os
import pathlib
import sys
import warnings
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore",
category=UserWarning,
message="pkg_resources is deprecated",
)
import pkg_resources
import pyimod02_importers # PyInstaller's bootstrap module
SYS_PREFIX = pathlib.PurePath(sys._MEIPASS)
class _TocFilesystem:
"""
A prefix tree implementation for embedded filesystem reconstruction.
NOTE: as of PyInstaller 6.0, the embedded PYZ archive cannot contain data files anymore. Instead, it contains
only .pyc modules - which are by design not returned by `PyiFrozenProvider`. So this implementation has been
reduced to supporting only directories implied by collected packages.
"""
def __init__(self, tree_node):
self._tree = tree_node
def _get_tree_node(self, path):
path = pathlib.PurePath(path)
current = self._tree
for component in path.parts:
if component not in current:
return None
current = current[component]
return current
def path_exists(self, path):
node = self._get_tree_node(path)
return isinstance(node, dict) # Directory only
def path_isdir(self, path):
node = self._get_tree_node(path)
return isinstance(node, dict) # Directory only
def path_listdir(self, path):
node = self._get_tree_node(path)
if not isinstance(node, dict):
return [] # Non-existent or file
# Return only sub-directories
return [entry_name for entry_name, entry_data in node.items() if isinstance(entry_data, dict)]
class PyiFrozenProvider(pkg_resources.NullProvider):
"""
Custom pkg_resources provider for PyiFrozenLoader.
"""
def __init__(self, module):
super().__init__(module)
# Get top-level path; if "module" corresponds to a package, we need the path to the package itself.
# If "module" is a submodule in a package, we need the path to the parent package.
#
# This is equivalent to `pkg_resources.NullProvider.module_path`, except we construct a `pathlib.PurePath`
# for easier manipulation.
#
# NOTE: the path is NOT resolved for symbolic links, as neither are paths that are passed by `pkg_resources`
# to `_has`, `_isdir`, `_listdir` (they are all anchored to `module_path`, which in turn is just
# `os.path.dirname(module.__file__)`. As `__file__` returned by `PyiFrozenLoader` is always anchored to
# `sys._MEIPASS`, we do not have to worry about cross-linked directories in macOS .app bundles, where the
# resolved `__file__` could be either in the `Contents/Frameworks` directory (the "true" `sys._MEIPASS`), or
# in the `Contents/Resources` directory due to cross-linking.
self._pkg_path = pathlib.PurePath(module.__file__).parent
# Construct _TocFilesystem on top of pre-computed prefix tree provided by pyimod02_importers.
self.embedded_tree = _TocFilesystem(pyimod02_importers.get_pyz_toc_tree())
def _normalize_path(self, path):
# Avoid using `Path.resolve`, because it resolves symlinks. This is undesirable, because the pure path in
# `self._pkg_path` does not have symlinks resolved, so comparison between the two would be faulty. Instead,
# use `os.path.normpath` to normalize the path and get rid of any '..' elements (the path itself should
# already be absolute).
return pathlib.Path(os.path.normpath(path))
def _is_relative_to_package(self, path):
return path == self._pkg_path or self._pkg_path in path.parents
def _has(self, path):
# Prevent access outside the package.
path = self._normalize_path(path)
if not self._is_relative_to_package(path):
return False
# Check the filesystem first to avoid unnecessarily computing the relative path...
if path.exists():
return True
rel_path = path.relative_to(SYS_PREFIX)
return self.embedded_tree.path_exists(rel_path)
def _isdir(self, path):
# Prevent access outside the package.
path = self._normalize_path(path)
if not self._is_relative_to_package(path):
return False
# Embedded resources have precedence over filesystem...
rel_path = path.relative_to(SYS_PREFIX)
node = self.embedded_tree._get_tree_node(rel_path)
if node is None:
return path.is_dir() # No match found; try the filesystem.
else:
# str = file, dict = directory
return not isinstance(node, str)
def _listdir(self, path):
# Prevent access outside the package.
path = self._normalize_path(path)
if not self._is_relative_to_package(path):
return []
# Relative path for searching embedded resources.
rel_path = path.relative_to(SYS_PREFIX)
# List content from embedded filesystem...
content = self.embedded_tree.path_listdir(rel_path)
# ... as well as the actual one.
if path.is_dir():
# Use os.listdir() to avoid having to convert Path objects to strings... Also make sure to de-duplicate
# the results.
path = str(path) # not is_py36
content = list(set(content + os.listdir(path)))
return content
pkg_resources.register_loader_type(pyimod02_importers.PyiFrozenLoader, PyiFrozenProvider)
# With our PyiFrozenFinder now being a path entry finder, it effectively replaces python's FileFinder. So we need
# to register it with `pkg_resources.find_on_path` to allow metadata to be found on filesystem.
pkg_resources.register_finder(pyimod02_importers.PyiFrozenFinder, pkg_resources.find_on_path)
# For the above change to fully take effect, we need to re-initialize pkg_resources's master working set (since the
# original one was built with assumption that sys.path entries are handled by python's FileFinder).
# See https://github.com/pypa/setuptools/issues/373
if hasattr(pkg_resources, '_initialize_master_working_set'):
pkg_resources._initialize_master_working_set()
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,64 @@
#-----------------------------------------------------------------------------
# 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

View File

@@ -0,0 +1,68 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2014-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 path to Qt's components may not default to the wheel layout for self-compiled PyQt5 installations. Mandate the
# wheel layout. See ``utils/hooks/qt.py`` for more details.
def _pyi_rthook():
import os
import sys
from _pyi_rth_utils import is_macos_app_bundle, prepend_path_to_environment_variable
from _pyi_rth_utils import qt as qt_rth_utils
# Ensure this is the only Qt bindings package in the application.
qt_rth_utils.ensure_single_qt_bindings_package("PyQt5")
# Try PyQt5 5.15.4-style path first...
pyqt_path = os.path.join(sys._MEIPASS, 'PyQt5', 'Qt5')
if not os.path.isdir(pyqt_path):
# ... and fall back to the older version
pyqt_path = os.path.join(sys._MEIPASS, 'PyQt5', 'Qt')
os.environ['QT_PLUGIN_PATH'] = os.path.join(pyqt_path, 'plugins')
if is_macos_app_bundle:
# Special handling for macOS .app bundles. To satisfy codesign requirements, we are forced to split `qml`
# directory into two parts; one that keeps only binaries (rooted in `Contents/Frameworks`) and one that keeps
# only data files (rooted in `Contents/Resources), with files from one directory tree being symlinked to the
# other to maintain illusion of a single mixed-content directory. As Qt seems to compute the identifier of its
# QML components based on location of the `qmldir` file w.r.t. the registered QML import paths, we need to
# register both paths, because the `qmldir` file for a component could be reached via either directory tree.
pyqt_path_res = os.path.normpath(
os.path.join(sys._MEIPASS, '..', 'Resources', os.path.relpath(pyqt_path, sys._MEIPASS))
)
os.environ['QML2_IMPORT_PATH'] = os.pathsep.join([
os.path.join(pyqt_path_res, 'qml'),
os.path.join(pyqt_path, 'qml'),
])
else:
os.environ['QML2_IMPORT_PATH'] = os.path.join(pyqt_path, 'qml')
# Back in the day, this was required because PyQt5 5.12.3 explicitly checked that `Qt5Core.dll` was in `PATH`
# (see #4293), and contemporary PyInstaller versions collected that DLL to `sys._MEIPASS`.
#
# Nowadays, we add `sys._MEIPASS` to `PATH` in order to ensure that `QtNetwork` can discover OpenSSL DLLs that might
# have been collected there (i.e., when they were not shipped with the package, and were collected from an external
# location).
if sys.platform.startswith('win'):
prepend_path_to_environment_variable(sys._MEIPASS, 'PATH')
# Qt bindings package installed via PyPI wheels typically ensures that its bundled Qt is relocatable, by creating
# embedded `qt.conf` file during its initialization. This run-time generated qt.conf dynamically sets the Qt prefix
# path to the package's Qt directory. For bindings packages that do not create embedded `qt.conf` during their
# initialization (for example, conda-installed packages), try to perform this step ourselves.
qt_rth_utils.create_embedded_qt_conf("PyQt5", pyqt_path)
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,70 @@
#-----------------------------------------------------------------------------
# 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 path to Qt's components may not default to the wheel layout for self-compiled PyQt6 installations. Mandate the
# wheel layout. See ``utils/hooks/qt.py`` for more details.
def _pyi_rthook():
import os
import sys
from _pyi_rth_utils import is_macos_app_bundle, prepend_path_to_environment_variable
from _pyi_rth_utils import qt as qt_rth_utils
# Ensure this is the only Qt bindings package in the application.
qt_rth_utils.ensure_single_qt_bindings_package("PyQt6")
# Try PyQt6 6.0.3-style path first...
pyqt_path = os.path.join(sys._MEIPASS, 'PyQt6', 'Qt6')
if not os.path.isdir(pyqt_path):
# ... and fall back to the older version.
pyqt_path = os.path.join(sys._MEIPASS, 'PyQt6', 'Qt')
os.environ['QT_PLUGIN_PATH'] = os.path.join(pyqt_path, 'plugins')
if is_macos_app_bundle:
# Special handling for macOS .app bundles. To satisfy codesign requirements, we are forced to split `qml`
# directory into two parts; one that keeps only binaries (rooted in `Contents/Frameworks`) and one that keeps
# only data files (rooted in `Contents/Resources), with files from one directory tree being symlinked to the
# other to maintain illusion of a single mixed-content directory. As Qt seems to compute the identifier of its
# QML components based on location of the `qmldir` file w.r.t. the registered QML import paths, we need to
# register both paths, because the `qmldir` file for a component could be reached via either directory tree.
pyqt_path_res = os.path.normpath(
os.path.join(sys._MEIPASS, '..', 'Resources', os.path.relpath(pyqt_path, sys._MEIPASS))
)
os.environ['QML2_IMPORT_PATH'] = os.pathsep.join([
os.path.join(pyqt_path_res, 'qml'),
os.path.join(pyqt_path, 'qml'),
])
else:
os.environ['QML2_IMPORT_PATH'] = os.path.join(pyqt_path, 'qml')
# Add `sys._MEIPASS` to `PATH` in order to ensure that `QtNetwork` can discover OpenSSL DLLs that might have been
# collected there (i.e., when they were not shipped with the package, and were collected from an external location).
if sys.platform.startswith('win'):
prepend_path_to_environment_variable(sys._MEIPASS, 'PATH')
# For macOS POSIX builds, we need to add `sys._MEIPASS` to `DYLD_LIBRARY_PATH` so that QtNetwork can discover
# OpenSSL dynamic libraries for its `openssl` TLS backend. This also prevents fallback to external locations, such
# as Homebrew. For .app bundles, this is unnecessary because `QtNetwork` explicitly searches `Contents/Frameworks`.
if sys.platform == 'darwin' and not is_macos_app_bundle:
prepend_path_to_environment_variable(sys._MEIPASS, 'DYLD_LIBRARY_PATH')
# Qt bindings package installed via PyPI wheels typically ensures that its bundled Qt is relocatable, by creating
# embedded `qt.conf` file during its initialization. This run-time generated qt.conf dynamically sets the Qt prefix
# path to the package's Qt directory. For bindings packages that do not create embedded `qt.conf` during their
# initialization (for example, conda-installed packages), try to perform this step ourselves.
qt_rth_utils.create_embedded_qt_conf("PyQt6", pyqt_path)
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,63 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2013-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 path to Qt's components may not default to the wheel layout for self-compiled PySide2 installations. Mandate the
# wheel layout. See ``utils/hooks/qt.py`` for more details.
def _pyi_rthook():
import os
import sys
from _pyi_rth_utils import is_macos_app_bundle, prepend_path_to_environment_variable
from _pyi_rth_utils import qt as qt_rth_utils
# Ensure this is the only Qt bindings package in the application.
qt_rth_utils.ensure_single_qt_bindings_package("PySide2")
if sys.platform.startswith('win'):
pyqt_path = os.path.join(sys._MEIPASS, 'PySide2')
else:
pyqt_path = os.path.join(sys._MEIPASS, 'PySide2', 'Qt')
os.environ['QT_PLUGIN_PATH'] = os.path.join(pyqt_path, 'plugins')
if is_macos_app_bundle:
# Special handling for macOS .app bundles. To satisfy codesign requirements, we are forced to split `qml`
# directory into two parts; one that keeps only binaries (rooted in `Contents/Frameworks`) and one that keeps
# only data files (rooted in `Contents/Resources), with files from one directory tree being symlinked to the
# other to maintain illusion of a single mixed-content directory. As Qt seems to compute the identifier of its
# QML components based on location of the `qmldir` file w.r.t. the registered QML import paths, we need to
# register both paths, because the `qmldir` file for a component could be reached via either directory tree.
pyqt_path_res = os.path.normpath(
os.path.join(sys._MEIPASS, '..', 'Resources', os.path.relpath(pyqt_path, sys._MEIPASS))
)
os.environ['QML2_IMPORT_PATH'] = os.pathsep.join([
os.path.join(pyqt_path_res, 'qml'),
os.path.join(pyqt_path, 'qml'),
])
else:
os.environ['QML2_IMPORT_PATH'] = os.path.join(pyqt_path, 'qml')
# Add `sys._MEIPASS` to `PATH` in order to ensure that `QtNetwork` can discover OpenSSL DLLs that might have been
# collected there (i.e., when they were not shipped with the package, and were collected from an external location).
if sys.platform.startswith('win'):
prepend_path_to_environment_variable(sys._MEIPASS, 'PATH')
# Qt bindings package installed via PyPI wheels typically ensures that its bundled Qt is relocatable, by creating
# embedded `qt.conf` file during its initialization. This run-time generated qt.conf dynamically sets the Qt prefix
# path to the package's Qt directory. For bindings packages that do not create embedded `qt.conf` during their
# initialization (for example, conda-installed packages), try to perform this step ourselves.
qt_rth_utils.create_embedded_qt_conf("PySide2", pyqt_path)
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,69 @@
#-----------------------------------------------------------------------------
# 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 path to Qt's components may not default to the wheel layout for self-compiled PySide6 installations. Mandate the
# wheel layout. See ``utils/hooks/qt.py`` for more details.
def _pyi_rthook():
import os
import sys
from _pyi_rth_utils import is_macos_app_bundle, prepend_path_to_environment_variable
from _pyi_rth_utils import qt as qt_rth_utils
# Ensure this is the only Qt bindings package in the application.
qt_rth_utils.ensure_single_qt_bindings_package("PySide6")
if sys.platform.startswith('win'):
pyqt_path = os.path.join(sys._MEIPASS, 'PySide6')
else:
pyqt_path = os.path.join(sys._MEIPASS, 'PySide6', 'Qt')
os.environ['QT_PLUGIN_PATH'] = os.path.join(pyqt_path, 'plugins')
if is_macos_app_bundle:
# Special handling for macOS .app bundles. To satisfy codesign requirements, we are forced to split `qml`
# directory into two parts; one that keeps only binaries (rooted in `Contents/Frameworks`) and one that keeps
# only data files (rooted in `Contents/Resources), with files from one directory tree being symlinked to the
# other to maintain illusion of a single mixed-content directory. As Qt seems to compute the identifier of its
# QML components based on location of the `qmldir` file w.r.t. the registered QML import paths, we need to
# register both paths, because the `qmldir` file for a component could be reached via either directory tree.
pyqt_path_res = os.path.normpath(
os.path.join(sys._MEIPASS, '..', 'Resources', os.path.relpath(pyqt_path, sys._MEIPASS))
)
os.environ['QML2_IMPORT_PATH'] = os.pathsep.join([
os.path.join(pyqt_path_res, 'qml'),
os.path.join(pyqt_path, 'qml'),
])
else:
os.environ['QML2_IMPORT_PATH'] = os.path.join(pyqt_path, 'qml')
# Add `sys._MEIPASS` to `PATH` in order to ensure that `QtNetwork` can discover OpenSSL DLLs that might have been
# collected there (i.e., when they were not shipped with the package, and were collected from an external location).
if sys.platform.startswith('win'):
prepend_path_to_environment_variable(sys._MEIPASS, 'PATH')
# For macOS POSIX builds, we need to add `sys._MEIPASS` to `DYLD_LIBRARY_PATH` so that QtNetwork can discover
# OpenSSL dynamic libraries for its `openssl` TLS backend. This also prevents fallback to external locations, such
# as Homebrew. For .app bundles, this is unnecessary because `QtNetwork` explicitly searches `Contents/Frameworks`.
if sys.platform == 'darwin' and not is_macos_app_bundle:
prepend_path_to_environment_variable(sys._MEIPASS, 'DYLD_LIBRARY_PATH')
# Qt bindings package installed via PyPI wheels typically ensures that its bundled Qt is relocatable, by creating
# embedded `qt.conf` file during its initialization. This run-time generated qt.conf dynamically sets the Qt prefix
# path to the package's Qt directory. For bindings packages that do not create embedded `qt.conf` during their
# initialization (for example, conda-installed packages), try to perform this step ourselves.
qt_rth_utils.create_embedded_qt_conf("PySide6", pyqt_path)
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,37 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2022-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
#-----------------------------------------------------------------------------
# This runtime hook performs the equivalent of the distutils-precedence.pth from the setuptools package;
# it registers a special meta finder that diverts import of distutils to setuptools._distutils, if available.
def _pyi_rthook():
def _install_setuptools_distutils_hack():
import os
import setuptools
# We need to query setuptools version at runtime, because the default value for SETUPTOOLS_USE_DISTUTILS
# has changed at version 60.0 from "stdlib" to "local", and we want to mimic that behavior.
setuptools_major = int(setuptools.__version__.split('.')[0])
default_value = "stdlib" if setuptools_major < 60 else "local"
if os.environ.get("SETUPTOOLS_USE_DISTUTILS", default_value) == "local":
import _distutils_hack
_distutils_hack.add_shim()
try:
_install_setuptools_distutils_hack()
except Exception:
pass
_pyi_rthook()
del _pyi_rthook