From 88e82e6cebeb04e79df9e2ecd5b4d827af6a9533 Mon Sep 17 00:00:00 2001 From: Rok Mandeljc Date: Mon, 24 Jul 2023 12:38:52 +0200 Subject: [PATCH] winutils: remove import_pywin32_module helper There is little point in trying to work around broken pywin32 installations on our side; if pythoncom/pywintypes cannot be imported in the given python environment as-is, the user cannot run and test their unfrozen program, either. Remove the `PyInstaller.utils.hooks.win32` module and move its only function, `get_pywin32_module_file_attribute` into `PyInstaller.utils.hooks`. The two hooks in contributed hooks repository that make use of this function are importing it from this package, anyway. Reduce the implementation into simple `get_module_attribute(name, '__file__')` call. --- PyInstaller/utils/hooks/__init__.py | 21 +++++++- PyInstaller/utils/hooks/win32.py | 46 ----------------- PyInstaller/utils/win32/winutils.py | 76 ----------------------------- 3 files changed, 19 insertions(+), 124 deletions(-) delete mode 100644 PyInstaller/utils/hooks/win32.py diff --git a/PyInstaller/utils/hooks/__init__.py b/PyInstaller/utils/hooks/__init__.py index 654af1f7c84..2af394547bf 100644 --- a/PyInstaller/utils/hooks/__init__.py +++ b/PyInstaller/utils/hooks/__init__.py @@ -26,8 +26,6 @@ from PyInstaller import log as logging from PyInstaller.depend.imphookapi import PostGraphAPI from PyInstaller.exceptions import ExecCommandFailed -from PyInstaller.utils.hooks.win32 import \ - get_pywin32_module_file_attribute # noqa: F401 from PyInstaller import isolated logger = logging.getLogger(__name__) @@ -348,6 +346,25 @@ def _get_module_file_attribute(package): return filename +def get_pywin32_module_file_attribute(module_name): + """ + Get the absolute path of the PyWin32 DLL specific to the PyWin32 module with the passed name (`pythoncom` + or `pywintypes`). + + On import, each PyWin32 module: + + * Imports a DLL specific to that module. + * Overwrites the values of all module attributes with values specific to that DLL. This includes that module's + `__file__` attribute, which then provides the absolute path of that DLL. + + This function imports the module in isolated subprocess and retrieves its `__file__` attribute. + """ + + # NOTE: we cannot use `get_module_file_attribute` as it does not account for the __file__ rewriting magic + # done by the module. Use `get_module_attribute` instead. + return get_module_attribute(module_name, '__file__') + + def is_module_satisfies( requirements: list | pkg_resources.Requirement, version: str | pkg_resources.Distribution | None = None, diff --git a/PyInstaller/utils/hooks/win32.py b/PyInstaller/utils/hooks/win32.py deleted file mode 100644 index 50b013d31dd..00000000000 --- a/PyInstaller/utils/hooks/win32.py +++ /dev/null @@ -1,46 +0,0 @@ -# ---------------------------------------------------------------------------- -# Copyright (c) 2005-2023, PyInstaller Development Team. -# -# Distributed under the terms of the GNU General Public License (version 2 -# or later) with exception for distributing the bootloader. -# -# The full license is in the file COPYING.txt, distributed with this software. -# -# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception) -# ---------------------------------------------------------------------------- - -from PyInstaller import isolated - - -@isolated.decorate -def get_pywin32_module_file_attribute(module_name): - """ - Get the absolute path of the PyWin32 DLL specific to the PyWin32 module with the passed name. - - On import, each PyWin32 module: - - * Imports a DLL specific to that module. - * Overwrites the values of all module attributes with values specific to that DLL. This includes that module's - `__file__` attribute, which then provides the absolute path of that DLL. - - This function safely imports that module in a PyWin32-aware subprocess and returns the value of that module's - `__file__` attribute. - - Parameters - ---------- - module_name : str - Fully-qualified name of that module. - - Returns - ---------- - str - Absolute path of that DLL. - - See Also - ---------- - `PyInstaller.utils.win32.winutils.import_pywin32_module()` - For further details. - """ - from PyInstaller.utils.win32 import winutils - module = winutils.import_pywin32_module(module_name) - return module.__file__ diff --git a/PyInstaller/utils/win32/winutils.py b/PyInstaller/utils/win32/winutils.py index 3d71039cd77..d8716c31672 100644 --- a/PyInstaller/utils/win32/winutils.py +++ b/PyInstaller/utils/win32/winutils.py @@ -12,14 +12,8 @@ Utilities for Windows platform. """ -import os -import sys - -import PyInstaller.log as logging from PyInstaller import compat -logger = logging.getLogger(__name__) - def get_windows_dir(): """ @@ -43,76 +37,6 @@ def get_system_path(): return [sys_dir, get_windows_dir()] -def import_pywin32_module(module_name): - """ - Import and return the PyWin32 module with the passed name. - - When imported, the `pywintypes` and `pythoncom` modules both internally import dynamic libraries - (e.g., `pywintypes.py` imports `pywintypes34.dll` under Python 3.4). The Anaconda Python distribution for Windows - installs these libraries to non-standard directories, resulting in - `"ImportError: No system module 'pywintypes' (pywintypes34.dll)"` - exceptions. This function catches these exceptions, searches for these libraries, adds their directories to - `sys.path`, and retries. - - Parameters - ---------- - module_name : str - Fully-qualified name of this module. - - Returns - ---------- - types.ModuleType - The desired module. - """ - module = None - - try: - module = __import__(module_name, globals={}, locals={}, fromlist=['']) - except ImportError as exc: - if str(exc).startswith('No system module'): - # True if "sys.frozen" is currently set. - is_sys_frozen = hasattr(sys, 'frozen') - - # Current value of "sys.frozen" if any. - sys_frozen = getattr(sys, 'frozen', None) - - # Force PyWin32 to search "sys.path" for DLLs. By default, PyWin32 only searches "site-packages\win32\lib", - # "sys.prefix", and Windows system directories (e.g., "C:\Windows\System32"). This is an ugly hack, but - # there is no other way. - sys.frozen = '|_|GLYH@CK' - - # If isolated to a venv, the preferred site.getsitepackages() function is unreliable. Fall back to searching - # "sys.path" instead. - if compat.is_venv: - sys_paths = sys.path - else: - sys_paths = compat.getsitepackages() - - for sys_path in sys_paths: - # Absolute path of the directory containing PyWin32 DLLs. - pywin32_dll_dir = os.path.join(sys_path, 'pywin32_system32') - if os.path.isdir(pywin32_dll_dir): - sys.path.append(pywin32_dll_dir) - try: - module = __import__(name=module_name, globals={}, locals={}, fromlist=['']) - break - except ImportError: - pass - - # If "sys.frozen" was previously set, restore its prior value. - if is_sys_frozen: - sys.frozen = sys_frozen - # Else, undo this hack entirely. - else: - del sys.frozen - - # If this module remains unimportable, PyWin32 is not installed. Fail. - if module is None: - raise - - return module - - def get_pe_file_machine_type(filename): """ Return the machine type code from the header of the given PE file.