dependabot-python 0.215.0 → 0.216.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ module Dependabot
4
4
  module Python
5
5
  module PythonVersions
6
6
  PRE_INSTALLED_PYTHON_VERSIONS = %w(
7
- 3.11.0
7
+ 3.11.3
8
8
  ).freeze
9
9
 
10
10
  # Due to an OpenSSL issue we can only install the following versions in
@@ -13,11 +13,11 @@ module Dependabot
13
13
  #
14
14
  # WARNING: 3.9.3 is purposefully omitted as it was recalled: https://www.python.org/downloads/release/python-393/
15
15
  SUPPORTED_VERSIONS = %w(
16
- 3.11.0
17
- 3.10.8 3.10.7 3.10.6 3.10.5 3.10.4 3.10.3 3.10.2 3.10.1 3.10.0
18
- 3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 3.9.10 3.9.9 3.9.8 3.9.7 3.9.6 3.9.5 3.9.4 3.9.2 3.9.1 3.9.0
19
- 3.8.15 3.8.14 3.8.13 3.8.12 3.8.11 3.8.10 3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 3.8.2 3.8.1 3.8.0
20
- 3.7.15 3.7.14 3.7.13 3.7.12 3.7.11 3.7.10 3.7.9 3.7.8 3.7.7 3.7.6 3.7.5 3.7.4 3.7.3 3.7.2 3.7.1 3.7.0
16
+ 3.11.3 3.11.2 3.11.1 3.11.0
17
+ 3.10.11 3.10.10 3.10.9 3.10.8 3.10.7 3.10.6 3.10.5 3.10.4 3.10.3 3.10.2 3.10.1 3.10.0
18
+ 3.9.16 3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 3.9.10 3.9.9 3.9.8 3.9.7 3.9.6 3.9.5 3.9.4 3.9.2 3.9.1 3.9.0
19
+ 3.8.16 3.8.15 3.8.14 3.8.13 3.8.12 3.8.11 3.8.10 3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 3.8.2 3.8.1 3.8.0
20
+ 3.7.16 3.7.15 3.7.14 3.7.13 3.7.12 3.7.11 3.7.10 3.7.9 3.7.8 3.7.7 3.7.6 3.7.5 3.7.4 3.7.3 3.7.2 3.7.1 3.7.0
21
21
  3.6.15 3.6.14 3.6.13 3.6.12 3.6.11 3.6.10 3.6.9 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.6.0
22
22
  3.5.10 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3
23
23
  ).freeze
@@ -9,7 +9,7 @@ module Dependabot
9
9
  VERSION = /([1-9][0-9]*!)?[0-9]+[a-zA-Z0-9\-_.*]*(\+[0-9a-zA-Z]+(\.[0-9a-zA-Z]+)*)?/
10
10
 
11
11
  REQUIREMENT = /(?<comparison>#{COMPARISON})\s*\\?\s*(?<version>#{VERSION})/
12
- HASH = /--hash=(?<algorithm>.*?):(?<hash>.*?)(?=\s|$)/
12
+ HASH = /--hash=(?<algorithm>.*?):(?<hash>.*?)(?=\s|\\|$)/
13
13
  REQUIREMENTS = /#{REQUIREMENT}(\s*,\s*\\?\s*#{REQUIREMENT})*/
14
14
  HASHES = /#{HASH}(\s*\\?\s*#{HASH})*/
15
15
  MARKER_OP = /\s*(#{COMPARISON}|(\s*in)|(\s*not\s*in))/
@@ -11,7 +11,7 @@ require "dependabot/python/file_updater/requirement_replacer"
11
11
  require "dependabot/python/file_updater/setup_file_sanitizer"
12
12
  require "dependabot/python/version"
13
13
  require "dependabot/shared_helpers"
14
- require "dependabot/python/helpers"
14
+ require "dependabot/python/language_version_manager"
15
15
  require "dependabot/python/native_helpers"
16
16
  require "dependabot/python/python_versions"
17
17
  require "dependabot/python/name_normaliser"
@@ -71,7 +71,7 @@ module Dependabot
71
71
  SharedHelpers.in_a_temporary_directory do
72
72
  SharedHelpers.with_git_configured(credentials: credentials) do
73
73
  write_temporary_dependency_files(updated_req: requirement)
74
- Helpers.install_required_python(python_version)
74
+ language_version_manager.install_required_python
75
75
 
76
76
  filenames_to_compile.each do |filename|
77
77
  # Shell out to pip-compile.
@@ -173,6 +173,10 @@ module Dependabot
173
173
  raise GitDependenciesNotReachable, url
174
174
  end
175
175
 
176
+ raise Dependabot::OutOfDisk if error.message.end_with?("[Errno 28] No space left on device")
177
+
178
+ raise Dependabot::OutOfMemory if error.message.end_with?("MemoryError")
179
+
176
180
  raise
177
181
  end
178
182
  # rubocop:enable Metrics/AbcSize
@@ -233,7 +237,7 @@ module Dependabot
233
237
  end
234
238
 
235
239
  def new_resolver_supported?
236
- python_version >= Python::Version.new("3.7")
240
+ language_version_manager.python_version >= Python::Version.new("3.7")
237
241
  end
238
242
 
239
243
  def pip_compile_options_fingerprint(options)
@@ -275,7 +279,7 @@ module Dependabot
275
279
 
276
280
  def run_pip_compile_command(command, fingerprint:)
277
281
  run_command(
278
- "pyenv local #{Helpers.python_major_minor(python_version)}",
282
+ "pyenv local #{language_version_manager.python_major_minor}",
279
283
  fingerprint: "pyenv local <python_major_minor>"
280
284
  )
281
285
 
@@ -322,7 +326,7 @@ module Dependabot
322
326
  end
323
327
 
324
328
  # Overwrite the .python-version with updated content
325
- File.write(".python-version", Helpers.python_major_minor(python_version))
329
+ File.write(".python-version", language_version_manager.python_major_minor)
326
330
 
327
331
  setup_files.each do |file|
328
332
  path = file.name
@@ -434,9 +438,9 @@ module Dependabot
434
438
  while (remaining_filenames = filenames - ordered_filenames).any?
435
439
  ordered_filenames +=
436
440
  remaining_filenames.
437
- select do |fn|
441
+ reject do |fn|
438
442
  unupdated_reqs = requirement_map[fn] - ordered_filenames
439
- (unupdated_reqs & filenames).empty?
443
+ unupdated_reqs.intersect?(filenames)
440
444
  end
441
445
  end
442
446
 
@@ -479,41 +483,6 @@ module Dependabot
479
483
  ).parse.find { |d| d.name == dependency.name }&.version
480
484
  end
481
485
 
482
- def python_version
483
- @python_version ||=
484
- user_specified_python_version ||
485
- python_version_matching_imputed_requirements ||
486
- PythonVersions::PRE_INSTALLED_PYTHON_VERSIONS.first
487
- end
488
-
489
- def user_specified_python_version
490
- return unless python_requirement_parser.user_specified_requirements.any?
491
-
492
- user_specified_requirements =
493
- python_requirement_parser.user_specified_requirements.
494
- map { |r| Python::Requirement.requirements_array(r) }
495
- python_version_matching(user_specified_requirements)
496
- end
497
-
498
- def python_version_matching_imputed_requirements
499
- compiled_file_python_requirement_markers =
500
- python_requirement_parser.imputed_requirements.map do |r|
501
- Dependabot::Python::Requirement.new(r)
502
- end
503
- python_version_matching(compiled_file_python_requirement_markers)
504
- end
505
-
506
- def python_version_matching(requirements)
507
- PythonVersions::SUPPORTED_VERSIONS_TO_ITERATE.find do |version_string|
508
- version = Python::Version.new(version_string)
509
- requirements.all? do |req|
510
- next req.any? { |r| r.satisfied_by?(version) } if req.is_a?(Array)
511
-
512
- req.satisfied_by?(version)
513
- end
514
- end
515
- end
516
-
517
486
  def python_requirement_parser
518
487
  @python_requirement_parser ||=
519
488
  FileParser::PythonRequirementParser.new(
@@ -521,8 +490,11 @@ module Dependabot
521
490
  )
522
491
  end
523
492
 
524
- def pre_installed_python?(version)
525
- PythonVersions::PRE_INSTALLED_PYTHON_VERSIONS.include?(version)
493
+ def language_version_manager
494
+ @language_version_manager ||=
495
+ LanguageVersionManager.new(
496
+ python_requirement_parser: python_requirement_parser
497
+ )
526
498
  end
527
499
 
528
500
  def setup_files
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dependabot/python/language_version_manager"
3
4
  require "dependabot/python/update_checker"
4
5
  require "dependabot/python/update_checker/latest_version_finder"
5
6
  require "dependabot/python/file_parser/python_requirement_parser"
@@ -20,17 +21,17 @@ module Dependabot
20
21
  end
21
22
 
22
23
  def latest_resolvable_version
23
- latest_version_finder.latest_version(python_version: python_version)
24
+ latest_version_finder.latest_version(python_version: language_version_manager.python_version)
24
25
  end
25
26
 
26
27
  def latest_resolvable_version_with_no_unlock
27
28
  latest_version_finder.
28
- latest_version_with_no_unlock(python_version: python_version)
29
+ latest_version_with_no_unlock(python_version: language_version_manager.python_version)
29
30
  end
30
31
 
31
32
  def lowest_resolvable_security_fix_version
32
33
  latest_version_finder.
33
- lowest_security_fix_version(python_version: python_version)
34
+ lowest_security_fix_version(python_version: language_version_manager.python_version)
34
35
  end
35
36
 
36
37
  private
@@ -49,45 +50,18 @@ module Dependabot
49
50
  )
50
51
  end
51
52
 
52
- def python_version
53
- @python_version ||=
54
- user_specified_python_version ||
55
- python_version_matching_imputed_requirements ||
56
- PythonVersions::PRE_INSTALLED_PYTHON_VERSIONS.first
57
- end
58
-
59
- def user_specified_python_version
60
- return unless python_requirement_parser.user_specified_requirements.any?
61
-
62
- user_specified_requirements =
63
- python_requirement_parser.user_specified_requirements.
64
- map { |r| Python::Requirement.requirements_array(r) }
65
- python_version_matching(user_specified_requirements)
66
- end
67
-
68
- def python_version_matching_imputed_requirements
69
- compiled_file_python_requirement_markers =
70
- python_requirement_parser.imputed_requirements.map do |r|
71
- Dependabot::Python::Requirement.new(r)
72
- end
73
- python_version_matching(compiled_file_python_requirement_markers)
74
- end
75
-
76
- def python_version_matching(requirements)
77
- PythonVersions::SUPPORTED_VERSIONS_TO_ITERATE.find do |version_string|
78
- version = Python::Version.new(version_string)
79
- requirements.all? do |req|
80
- next req.any? { |r| r.satisfied_by?(version) } if req.is_a?(Array)
81
-
82
- req.satisfied_by?(version)
83
- end
84
- end
85
- end
86
-
87
53
  def python_requirement_parser
88
54
  @python_requirement_parser ||=
89
- FileParser::PythonRequirementParser.
90
- new(dependency_files: dependency_files)
55
+ FileParser::PythonRequirementParser.new(
56
+ dependency_files: dependency_files
57
+ )
58
+ end
59
+
60
+ def language_version_manager
61
+ @language_version_manager ||=
62
+ LanguageVersionManager.new(
63
+ python_requirement_parser: python_requirement_parser
64
+ )
91
65
  end
92
66
  end
93
67
  end
@@ -175,7 +175,7 @@ module Dependabot
175
175
  end
176
176
 
177
177
  if error.message.include?("UnsupportedPythonVersion") &&
178
- user_specified_python_requirement
178
+ language_version_manager.user_specified_python_version
179
179
  check_original_requirements_resolvable
180
180
 
181
181
  # The latest version of the dependency we're updating to needs a
@@ -231,7 +231,7 @@ module Dependabot
231
231
  end
232
232
 
233
233
  if error.message.include?("UnsupportedPythonVersion") &&
234
- user_specified_python_requirement
234
+ language_version_manager.user_specified_python_version
235
235
  msg = clean_error_message(error.message).
236
236
  lines.take_while { |l| !l.start_with?("File") }.join.strip
237
237
  raise if msg.empty?
@@ -239,8 +239,10 @@ module Dependabot
239
239
  raise DependencyFileNotResolvable, msg
240
240
  end
241
241
 
242
- # NOTE: Pipenv masks the actualy error, see this issue for updates:
242
+ # NOTE: Pipenv masks the actual error, see this issue for updates:
243
243
  # https://github.com/pypa/pipenv/issues/2791
244
+ # TODO: This may no longer be reproducible on latest pipenv, see linked issue,
245
+ # so investigate when we next bump to newer pipenv...
244
246
  handle_pipenv_installation_error(error.message) if error.message.match?(PIPENV_INSTALLATION_ERROR_REGEX)
245
247
 
246
248
  # Raise an unhandled error, as this could be a problem with
@@ -290,7 +292,7 @@ module Dependabot
290
292
  end
291
293
 
292
294
  # Overwrite the .python-version with updated content
293
- File.write(".python-version", Helpers.python_major_minor(python_version))
295
+ File.write(".python-version", language_version_manager.python_major_minor)
294
296
 
295
297
  setup_files.each do |file|
296
298
  path = file.name
@@ -320,7 +322,7 @@ module Dependabot
320
322
  nil
321
323
  end
322
324
 
323
- Helpers.install_required_python(python_version)
325
+ language_version_manager.install_required_python
324
326
  end
325
327
 
326
328
  def sanitized_setup_file_content(file)
@@ -354,7 +356,7 @@ module Dependabot
354
356
  def update_python_requirement(pipfile_content)
355
357
  Python::FileUpdater::PipfilePreparer.
356
358
  new(pipfile_content: pipfile_content).
357
- update_python_requirement(Helpers.python_major_minor(python_version))
359
+ update_python_requirement(language_version_manager.python_major_minor)
358
360
  end
359
361
 
360
362
  # rubocop:disable Metrics/PerceivedComplexity
@@ -398,57 +400,6 @@ module Dependabot
398
400
  replace_sources(credentials)
399
401
  end
400
402
 
401
- def python_version
402
- @python_version ||= python_version_from_supported_versions
403
- end
404
-
405
- def python_version_from_supported_versions
406
- requirement_string =
407
- if @using_python_two then "2.7.*"
408
- elsif user_specified_python_requirement
409
- parts = user_specified_python_requirement.split(".")
410
- parts.fill("*", (parts.length)..2).join(".")
411
- else
412
- PythonVersions::PRE_INSTALLED_PYTHON_VERSIONS.first
413
- end
414
-
415
- # Ideally, the requirement is satisfied by a Python version we support
416
- requirement =
417
- Python::Requirement.requirements_array(requirement_string).first
418
- version =
419
- PythonVersions::SUPPORTED_VERSIONS_TO_ITERATE.
420
- find { |v| requirement.satisfied_by?(Python::Version.new(v)) }
421
- return version if version
422
-
423
- # If not, and changing the patch version would fix things, we do that
424
- # as the patch version is unlikely to affect resolution
425
- requirement =
426
- Python::Requirement.new(requirement_string.gsub(/\.\d+$/, ".*"))
427
- version =
428
- PythonVersions::SUPPORTED_VERSIONS_TO_ITERATE.
429
- find { |v| requirement.satisfied_by?(Python::Version.new(v)) }
430
- return version if version
431
-
432
- # Otherwise we have to raise, giving details of the Python versions
433
- # that Dependabot supports
434
- msg = "Dependabot detected the following Python requirement " \
435
- "for your project: '#{requirement_string}'.\n\nCurrently, the " \
436
- "following Python versions are supported in Dependabot: " \
437
- "#{PythonVersions::SUPPORTED_VERSIONS.join(', ')}."
438
- raise DependencyFileNotResolvable, msg
439
- end
440
-
441
- def user_specified_python_requirement
442
- python_requirement_parser.user_specified_requirements.first
443
- end
444
-
445
- def python_requirement_parser
446
- @python_requirement_parser ||=
447
- FileParser::PythonRequirementParser.new(
448
- dependency_files: dependency_files
449
- )
450
- end
451
-
452
403
  def run_command(command, env: {})
453
404
  start = Time.now
454
405
  command = SharedHelpers.escape_command(command)
@@ -468,7 +419,7 @@ module Dependabot
468
419
  end
469
420
 
470
421
  def run_pipenv_command(command, env: pipenv_env_variables)
471
- run_command("pyenv local #{Helpers.python_major_minor(python_version)}")
422
+ run_command("pyenv local #{language_version_manager.python_major_minor}")
472
423
  run_command(command, env: env)
473
424
  end
474
425
 
@@ -486,6 +437,20 @@ module Dependabot
486
437
  NameNormaliser.normalise(name)
487
438
  end
488
439
 
440
+ def python_requirement_parser
441
+ @python_requirement_parser ||=
442
+ FileParser::PythonRequirementParser.new(
443
+ dependency_files: dependency_files
444
+ )
445
+ end
446
+
447
+ def language_version_manager
448
+ @language_version_manager ||=
449
+ LanguageVersionManager.new(
450
+ python_requirement_parser: python_requirement_parser
451
+ )
452
+ end
453
+
489
454
  def pipfile
490
455
  dependency_files.find { |f| f.name == "Pipfile" }
491
456
  end
@@ -92,10 +92,10 @@ module Dependabot
92
92
  write_temporary_dependency_files(updated_req: requirement)
93
93
  add_auth_env_vars
94
94
 
95
- Helpers.install_required_python(python_version)
95
+ language_version_manager.install_required_python
96
96
 
97
97
  # use system git instead of the pure Python dulwich
98
- unless python_version&.start_with?("3.6")
98
+ unless language_version_manager.python_version&.start_with?("3.6")
99
99
  run_poetry_command("pyenv exec poetry config experimental.system-git-client true")
100
100
  end
101
101
 
@@ -205,7 +205,7 @@ module Dependabot
205
205
  end
206
206
 
207
207
  # Overwrite the .python-version with updated content
208
- File.write(".python-version", Helpers.python_major_minor(python_version)) if python_version
208
+ File.write(".python-version", language_version_manager.python_major_minor)
209
209
 
210
210
  # Overwrite the pyproject with updated content
211
211
  if update_pyproject
@@ -224,39 +224,10 @@ module Dependabot
224
224
  add_auth_env_vars(credentials)
225
225
  end
226
226
 
227
- def python_version
228
- requirements = python_requirement_parser.user_specified_requirements
229
- requirements = requirements.
230
- map { |r| Python::Requirement.requirements_array(r) }
231
-
232
- version = PythonVersions::SUPPORTED_VERSIONS_TO_ITERATE.find do |v|
233
- requirements.all? do |reqs|
234
- reqs.any? { |r| r.satisfied_by?(Python::Version.new(v)) }
235
- end
236
- end
237
- return version if version
238
-
239
- msg = "Dependabot detected the following Python requirements " \
240
- "for your project: '#{requirements}'.\n\nCurrently, the " \
241
- "following Python versions are supported in Dependabot: " \
242
- "#{PythonVersions::SUPPORTED_VERSIONS.join(', ')}."
243
- raise DependencyFileNotResolvable, msg
244
- end
245
-
246
- def python_requirement_parser
247
- @python_requirement_parser ||=
248
- FileParser::PythonRequirementParser.new(
249
- dependency_files: dependency_files
250
- )
251
- end
252
-
253
- def pre_installed_python?(version)
254
- PythonVersions::PRE_INSTALLED_PYTHON_VERSIONS.include?(version)
255
- end
256
-
257
227
  def updated_pyproject_content(updated_requirement:)
258
228
  content = pyproject.content
259
229
  content = sanitize_pyproject_content(content)
230
+ content = update_python_requirement(content)
260
231
  content = freeze_other_dependencies(content)
261
232
  content = set_target_dependency_req(content, updated_requirement)
262
233
  content
@@ -265,6 +236,7 @@ module Dependabot
265
236
  def sanitized_pyproject_content
266
237
  content = pyproject.content
267
238
  content = sanitize_pyproject_content(content)
239
+ content = update_python_requirement(content)
268
240
  content
269
241
  end
270
242
 
@@ -274,13 +246,18 @@ module Dependabot
274
246
  sanitize
275
247
  end
276
248
 
249
+ def update_python_requirement(pyproject_content)
250
+ Python::FileUpdater::PyprojectPreparer.
251
+ new(pyproject_content: pyproject_content).
252
+ update_python_requirement(language_version_manager.python_major_minor)
253
+ end
254
+
277
255
  def freeze_other_dependencies(pyproject_content)
278
256
  Python::FileUpdater::PyprojectPreparer.
279
257
  new(pyproject_content: pyproject_content, lockfile: lockfile).
280
258
  freeze_top_level_dependencies_except([dependency])
281
259
  end
282
260
 
283
- # rubocop:disable Metrics/PerceivedComplexity
284
261
  def set_target_dependency_req(pyproject_content, updated_requirement)
285
262
  return pyproject_content unless updated_requirement
286
263
 
@@ -288,15 +265,15 @@ module Dependabot
288
265
  poetry_object = pyproject_object.dig("tool", "poetry")
289
266
 
290
267
  Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |type|
291
- names = poetry_object[type]&.keys || []
292
- pkg_name = names.find { |nm| normalise(nm) == dependency.name }
293
- next unless pkg_name
294
-
295
- if poetry_object.dig(type, pkg_name).is_a?(Hash)
296
- poetry_object[type][pkg_name]["version"] = updated_requirement
297
- else
298
- poetry_object[type][pkg_name] = updated_requirement
299
- end
268
+ dependencies = poetry_object[type]
269
+ next unless dependencies
270
+
271
+ update_dependency_requirement(dependencies, updated_requirement)
272
+ end
273
+
274
+ groups = poetry_object["group"]&.values || []
275
+ groups.each do |group_spec|
276
+ update_dependency_requirement(group_spec["dependencies"], updated_requirement)
300
277
  end
301
278
 
302
279
  # If this is a sub-dependency, add the new requirement
@@ -307,7 +284,18 @@ module Dependabot
307
284
 
308
285
  TomlRB.dump(pyproject_object)
309
286
  end
310
- # rubocop:enable Metrics/PerceivedComplexity
287
+
288
+ def update_dependency_requirement(toml_node, requirement)
289
+ names = toml_node.keys
290
+ pkg_name = names.find { |nm| normalise(nm) == dependency.name }
291
+ return unless pkg_name
292
+
293
+ if toml_node[pkg_name].is_a?(Hash)
294
+ toml_node[pkg_name]["version"] = requirement
295
+ else
296
+ toml_node[pkg_name] = requirement
297
+ end
298
+ end
311
299
 
312
300
  def subdep_type
313
301
  category =
@@ -318,6 +306,20 @@ module Dependabot
318
306
  category == "dev" ? "dev-dependencies" : "dependencies"
319
307
  end
320
308
 
309
+ def python_requirement_parser
310
+ @python_requirement_parser ||=
311
+ FileParser::PythonRequirementParser.new(
312
+ dependency_files: dependency_files
313
+ )
314
+ end
315
+
316
+ def language_version_manager
317
+ @language_version_manager ||=
318
+ LanguageVersionManager.new(
319
+ python_requirement_parser: python_requirement_parser
320
+ )
321
+ end
322
+
321
323
  def pyproject
322
324
  dependency_files.find { |f| f.name == "pyproject.toml" }
323
325
  end
@@ -88,12 +88,19 @@ module Dependabot
88
88
  case update_strategy
89
89
  when :widen_ranges then widen_pyproject_requirement(req)
90
90
  when :bump_versions then update_pyproject_version(req)
91
+ when :bump_versions_if_necessary then update_pyproject_version_if_needed(req)
91
92
  else raise "Unexpected update strategy: #{update_strategy}"
92
93
  end
93
94
  rescue UnfixableRequirement
94
95
  req.merge(requirement: :unfixable)
95
96
  end
96
97
 
98
+ def update_pyproject_version_if_needed(req)
99
+ return req if new_version_satisfies?(req)
100
+
101
+ update_pyproject_version(req)
102
+ end
103
+
97
104
  def update_pyproject_version(req)
98
105
  requirement_strings = req[:requirement].split(",").map(&:strip)
99
106
 
@@ -180,10 +187,14 @@ module Dependabot
180
187
  return req unless req.fetch(:requirement)
181
188
 
182
189
  case update_strategy
190
+ when :widen_ranges
191
+ widen_requirement(req)
183
192
  when :bump_versions
184
193
  update_requirement(req)
185
194
  when :bump_versions_if_necessary
186
195
  update_requirement_if_needed(req)
196
+ else
197
+ raise "Unexpected update strategy: #{update_strategy}"
187
198
  end
188
199
  end
189
200
 
@@ -212,6 +223,14 @@ module Dependabot
212
223
  req.merge(requirement: :unfixable)
213
224
  end
214
225
 
226
+ def widen_requirement(req)
227
+ return req if new_version_satisfies?(req)
228
+
229
+ new_requirement = widen_requirement_range(req[:requirement])
230
+
231
+ req.merge(requirement: new_requirement)
232
+ end
233
+
215
234
  def new_version_satisfies?(req)
216
235
  requirement_class.
217
236
  requirements_array(req.fetch(:requirement)).
@@ -256,8 +275,10 @@ module Dependabot
256
275
  next r.to_s if r.satisfied_by?(latest_resolvable_version)
257
276
 
258
277
  case op = r.requirements.first.first
259
- when "<", "<="
260
- "<" + update_greatest_version(r.to_s, latest_resolvable_version)
278
+ when "<"
279
+ "<" + update_greatest_version(r.requirements.first.last, latest_resolvable_version)
280
+ when "<="
281
+ "<=" + latest_resolvable_version.to_s
261
282
  when "!=", ">", ">="
262
283
  raise UnfixableRequirement
263
284
  else
@@ -329,14 +350,12 @@ module Dependabot
329
350
  end
330
351
  end
331
352
 
332
- # Updates the version in a "<" or "<=" constraint to allow the given
333
- # version
334
- def update_greatest_version(req_string, version_to_be_permitted)
353
+ # Updates the version in a "<" constraint to allow the given version
354
+ def update_greatest_version(version, version_to_be_permitted)
335
355
  if version_to_be_permitted.is_a?(String)
336
356
  version_to_be_permitted =
337
357
  Python::Version.new(version_to_be_permitted)
338
358
  end
339
- version = Python::Version.new(req_string.gsub(/<=?/, ""))
340
359
  version = version.release if version.prerelease?
341
360
 
342
361
  index_to_update = [