109 lines
4.7 KiB
Python
109 lines
4.7 KiB
Python
#-----------------------------------------------------------------------------
|
|
# 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 os
|
|
import sys
|
|
|
|
import threading
|
|
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
|
|
|
|
# Bootloader clears the `_MEIPASS2` environment variable, which allows a PyInstaller-frozen executable to run a
|
|
# different PyInstaller-frozen executable. However, in the case of `multiprocessing`, we are actually trying
|
|
# to run the same executable, so we need to restore `_MEIPASS2` to prevent onefile executable from unpacking
|
|
# again in a different directory.
|
|
#
|
|
# This is needed for `spawn` start method (default on Windows and macOS) and also with `forkserver` start method
|
|
# (available on Linux and macOS). It is not needed for `fork` start method (default on Linux and other Unix OSes),
|
|
# because fork copies the parent process instead of starting it from scratch.
|
|
|
|
# Mix-in to re-set _MEIPASS2 from sys._MEIPASS.
|
|
class FrozenSupportMixIn:
|
|
_lock = threading.Lock()
|
|
|
|
def __init__(self, *args, **kw):
|
|
# The whole code block needs be executed under a lock to prevent race conditions between `os.putenv` and
|
|
# `os.unsetenv` calls when processes are spawned concurrently from multiple threads. See #7410.
|
|
with self._lock:
|
|
# We have to set original _MEIPASS2 value from sys._MEIPASS to get --onefile mode working.
|
|
os.putenv('_MEIPASS2', sys._MEIPASS) # @UndefinedVariable
|
|
try:
|
|
super().__init__(*args, **kw)
|
|
finally:
|
|
# On some platforms (e.g. AIX) 'os.unsetenv()' is not available. In those cases we cannot delete the
|
|
# variable but only set it to the empty string. The bootloader can handle this case.
|
|
if hasattr(os, 'unsetenv'):
|
|
os.unsetenv('_MEIPASS2')
|
|
else:
|
|
os.putenv('_MEIPASS2', '')
|
|
|
|
if sys.platform.startswith('win'):
|
|
# Windows; patch `Popen` for `spawn` start method
|
|
from multiprocessing import popen_spawn_win32
|
|
|
|
class _SpawnPopen(FrozenSupportMixIn, popen_spawn_win32.Popen):
|
|
pass
|
|
|
|
popen_spawn_win32.Popen = _SpawnPopen
|
|
else:
|
|
# UNIX OSes; patch `Popen` for `spawn` and `forkserver` start methods
|
|
from multiprocessing import popen_spawn_posix
|
|
from multiprocessing import popen_forkserver
|
|
|
|
class _SpawnPopen(FrozenSupportMixIn, popen_spawn_posix.Popen):
|
|
pass
|
|
|
|
class _ForkserverPopen(FrozenSupportMixIn, popen_forkserver.Popen):
|
|
pass
|
|
|
|
popen_spawn_posix.Popen = _SpawnPopen
|
|
popen_forkserver.Popen = _ForkserverPopen
|
|
|
|
|
|
_pyi_rthook()
|
|
del _pyi_rthook
|