diff options
Diffstat (limited to 'sources/shiboken6/libshiboken/embed/signature_bootstrap.py')
-rw-r--r-- | sources/shiboken6/libshiboken/embed/signature_bootstrap.py | 211 |
1 files changed, 102 insertions, 109 deletions
diff --git a/sources/shiboken6/libshiboken/embed/signature_bootstrap.py b/sources/shiboken6/libshiboken/embed/signature_bootstrap.py index 3078ac90a..37f95cd35 100644 --- a/sources/shiboken6/libshiboken/embed/signature_bootstrap.py +++ b/sources/shiboken6/libshiboken/embed/signature_bootstrap.py @@ -1,41 +1,5 @@ -############################################################################# -## -## Copyright (C) 2019 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of PySide6. -## -## $QT_BEGIN_LICENSE:LGPL$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## GNU Lesser General Public License Usage -## Alternatively, this file may be used under the terms of the GNU Lesser -## General Public License version 3 as published by the Free Software -## Foundation and appearing in the file LICENSE.LGPL3 included in the -## packaging of this file. Please review the following information to -## ensure the GNU Lesser General Public License version 3 requirements -## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -## -## GNU General Public License Usage -## Alternatively, this file may be used under the terms of the GNU -## General Public License version 2.0 or (at your option) the GNU General -## Public license version 3 or any later version approved by the KDE Free -## Qt Foundation. The licenses are as published by the Free Software -## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -## included in the packaging of this file. Please review the following -## information to ensure the GNU General Public License requirements will -## be met: https://www.gnu.org/licenses/gpl-2.0.html and -## https://www.gnu.org/licenses/gpl-3.0.html. -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only """ signature_bootstrap.py @@ -52,22 +16,27 @@ This file replaces the hard to read Python stub in 'signature.cpp', and we could distinguish better between bootstrap related functions and loader functions. It is embedded into 'signature.cpp' as "embed/signature_bootstrap.inc". + +# PYSIDE-1436: Python 3.10 had a problem with EmbeddableZipImporter because the +imports were in the functions. Moved them outside into the globals. """ recursion_trap = 0 -# We avoid real imports in phase 1 that could fail (simply removed all). -# Python 2 is not able to import when the extension import is still active. -# Phase 1 simply defines the functions, which will be used in Phase 2. -# PYSIDE-1621: This can be removed after the backport but we leave it so. +import base64 +import importlib +import io +import os +import sys +import traceback +import zipfile + +from contextlib import contextmanager +from importlib.machinery import ModuleSpec +from pathlib import Path + def bootstrap(): - import sys - import os - import tempfile - import traceback - from contextlib import contextmanager - from pathlib import Path global recursion_trap if recursion_trap: @@ -93,52 +62,83 @@ def bootstrap(): import shibokensupport yield except Exception as e: - print("Problem importing shibokensupport:") - print(f"{e.__class__.__name__}: {e}") + f = sys.stderr + print("Problem importing shibokensupport:", file=f) + print(f"{e.__class__.__name__}: {e}", file=f) traceback.print_exc() - print("sys.path:") + print("sys.path:", file=f) for p in sys.path: - print(" " + p) - sys.stdout.flush() + print(" " + p, file=f) + f.flush() sys.exit(-1) target.remove(support_path) - import shiboken6 as root - path = Path(root.__file__) - rp = path.parent.resolve() - # This can be the shiboken6 directory or the binary module, so search. - look_for = Path("files.dir") / "shibokensupport" / "signature" / "loader.py" - while not (rp / look_for).exists(): - dir = rp.parent - if dir == rp: # Hit root, '/', 'C:\', '\\server\share' - break - rp = dir - - # Here we decide if we work embedded or not. - embedding_var = "pyside_uses_embedding" - use_embedding = bool(getattr(sys, embedding_var, False)) - loader_path = rp / look_for - files_dir = loader_path.parents[2] - assert files_dir.name == "files.dir" - - if not loader_path.exists(): - use_embedding = True - setattr(sys, embedding_var, use_embedding) - - if use_embedding: - target, support_path = prepare_zipfile() + # Here we decide if we re-incarnate the embedded files or use embedding. + incarnated = find_incarnated_files() + if incarnated: + target, support_path = sys.path, os.fspath(incarnated) else: - target, support_path = sys.path, os.fspath(files_dir) + target, support_path = prepare_zipfile() + with ensure_shibokensupport(target, support_path): + from shibokensupport.signature import loader + return loader + +# Newer functionality: +# This function checks if the support directory exist and returns it. +# If does not exist, we try to create it and return it. +# Otherwise, we return None. +def find_incarnated_files(): + import shiboken6 as root + files_dir = Path(root.__file__).resolve().parent / "files.dir" + handle_embedding_switch(files_dir) + if files_dir.exists(): + sys.path.insert(0, os.fspath(files_dir)) + # Note: To avoid recursion problems, we need to preload the loader. + # But that has the side-effect that we need to delay the feature + # initialization until all function pointers are set. + # See `post_init_func` in signature_globals.cpp . + import shibokensupport.signature.loader + del sys.path[0] + return files_dir + return None + + +def handle_embedding_switch(files_dir): + """ + This handles the optional environment variable `SBK_EMBED` + if not set : do nothing + if set to 0, false, no : de-virtualize the Python files + if set to 1, true, yes : virtualize again (delete "files.dir") + """ + env_name = "SBK_EMBED" + env_var = os.environ.get(env_name) + if not env_var: + return + if env_var.lower() in ("1", "t", "true", "y", "yes"): + import shutil + shutil.rmtree(files_dir, ignore_errors=True) + elif env_var.lower() in ("0", "f", "false", "n", "no"): + reincarnate_files(files_dir) + + +def reincarnate_files(files_dir): + target, zip = prepare_zipfile() + names = (_ for _ in zip.zfile.namelist() if _.endswith(".py")) + try: + # First check mkdir to get an error when we cannot write. + files_dir.mkdir(exist_ok=True) + except os.error as e: + print(f"SBK_EMBED=False: Warning: Cannot write into {files_dir}") + return None try: - with ensure_shibokensupport(target, support_path): - from shibokensupport.signature import loader + # Then check for a real error when unpacking the zip file. + zip.zfile.extractall(path=files_dir, members=names) + return files_dir except Exception as e: - print('Exception:', e) - traceback.print_exc(file=sys.stdout) - - return loader - + print(f"{e.__class__.__name__}: {e}", file=sys.stderr) + traceback.print_exc() + raise # New functionality: Loading from a zip archive. # There exists the zip importer, but as it is written, only real zip files are @@ -159,10 +159,6 @@ def prepare_zipfile(): The sys.path way does not work, instead we need to use sys.meta_path . See https://docs.python.org/3/library/sys.html#sys.meta_path """ - import base64 - import io - import sys - import zipfile # 'zipstring_sequence' comes from signature.cpp zipbytes = base64.b64decode(''.join(zipstring_sequence)) @@ -181,31 +177,28 @@ class EmbeddableZipImporter(object): return None self.zfile = zip_file - self._path2mod = {_.filename : p2m(_.filename) for _ in zip_file.filelist} - self._mod2path = {_[1] : _[0] for _ in self._path2mod.items()} + self._mod2path = {p2m(_.filename) : _.filename for _ in zip_file.filelist} - def find_module(self, fullname, path): - return self if self._mod2path.get(fullname) else None + def find_spec(self, fullname, path, target=None): + path = self._mod2path.get(fullname) + return ModuleSpec(fullname, self) if path else None - def load_module(self, fullname): - import importlib - import sys + def create_module(self, spec): + return None - filename = self._mod2path.get(fullname) - if filename not in self._path2mod: - raise ImportError(fullname) - module_spec = importlib.machinery.ModuleSpec(fullname, None) - new_module = importlib.util.module_from_spec(module_spec) + def exec_module(self, module): + fullname = module.__spec__.name + filename = self._mod2path[fullname] with self.zfile.open(filename, "r") as f: # "rb" not for zipfile - exec(f.read(), new_module.__dict__) - new_module.__file__ = filename - new_module.__loader__ = self + codeob = compile(f.read(), filename, "exec") + exec(codeob, module.__dict__) + module.__file__ = filename + module.__loader__ = self if filename.endswith("/__init__.py"): - new_module.__path__ = [] - new_module.__package__ = fullname + module.__path__ = [] + module.__package__ = fullname else: - new_module.__package__ = fullname.rpartition('.')[0] - sys.modules[fullname] = new_module - return new_module + module.__package__ = fullname.rpartition('.')[0] + sys.modules[fullname] = module # eof |