Skip to content

Commit 8e199eb

Browse files
virtualdhtgoebel
authored andcommitted
Import pywin32 in compat and only use it from there.
This eliminates the "early pywin32 import" warning/error. - This allows using pywin32ctypes without confusing import requirements - Removes code that assumes win32api might not be present (which can never be true now anyways) See c901c42 and 3e14224.
1 parent 007f78f commit 8e199eb

File tree

7 files changed

+59
-55
lines changed

7 files changed

+59
-55
lines changed

PyInstaller/__init__.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@
3838
# Do conversion to ShortPathName really only in case HOMEPATH is not
3939
# ascii only - conversion to unicode type cause this unicode error.
4040
try:
41-
import win32api
42-
HOMEPATH = win32api.GetShortPathName(HOMEPATH)
41+
HOMEPATH = compat.win32api.GetShortPathName(HOMEPATH)
4342
except ImportError:
4443
pass
4544

PyInstaller/compat.py

+25-29
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,30 @@
198198
modname_tkinter = 'tkinter'
199199

200200

201+
# On Windows we require pypiwin32 or pywin32-ctypes
202+
# -> all pyinstaller modules should use win32api from PyInstaller.compat to
203+
# ensure that it can work on MSYS2 (which requires pywin32-ctypes)
204+
if is_win:
205+
try:
206+
from PyInstaller.utils.win32 import winutils
207+
try:
208+
pywintypes = winutils.import_pywin32_module('pywintypes', _is_venv=is_venv)
209+
win32api = winutils.import_pywin32_module('win32api', _is_venv=is_venv)
210+
except ImportError:
211+
try:
212+
from win32ctypes.pywin32 import pywintypes
213+
from win32ctypes.pywin32 import win32api
214+
except ImportError:
215+
raise
216+
except ImportError:
217+
# This environment variable is set by seutp.py
218+
# - It's not an error for pywin32 to not be installed at that point
219+
if not os.environ.get('PYINSTALLER_NO_PYWIN32_FAILURE'):
220+
raise SystemExit('PyInstaller cannot check for assembly dependencies.\n'
221+
'Please install PyWin32 or pywin32-ctypes.\n\n'
222+
'pip install pypiwin32\n')
223+
224+
201225
def architecture():
202226
"""
203227
Returns the bit depth of the python interpreter's architecture as
@@ -578,7 +602,6 @@ def getcwd():
578602
# Do conversion to ShortPathName really only in case 'cwd' is not
579603
# ascii only - conversion to unicode type cause this unicode error.
580604
try:
581-
import win32api
582605
cwd = win32api.GetShortPathName(cwd)
583606
except ImportError:
584607
pass
@@ -833,37 +856,10 @@ class FileNotFoundError(OSError):
833856

834857
def check_requirements():
835858
"""
836-
Verify that all requirements to run PyInstaller are met. Especially
837-
PyWin32 is installed on Windows.
859+
Verify that all requirements to run PyInstaller are met.
838860
839861
Fail hard if any requirement is not met.
840862
"""
841863
# Fail hard if Python does not have minimum required version
842864
if sys.version_info < (3, 3) and sys.version_info[:2] != (2, 7):
843865
raise SystemExit('PyInstaller requires at least Python 2.7 or 3.3+.')
844-
845-
if is_win:
846-
if 'win32api' in sys.modules or 'pywintypes' in sys.modules:
847-
# Users should never see this error; if it occurs, it means someone
848-
# wasn't careful and added an import where it shouldn't be
849-
# Unfortunately this error is triggered when running under pytest
850-
# since all PyInstaller runs are done in the same process
851-
logger.warning("Internal error: early pywin32 import was introduced")
852-
return
853-
854-
try:
855-
from PyInstaller.utils.win32 import winutils
856-
try:
857-
pywintypes = winutils.import_pywin32_module('pywintypes')
858-
except ImportError:
859-
from win32ctypes.pywin32 import pywintypes
860-
from win32ctypes.pywin32 import win32api
861-
862-
# if this succeeded, then install pywin32-ctypes into sys.modules
863-
sys.modules['win32api'] = win32api
864-
sys.modules['pywintypes'] = pywintypes
865-
866-
except ImportError:
867-
raise SystemExit('PyInstaller cannot check for assembly dependencies.\n'
868-
'Please install PyWin32 or pywin32-ctypes.\n\n'
869-
'pip install pypiwin32\n')

PyInstaller/utils/win32/icon.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
except AttributeError:
2222
StringTypes = [ type("") ]
2323

24+
from ...compat import win32api
25+
2426
import PyInstaller.log as logging
2527
logger = logging.getLogger(__name__)
2628

@@ -112,7 +114,6 @@ def grp_icondir_entries(self, id=1):
112114

113115

114116
def CopyIcons_FromIco(dstpath, srcpath, id=1):
115-
import win32api #, win32con
116117
icons = map(IconFile, srcpath)
117118
logger.info("Updating icons from %s to %s", srcpath, dstpath)
118119

@@ -167,7 +168,7 @@ def splitter(s):
167168
logger.info("Updating icons from %s, %d to %s", srcpath, index, dstpath)
168169
else:
169170
logger.info("Updating icons from %s to %s", srcpath, dstpath)
170-
import win32api #, win32con
171+
171172
hdst = win32api.BeginUpdateResource(dstpath, 0)
172173
hsrc = win32api.LoadLibraryEx(srcpath, 0, LOAD_LIBRARY_AS_DATAFILE)
173174
if index is None:

PyInstaller/utils/win32/versioninfo.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
import codecs
1313
import struct
1414

15-
from ...compat import is_py3
15+
from ...compat import is_py3, win32api
1616

17-
import win32api
1817
# ::TODO:: #1920 revert to using pypi version
1918
import pefile
2019

PyInstaller/utils/win32/winresource.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@
1616
Updates or adds resources from file <srcpath> in file <dstpath>.
1717
"""
1818

19-
20-
import pywintypes
21-
import win32api
22-
19+
from ...compat import pywintypes, win32api
2320

2421
import PyInstaller.log as logging
2522
logger = logging.getLogger(__name__)

PyInstaller/utils/win32/winutils.py

+25-16
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
import os
1818
import sys
1919

20-
from PyInstaller import compat
21-
from PyInstaller.compat import is_py3
20+
# Do not import 'compat' globally to avoid circual import:
21+
# import_pywin32_module() is used by compat
22+
#from ... import compat
2223

2324
import PyInstaller.log as logging
2425
logger = logging.getLogger(__name__)
@@ -28,12 +29,9 @@ def get_windows_dir():
2829
"""
2930
Return the Windows directory e.g. C:\\Windows.
3031
"""
31-
try:
32-
import win32api
33-
except ImportError:
34-
windir = compat.getenv('SystemRoot', compat.getenv('WINDIR'))
35-
else:
36-
windir = win32api.GetWindowsDirectory()
32+
# imported here to avoid circular import
33+
from ... import compat
34+
windir = compat.win32api.GetWindowsDirectory()
3735
if not windir:
3836
raise SystemExit("Error: Can not determine your Windows directory")
3937
return windir
@@ -43,12 +41,10 @@ def get_system_path():
4341
"""
4442
Return the path that Windows will search for dlls.
4543
"""
44+
# imported here to avoid circular import
45+
from ... import compat
4646
_bpath = []
47-
try:
48-
import win32api
49-
sys_dir = win32api.GetSystemDirectory()
50-
except ImportError:
51-
sys_dir = os.path.normpath(os.path.join(get_windows_dir(), 'system32'))
47+
sys_dir = compat.win32api.GetSystemDirectory()
5248
# Ensure C:\Windows\system32 and C:\Windows directories are
5349
# always present in PATH variable.
5450
# C:\Windows\system32 is valid even for 64bit Windows. Access do DLLs are
@@ -65,13 +61,15 @@ def extend_system_path(paths):
6561
6662
Some hooks might extend PATH where PyInstaller should look for dlls.
6763
"""
64+
# imported here to avoid circular import
65+
from ... import compat
6866
old_PATH = compat.getenv('PATH', '')
6967
paths.append(old_PATH)
7068
new_PATH = os.pathsep.join(paths)
7169
compat.setenv('PATH', new_PATH)
7270

7371

74-
def import_pywin32_module(module_name):
72+
def import_pywin32_module(module_name, _is_venv=None):
7573
"""
7674
Import and return the PyWin32 module with the passed name.
7775
@@ -87,6 +85,11 @@ def import_pywin32_module(module_name):
8785
----------
8886
module_name : str
8987
Fully-qualified name of this module.
88+
_is_venv: bool
89+
Internal paramter used by compat.py, to prevent circular import. If None
90+
(the default), compat is imported and comapt.is_venv ist used. If not
91+
None, it is assumed to be called from compat and the value to be the same
92+
as compat.is_venv.
9093
9194
Returns
9295
----------
@@ -112,9 +115,13 @@ def import_pywin32_module(module_name):
112115
# an ugly hack, but there is no other way.
113116
sys.frozen = '|_|GLYH@CK'
114117

118+
if _is_venv is None: # not called from within compat
119+
# imported here to avoid circular import
120+
from ... import compat
121+
_is_venv = compat.is_venv
115122
# If isolated to a venv, the preferred site.getsitepackages()
116123
# function is unreliable. Fallback to searching "sys.path" instead.
117-
if compat.is_venv:
124+
if _is_venv:
118125
sys_paths = sys.path
119126
else:
120127
import site
@@ -154,7 +161,9 @@ def convert_dll_name_to_str(dll_name):
154161
:param dll_name:
155162
:return:
156163
"""
164+
# imported here to avoid circular import
165+
from ...compat import is_py3
157166
if is_py3 and isinstance(dll_name, bytes):
158167
return str(dll_name, encoding='UTF-8')
159168
else:
160-
return dll_name
169+
return dll_name

setup.py

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
import codecs
1414
import sys, os
1515
from setuptools import setup
16+
17+
# Hack required to allow compat to not fail when pypiwin32 isn't found
18+
os.environ["PYINSTALLER_NO_PYWIN32_FAILURE"] = "1"
1619
from PyInstaller import (__version__ as version, is_linux, is_win, is_cygwin,
1720
HOMEPATH, PLATFORM, compat)
1821

0 commit comments

Comments
 (0)