dependabot-python 0.215.0 → 0.216.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 = [