@@ -121,33 +121,12 @@ def __init__(self, namespace):
121
121
# Windows, or with Qt and PyQt5 installed on linux using native package manager), and in those, the Qt
122
122
# PrefixPath does not reflect the required relative target path for the frozen application.
123
123
if namespace == 'PyQt5' :
124
- # PyQt5 uses PyQt5/Qt on all platforms, or PyQt5/Qt5 from version 5.15.4 on
125
- try :
126
- # The call below might fail with AttributeError on some PyQt5 versions (e.g., 5.9.2 from conda's main
127
- # channel); missing dist information forces a fallback codepath that tries to check for __version__
128
- # attribute that does not exist, either. So handle the error gracefully and assume old layout.
129
- new_layout = hooks .is_module_satisfies ("PyQt5 >= 5.15.4" )
130
- except AttributeError :
131
- new_layout = False
132
- if new_layout :
124
+ if self ._use_new_layout ("PyQt5" , "5.15.4" , False ):
133
125
self .qt_rel_dir = os .path .join ('PyQt5' , 'Qt5' )
134
126
else :
135
127
self .qt_rel_dir = os .path .join ('PyQt5' , 'Qt' )
136
128
elif namespace == 'PyQt6' :
137
- # Similarly to PyQt5, PyQt6 switched from PyQt6/Qt to PyQt6/Qt6 in 6.0.3
138
- try :
139
- # The call below might fail with AttributeError in case of a partial PyQt6 installation. For example,
140
- # user installs PyQt6 via pip, which also installs PyQt6-Qt6 and PyQt6-sip. Then they naively uninstall
141
- # PyQt6 package, which leaves the other two behind. PyQt6 now becomes a namespace package and there is
142
- # no dist metadata, so a fallback codepath in is_module_satisfies tries to check for __version__
143
- # attribute that does not exist, either. Handle such errors gracefully and assume new layout (with
144
- # PyQt6, the new layout is more likely); it does not really matter what layout we assume, as library is
145
- # not usable anyway, but we do need to be able to return an instance of QtLibraryInfo with "version"
146
- # attribute set to a falsey value.
147
- new_layout = hooks .is_module_satisfies ("PyQt6 >= 6.0.3" )
148
- except AttributeError :
149
- new_layout = True
150
- if new_layout :
129
+ if self ._use_new_layout ("PyQt6" , "6.0.3" , True ):
151
130
self .qt_rel_dir = os .path .join ('PyQt6' , 'Qt6' )
152
131
else :
153
132
self .qt_rel_dir = os .path .join ('PyQt6' , 'Qt' )
@@ -181,6 +160,31 @@ def __getattr__(self, name):
181
160
# ... and return the requested attribute
182
161
return getattr (self , name )
183
162
163
+ # Check whether we must use the new layout (e.g. PyQt5/Qt5, PyQt6/Qt6) instead of the old layout (PyQt5/Qt,
164
+ # PyQt6/Qt).
165
+ @staticmethod
166
+ def _use_new_layout (package_basename : str , version : str , fallback_value : bool ) -> bool :
167
+ # The call to is_module_satisfies might fail with AttributeError in case of a partial installation, or when dist
168
+ # information is missing, in which case a fallback codepath tries to check for a __version__ attribute that does
169
+ # not exist.
170
+ #
171
+ # This may happen for the following (not exhaustive) reasons:
172
+ #
173
+ # - PyQt 5.9.2 installed from conda's main channel.
174
+ # - User installs PyQt6 via pip, which also installs PyQt6-Qt6 and PyQt6-sip. Then they naively uninstall PyQt6
175
+ # package, which leaves the other two behind. PyQt6 now becomes a namespace package and there is no dist
176
+ # metadata.
177
+ # - The PyQt5 commercial wheel is installed. It creates the PyQt5 namespace package but dist information is
178
+ # available under PyQt5_commercial. Since we first check for the non-commercial wheel, we trip the fallback
179
+ # codepath inside is_module_satisfies.
180
+ try :
181
+ return hooks .is_module_satisfies (f"{ package_basename } >= { version } " )
182
+ except AttributeError :
183
+ try :
184
+ return hooks .is_module_satisfies (f"{ package_basename } _commercial >= { version } " )
185
+ except AttributeError :
186
+ return fallback_value
187
+
184
188
# Load Qt information (called on first access to related fields)
185
189
def _load_qt_info (self ):
186
190
"""
0 commit comments