@@ -160,22 +160,44 @@ def check_version_conflicts(
160
160
Returns set of conflicting packages.
161
161
"""
162
162
conflicts = set ()
163
- try :
164
- new_version_obj = Version (new_version )
165
- except InvalidVersion :
166
- new_version_obj = SpecifierSet (new_version )
163
+
164
+ # Handle various wildcard patterns
165
+ if new_version == "*" :
166
+ # Full wildcard - matches any version
167
+ # We'll use a very permissive specifier
168
+ new_version_obj = SpecifierSet (">=0.0.0" )
169
+ elif new_version .endswith (".*" ):
170
+ # Major version wildcard like '2.*'
171
+ try :
172
+ major = int (new_version [:- 2 ])
173
+ new_version_obj = SpecifierSet (f">={ major } ,<{ major + 1 } " )
174
+ except (ValueError , TypeError ):
175
+ # If we can't parse the major version, use a permissive specifier
176
+ new_version_obj = SpecifierSet (">=0.0.0" )
177
+ else :
178
+ try :
179
+ new_version_obj = Version (new_version )
180
+ except InvalidVersion :
181
+ try :
182
+ # Try to parse as a specifier set
183
+ new_version_obj = SpecifierSet (new_version )
184
+ except Exception : # noqa: PERF203
185
+ # If we can't parse the version at all, return no conflicts
186
+ # This allows the installation to proceed and let pip handle it
187
+ return conflicts
167
188
168
189
for dependent , req_version in reverse_deps .get (package_name , set ()):
169
190
if req_version == "Any" :
170
191
continue
171
192
172
- try :
173
- specifier_set = SpecifierSet (req_version )
193
+ specifier_set = SpecifierSet (req_version )
194
+ # For Version objects, we check if the specifier contains the version
195
+ # For SpecifierSet objects, we need to check compatibility differently
196
+ if isinstance (new_version_obj , Version ):
174
197
if not specifier_set .contains (new_version_obj ):
175
198
conflicts .add (dependent )
176
- except Exception : # noqa: PERF203
177
- # If we can't parse the version requirement, assume it's a conflict
178
- conflicts .add (dependent )
199
+ # Otherwise this is a complex case where we have a specifier vs specifier ...
200
+ # We'll let the resolver figure those out
179
201
180
202
return conflicts
181
203
@@ -296,15 +318,21 @@ def _detect_conflicts(package_args, reverse_deps, lockfile):
296
318
"""Detect version conflicts in package arguments."""
297
319
conflicts_found = False
298
320
for package in package_args :
321
+ # Handle both == and = version specifiers
299
322
if "==" in package :
300
- name , version = package .split ("==" )
301
- conflicts = check_version_conflicts (name , version , reverse_deps , lockfile )
302
- if conflicts :
303
- conflicts_found = True
304
- err .print (
305
- f"[red bold]Error[/red bold]: Updating [bold]{ name } [/bold] "
306
- f"to version { version } would create conflicts with: { ', ' .join (sorted (conflicts ))} "
307
- )
323
+ name , version = package .split ("==" , 1 ) # Split only on the first occurrence
324
+ elif "=" in package and not package .startswith ("-e" ): # Avoid matching -e flag
325
+ name , version = package .split ("=" , 1 ) # Split only on the first occurrence
326
+ else :
327
+ continue # Skip packages without version specifiers
328
+
329
+ conflicts = check_version_conflicts (name , version , reverse_deps , lockfile )
330
+ if conflicts :
331
+ conflicts_found = True
332
+ err .print (
333
+ f"[red bold]Error[/red bold]: Updating [bold]{ name } [/bold] "
334
+ f"to version { version } would create conflicts with: { ', ' .join (sorted (conflicts ))} "
335
+ )
308
336
309
337
return conflicts_found
310
338
0 commit comments