dependabot-python 0.320.0 → 0.320.1

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.
@@ -1,6 +1,7 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
4
5
  require "open3"
5
6
  require "dependabot/dependency"
6
7
  require "dependabot/python/requirement_parser"
@@ -23,33 +24,58 @@ module Dependabot
23
24
  # This class does version resolution for pip-compile. Its approach is:
24
25
  # - Unlock the dependency we're checking in the requirements.in file
25
26
  # - Run `pip-compile` and see what the result is
26
- class PipCompileVersionResolver
27
- GIT_DEPENDENCY_UNREACHABLE_REGEX = /git clone --filter=blob:none --quiet (?<url>[^\s]+).* /
28
- GIT_REFERENCE_NOT_FOUND_REGEX = /Did not find branch or tag '(?<tag>[^\n"]+)'/m
29
- NATIVE_COMPILATION_ERROR =
30
- "pip._internal.exceptions.InstallationSubprocessError: Getting requirements to build wheel exited with 1"
27
+ class PipCompileVersionResolver # rubocop:disable Metrics/ClassLength
28
+ extend T::Sig
29
+
30
+ GIT_DEPENDENCY_UNREACHABLE_REGEX = T.let(/git clone --filter=blob:none --quiet (?<url>[^\s]+).* /, Regexp)
31
+ GIT_REFERENCE_NOT_FOUND_REGEX = T.let(/Did not find branch or tag '(?<tag>[^\n"]+)'/m, Regexp)
32
+ NATIVE_COMPILATION_ERROR = T.let(
33
+ "pip._internal.exceptions.InstallationSubprocessError: Getting requirements to build wheel exited with 1",
34
+ String
35
+ )
31
36
  # See https://packaging.python.org/en/latest/tutorials/packaging-projects/#configuring-metadata
32
- PYTHON_PACKAGE_NAME_REGEX = /[A-Za-z0-9_\-]+/
33
- RESOLUTION_IMPOSSIBLE_ERROR = "ResolutionImpossible"
34
- ERROR_REGEX = /(?<=ERROR\:\W).*$/
37
+ PYTHON_PACKAGE_NAME_REGEX = T.let(/[A-Za-z0-9_\-]+/, Regexp)
38
+ RESOLUTION_IMPOSSIBLE_ERROR = T.let("ResolutionImpossible", String)
39
+ ERROR_REGEX = T.let(/(?<=ERROR\:\W).*$/, Regexp)
35
40
 
41
+ sig { returns(Dependabot::Dependency) }
36
42
  attr_reader :dependency
43
+
44
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
37
45
  attr_reader :dependency_files
46
+
47
+ sig { returns(T::Array[Dependabot::Credential]) }
38
48
  attr_reader :credentials
49
+
50
+ sig { returns(T.nilable(String)) }
39
51
  attr_reader :repo_contents_path
52
+
53
+ sig { returns(PipCompileErrorHandler) }
40
54
  attr_reader :error_handler
41
55
 
56
+ sig do
57
+ params(
58
+ dependency: Dependabot::Dependency,
59
+ dependency_files: T::Array[Dependabot::DependencyFile],
60
+ credentials: T::Array[Dependabot::Credential],
61
+ repo_contents_path: T.nilable(String)
62
+ ).void
63
+ end
42
64
  def initialize(dependency:, dependency_files:, credentials:, repo_contents_path:)
43
- @dependency = dependency
44
- @dependency_files = dependency_files
45
- @credentials = credentials
46
- @repo_contents_path = repo_contents_path
47
- @build_isolation = true
48
- @error_handler = PipCompileErrorHandler.new
65
+ @dependency = T.let(dependency, Dependabot::Dependency)
66
+ @dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
67
+ @credentials = T.let(credentials, T::Array[Dependabot::Credential])
68
+ @repo_contents_path = T.let(repo_contents_path, T.nilable(String))
69
+ @build_isolation = T.let(true, T::Boolean)
70
+ @error_handler = T.let(PipCompileErrorHandler.new, PipCompileErrorHandler)
49
71
  end
50
72
 
73
+ sig { params(requirement: T.nilable(String)).returns(T.nilable(Dependabot::Python::Version)) }
51
74
  def latest_resolvable_version(requirement: nil)
52
- @latest_resolvable_version_string ||= {}
75
+ @latest_resolvable_version_string ||= T.let(
76
+ {},
77
+ T.nilable(T::Hash[T.nilable(String), T.nilable(Dependabot::Python::Version)])
78
+ )
53
79
  return @latest_resolvable_version_string[requirement] if @latest_resolvable_version_string.key?(requirement)
54
80
 
55
81
  version_string =
@@ -59,9 +85,10 @@ module Dependabot
59
85
  version_string.nil? ? nil : Python::Version.new(version_string)
60
86
  end
61
87
 
88
+ sig { params(version: Gem::Version).returns(T::Boolean) }
62
89
  def resolvable?(version:)
63
- @resolvable ||= {}
64
- return @resolvable[version] if @resolvable.key?(version)
90
+ @resolvable ||= T.let({}, T.nilable(T::Hash[Gem::Version, T::Boolean]))
91
+ return T.must(@resolvable[version]) if @resolvable.key?(version)
65
92
 
66
93
  @resolvable[version] = if latest_resolvable_version(requirement: "==#{version}")
67
94
  true
@@ -72,6 +99,7 @@ module Dependabot
72
99
 
73
100
  private
74
101
 
102
+ sig { params(requirement: T.nilable(String)).returns(T.nilable(String)) }
75
103
  def fetch_latest_resolvable_version_string(requirement:)
76
104
  SharedHelpers.in_a_temporary_directory do
77
105
  SharedHelpers.with_git_configured(credentials: credentials) do
@@ -90,6 +118,7 @@ module Dependabot
90
118
  end
91
119
  end
92
120
 
121
+ sig { params(filename: String).returns(T::Boolean) }
93
122
  def compile_file(filename)
94
123
  # Shell out to pip-compile.
95
124
  # This is slow, as pip-compile needs to do installs.
@@ -126,15 +155,18 @@ module Dependabot
126
155
  end
127
156
 
128
157
  handle_pip_compile_errors(e.message)
158
+ false
129
159
  end
130
160
 
161
+ sig { params(error: Dependabot::SharedHelpers::HelperSubprocessFailed).returns(T::Boolean) }
131
162
  def compilation_error?(error)
132
163
  error.message.include?(NATIVE_COMPILATION_ERROR)
133
164
  end
134
165
 
135
166
  # rubocop:disable Metrics/AbcSize
136
167
  # rubocop:disable Metrics/PerceivedComplexity
137
- def handle_pip_compile_errors(message)
168
+ sig { params(message: String).returns(T.nilable(String)) }
169
+ def handle_pip_compile_errors(message) # rubocop:disable Metrics/MethodLength
138
170
  if message.include?(RESOLUTION_IMPOSSIBLE_ERROR)
139
171
  check_original_requirements_resolvable
140
172
  # If the original requirements are resolvable but we get an
@@ -167,21 +199,24 @@ module Dependabot
167
199
  end
168
200
 
169
201
  if message.match?(GIT_REFERENCE_NOT_FOUND_REGEX)
170
- tag = message.match(GIT_REFERENCE_NOT_FOUND_REGEX).named_captures.fetch("tag")
171
- constraints_section = message.split("Finding the best candidates:").first
202
+ tag = T.must(T.must(message.match(GIT_REFERENCE_NOT_FOUND_REGEX)).named_captures.fetch("tag"))
203
+ constraints_section = T.must(message.split("Finding the best candidates:").first)
172
204
  egg_regex = /#{Regexp.escape(tag)}#egg=(#{PYTHON_PACKAGE_NAME_REGEX})/
173
205
  name_match = constraints_section.scan(egg_regex)
174
206
 
175
207
  # We can determine the name of the package from another part of the logger output if it has a unique tag
176
- raise GitDependencyReferenceNotFound, name_match.first.first if name_match.length == 1
208
+ if name_match.length == 1 && name_match.first.is_a?(Array)
209
+ raise GitDependencyReferenceNotFound,
210
+ T.must(T.cast(T.must(name_match.first), T::Array[String]).first)
211
+ end
177
212
 
178
213
  raise GitDependencyReferenceNotFound, "(unknown package at #{tag})"
179
214
  end
180
215
 
181
216
  if message.match?(GIT_DEPENDENCY_UNREACHABLE_REGEX)
182
- url = message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX)
183
- .named_captures.fetch("url")
184
- raise GitDependenciesNotReachable, url
217
+ url = T.must(message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX))
218
+ .named_captures.fetch("url")
219
+ raise GitDependenciesNotReachable, T.must(url)
185
220
  end
186
221
 
187
222
  raise Dependabot::OutOfDisk if message.end_with?("[Errno 28] No space left on device")
@@ -199,6 +234,7 @@ module Dependabot
199
234
  # Note: We raise errors from this method, rather than returning a
200
235
  # boolean, so that all deps for this repo will raise identical
201
236
  # errors when failing to update
237
+ sig { returns(T::Boolean) }
202
238
  def check_original_requirements_resolvable
203
239
  SharedHelpers.in_a_temporary_directory do
204
240
  SharedHelpers.with_git_configured(credentials: credentials) do
@@ -230,10 +266,12 @@ module Dependabot
230
266
  end
231
267
  end
232
268
 
233
- def run_command(command, env: python_env, fingerprint:)
269
+ sig { params(command: String, fingerprint: String, env: T::Hash[String, String]).void }
270
+ def run_command(command, fingerprint:, env: python_env)
234
271
  SharedHelpers.run_shell_command(command, env: env, fingerprint: fingerprint, stderr_to_stdout: true)
235
272
  end
236
273
 
274
+ sig { params(options: String).returns(String) }
237
275
  def pip_compile_options_fingerprint(options)
238
276
  options.sub(
239
277
  /--output-file=\S+/, "--output-file=<output_file>"
@@ -244,6 +282,7 @@ module Dependabot
244
282
  )
245
283
  end
246
284
 
285
+ sig { params(filename: String).returns(String) }
247
286
  def pip_compile_options(filename)
248
287
  options = @build_isolation ? ["--build-isolation"] : ["--no-build-isolation"]
249
288
  options += pip_compile_index_options
@@ -258,6 +297,7 @@ module Dependabot
258
297
  options.join(" ")
259
298
  end
260
299
 
300
+ sig { returns(T::Array[String]) }
261
301
  def pip_compile_index_options
262
302
  credentials
263
303
  .select { |cred| cred["type"] == "python_index" }
@@ -272,6 +312,7 @@ module Dependabot
272
312
  end
273
313
  end
274
314
 
315
+ sig { params(command: String, fingerprint: String).void }
275
316
  def run_pip_compile_command(command, fingerprint:)
276
317
  run_command(
277
318
  "pyenv local #{language_version_manager.python_major_minor}",
@@ -281,12 +322,13 @@ module Dependabot
281
322
  run_command(command, fingerprint: fingerprint)
282
323
  end
283
324
 
325
+ sig { returns(T::Hash[String, String]) }
284
326
  def python_env
285
327
  env = {}
286
328
 
287
329
  # Handle Apache Airflow 1.10.x installs
288
- if dependency_files.any? { |f| f.content.include?("apache-airflow") }
289
- if dependency_files.any? { |f| f.content.include?("unidecode") }
330
+ if dependency_files.any? { |f| T.must(f.content).include?("apache-airflow") }
331
+ if dependency_files.any? { |f| T.must(f.content).include?("unidecode") }
290
332
  env["AIRFLOW_GPL_UNIDECODE"] = "yes"
291
333
  else
292
334
  env["SLUGIFY_USES_TEXT_UNIDECODE"] = "yes"
@@ -296,8 +338,11 @@ module Dependabot
296
338
  env
297
339
  end
298
340
 
299
- def write_temporary_dependency_files(updated_req: nil,
300
- update_requirement: true)
341
+ sig do
342
+ params(updated_req: T.nilable(String), update_requirement: T::Boolean)
343
+ .returns(T::Array[Dependabot::DependencyFile])
344
+ end
345
+ def write_temporary_dependency_files(updated_req: nil, update_requirement: true)
301
346
  dependency_files.each do |file|
302
347
  path = file.name
303
348
  FileUtils.mkdir_p(Pathname.new(path).dirname)
@@ -325,6 +370,7 @@ module Dependabot
325
370
  end
326
371
  end
327
372
 
373
+ sig { void }
328
374
  def write_original_manifest_files
329
375
  pip_compile_files.each do |file|
330
376
  FileUtils.mkdir_p(Pathname.new(file.name).dirname)
@@ -332,9 +378,10 @@ module Dependabot
332
378
  end
333
379
  end
334
380
 
381
+ sig { params(file: Dependabot::DependencyFile).returns(String) }
335
382
  def sanitized_setup_file_content(file)
336
- @sanitized_setup_file_content ||= {}
337
- return @sanitized_setup_file_content[file.name] if @sanitized_setup_file_content[file.name]
383
+ @sanitized_setup_file_content ||= T.let({}, T.nilable(T::Hash[String, String]))
384
+ return T.must(@sanitized_setup_file_content[file.name]) if @sanitized_setup_file_content[file.name]
338
385
 
339
386
  @sanitized_setup_file_content[file.name] =
340
387
  Python::FileUpdater::SetupFileSanitizer
@@ -342,35 +389,40 @@ module Dependabot
342
389
  .sanitized_content
343
390
  end
344
391
 
392
+ sig { params(file: Dependabot::DependencyFile).returns(T.nilable(Dependabot::DependencyFile)) }
345
393
  def setup_cfg(file)
346
394
  dependency_files.find do |f|
347
395
  f.name == file.name.sub(/\.py$/, ".cfg")
348
396
  end
349
397
  end
350
398
 
399
+ sig { params(file: Dependabot::DependencyFile, updated_req: T.nilable(String)).returns(String) }
351
400
  def update_req_file(file, updated_req)
352
- return file.content unless file.name.end_with?(".in")
401
+ return T.must(file.content) unless file.name.end_with?(".in")
353
402
 
354
403
  req = dependency.requirements.find { |r| r[:file] == file.name }
355
404
 
356
- return file.content + "\n#{dependency.name} #{updated_req}" unless req&.fetch(:requirement)
405
+ return T.must(file.content) + "\n#{dependency.name} #{updated_req}" unless req&.fetch(:requirement)
357
406
 
358
407
  Python::FileUpdater::RequirementReplacer.new(
359
- content: file.content,
408
+ content: T.must(file.content),
360
409
  dependency_name: dependency.name,
361
410
  old_requirement: req[:requirement],
362
411
  new_requirement: updated_req
363
412
  ).updated_content
364
413
  end
365
414
 
415
+ sig { params(name: String).returns(String) }
366
416
  def normalise(name)
367
417
  NameNormaliser.normalise(name)
368
418
  end
369
419
 
420
+ sig { params(message: String).returns(String) }
370
421
  def clean_error_message(message)
371
- message.scan(ERROR_REGEX).last
422
+ T.must(T.cast(message.scan(ERROR_REGEX), T::Array[String]).last)
372
423
  end
373
424
 
425
+ sig { returns(T::Array[String]) }
374
426
  def filenames_to_compile
375
427
  files_from_reqs =
376
428
  dependency.requirements
@@ -388,10 +440,11 @@ module Dependabot
388
440
  order_filenames_for_compilation(filenames)
389
441
  end
390
442
 
443
+ sig { params(filename: String).returns(T.nilable(Dependabot::DependencyFile)) }
391
444
  def compiled_file_for_filename(filename)
392
445
  compiled_file =
393
446
  compiled_files
394
- .find { |f| f.content.match?(output_file_regex(filename)) }
447
+ .find { |f| T.must(f.content).match?(output_file_regex(filename)) }
395
448
 
396
449
  compiled_file ||=
397
450
  compiled_files
@@ -400,22 +453,25 @@ module Dependabot
400
453
  compiled_file
401
454
  end
402
455
 
456
+ sig { params(filename: String).returns(String) }
403
457
  def output_file_regex(filename)
404
458
  "--output-file[=\s]+.*\s#{Regexp.escape(filename)}\s*$"
405
459
  end
406
460
 
461
+ sig { params(compiled_file: T.nilable(Dependabot::DependencyFile)).returns(T::Boolean) }
407
462
  def compiled_file_includes_dependency?(compiled_file)
408
463
  return false unless compiled_file
409
464
 
410
465
  regex = RequirementParser::INSTALL_REQ_WITH_REQUIREMENT
411
466
 
412
467
  matches = []
413
- compiled_file.content.scan(regex) { matches << Regexp.last_match }
468
+ T.must(compiled_file.content).scan(regex) { matches << Regexp.last_match }
414
469
  matches.any? { |m| normalise(m[:name]) == dependency.name }
415
470
  end
416
471
 
417
472
  # If the files we need to update require one another then we need to
418
473
  # update them in the right order
474
+ sig { params(filenames: T::Array[String]).returns(T::Array[String]) }
419
475
  def order_filenames_for_compilation(filenames)
420
476
  ordered_filenames = T.let([], T::Array[String])
421
477
 
@@ -423,7 +479,7 @@ module Dependabot
423
479
  ordered_filenames +=
424
480
  remaining_filenames
425
481
  .reject do |fn|
426
- unupdated_reqs = requirement_map[fn] - ordered_filenames
482
+ unupdated_reqs = T.must(requirement_map[fn]) - ordered_filenames
427
483
  unupdated_reqs.intersect?(filenames)
428
484
  end
429
485
  end
@@ -431,11 +487,12 @@ module Dependabot
431
487
  ordered_filenames
432
488
  end
433
489
 
490
+ sig { returns(T::Hash[String, T::Array[String]]) }
434
491
  def requirement_map
435
492
  child_req_regex = Python::FileFetcher::CHILD_REQUIREMENT_REGEX
436
- @requirement_map ||=
493
+ @requirement_map ||= T.let(
437
494
  pip_compile_files.each_with_object({}) do |file, req_map|
438
- paths = file.content.scan(child_req_regex).flatten
495
+ paths = T.must(file.content).scan(child_req_regex).flatten
439
496
  current_dir = File.dirname(file.name)
440
497
 
441
498
  req_map[file.name] =
@@ -447,9 +504,12 @@ module Dependabot
447
504
 
448
505
  path
449
506
  end.uniq.compact
450
- end
507
+ end,
508
+ T.nilable(T::Hash[String, T::Array[String]])
509
+ )
451
510
  end
452
511
 
512
+ sig { returns(T.nilable(String)) }
453
513
  def parse_updated_files
454
514
  updated_files =
455
515
  dependency_files.map do |file|
@@ -467,32 +527,40 @@ module Dependabot
467
527
  ).parse.find { |d| d.name == dependency.name }&.version
468
528
  end
469
529
 
530
+ sig { returns(Dependabot::Python::FileParser::PythonRequirementParser) }
470
531
  def python_requirement_parser
471
- @python_requirement_parser ||=
532
+ @python_requirement_parser ||= T.let(
472
533
  FileParser::PythonRequirementParser.new(
473
534
  dependency_files: dependency_files
474
- )
535
+ ), T.nilable(FileParser::PythonRequirementParser)
536
+ )
475
537
  end
476
538
 
539
+ sig { returns(Dependabot::Python::LanguageVersionManager) }
477
540
  def language_version_manager
478
- @language_version_manager ||=
541
+ @language_version_manager ||= T.let(
479
542
  LanguageVersionManager.new(
480
543
  python_requirement_parser: python_requirement_parser
481
- )
544
+ ), T.nilable(LanguageVersionManager)
545
+ )
482
546
  end
483
547
 
548
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
484
549
  def setup_files
485
550
  dependency_files.select { |f| f.name.end_with?("setup.py") }
486
551
  end
487
552
 
553
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
488
554
  def pip_compile_files
489
555
  dependency_files.select { |f| f.name.end_with?(".in") }
490
556
  end
491
557
 
558
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
492
559
  def compiled_files
493
560
  dependency_files.select { |f| f.name.end_with?(".txt") }
494
561
  end
495
562
 
563
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
496
564
  def setup_cfg_files
497
565
  dependency_files.select { |f| f.name.end_with?("setup.cfg") }
498
566
  end
@@ -500,14 +568,17 @@ module Dependabot
500
568
  end
501
569
 
502
570
  class PipCompileErrorHandler
503
- SUBPROCESS_ERROR = /subprocess-exited-with-error/
571
+ extend T::Sig
572
+
573
+ SUBPROCESS_ERROR = T.let(/subprocess-exited-with-error/, Regexp)
504
574
 
505
- INSTALLATION_ERROR = /InstallationError/
575
+ INSTALLATION_ERROR = T.let(/InstallationError/, Regexp)
506
576
 
507
- INSTALLATION_SUBPROCESS_ERROR = /InstallationSubprocessError/
577
+ INSTALLATION_SUBPROCESS_ERROR = T.let(/InstallationSubprocessError/, Regexp)
508
578
 
509
- HASH_MISMATCH = /HashMismatch/
579
+ HASH_MISMATCH = T.let(/HashMismatch/, Regexp)
510
580
 
581
+ sig { params(error: String).void }
511
582
  def handle_pipcompile_error(error)
512
583
  return unless error.match?(SUBPROCESS_ERROR) || error.match?(INSTALLATION_ERROR) ||
513
584
  error.match?(INSTALLATION_SUBPROCESS_ERROR) || error.match?(HASH_MISMATCH)
@@ -1,6 +1,7 @@
1
- # typed: true
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
4
5
  require "dependabot/python/language_version_manager"
5
6
  require "dependabot/python/update_checker"
6
7
  require "dependabot/python/update_checker/latest_version_finder"
@@ -10,27 +11,45 @@ module Dependabot
10
11
  module Python
11
12
  class UpdateChecker
12
13
  class PipVersionResolver
14
+ extend T::Sig
15
+
16
+ sig do
17
+ params(
18
+ dependency: Dependabot::Dependency,
19
+ dependency_files: T::Array[Dependabot::DependencyFile],
20
+ credentials: T::Array[Dependabot::Credential],
21
+ ignored_versions: T::Array[String],
22
+ security_advisories: T::Array[Dependabot::SecurityAdvisory],
23
+ update_cooldown: T.nilable(Dependabot::Package::ReleaseCooldownOptions),
24
+ raise_on_ignored: T::Boolean
25
+ ).void
26
+ end
13
27
  def initialize(dependency:, dependency_files:, credentials:,
14
- ignored_versions:, update_cooldown: nil, raise_on_ignored: false,
15
- security_advisories:)
16
- @dependency = dependency
17
- @dependency_files = dependency_files
18
- @credentials = credentials
19
- @ignored_versions = ignored_versions
20
- @update_cooldown = update_cooldown
21
- @raise_on_ignored = raise_on_ignored
22
- @security_advisories = security_advisories
28
+ ignored_versions:, security_advisories:, update_cooldown: nil, raise_on_ignored: false)
29
+ @dependency = T.let(dependency, Dependabot::Dependency)
30
+ @dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
31
+ @credentials = T.let(credentials, T::Array[Dependabot::Credential])
32
+ @ignored_versions = T.let(ignored_versions, T::Array[String])
33
+ @security_advisories = T.let(security_advisories, T::Array[Dependabot::SecurityAdvisory])
34
+ @update_cooldown = T.let(update_cooldown, T.nilable(Dependabot::Package::ReleaseCooldownOptions))
35
+ @raise_on_ignored = T.let(raise_on_ignored, T::Boolean)
36
+ @latest_version_finder = T.let(nil, T.nilable(LatestVersionFinder))
37
+ @python_requirement_parser = T.let(nil, T.nilable(FileParser::PythonRequirementParser))
38
+ @language_version_manager = T.let(nil, T.nilable(LanguageVersionManager))
23
39
  end
24
40
 
41
+ sig { returns(T.nilable(Dependabot::Version)) }
25
42
  def latest_resolvable_version
26
43
  latest_version_finder.latest_version(language_version: language_version_manager.python_version)
27
44
  end
28
45
 
46
+ sig { returns(T.nilable(Dependabot::Version)) }
29
47
  def latest_resolvable_version_with_no_unlock
30
48
  latest_version_finder
31
49
  .latest_version_with_no_unlock(language_version: language_version_manager.python_version)
32
50
  end
33
51
 
52
+ sig { returns(T.nilable(Dependabot::Version)) }
34
53
  def lowest_resolvable_security_fix_version
35
54
  latest_version_finder
36
55
  .lowest_security_fix_version(language_version: language_version_manager.python_version)
@@ -38,12 +57,22 @@ module Dependabot
38
57
 
39
58
  private
40
59
 
60
+ sig { returns(Dependabot::Dependency) }
41
61
  attr_reader :dependency
62
+
63
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
42
64
  attr_reader :dependency_files
65
+
66
+ sig { returns(T::Array[Dependabot::Credential]) }
43
67
  attr_reader :credentials
68
+
69
+ sig { returns(T::Array[String]) }
44
70
  attr_reader :ignored_versions
71
+
72
+ sig { returns(T::Array[Dependabot::SecurityAdvisory]) }
45
73
  attr_reader :security_advisories
46
74
 
75
+ sig { returns(LatestVersionFinder) }
47
76
  def latest_version_finder
48
77
  @latest_version_finder ||= LatestVersionFinder.new(
49
78
  dependency: dependency,
@@ -57,6 +86,7 @@ module Dependabot
57
86
  @latest_version_finder
58
87
  end
59
88
 
89
+ sig { returns(FileParser::PythonRequirementParser) }
60
90
  def python_requirement_parser
61
91
  @python_requirement_parser ||=
62
92
  FileParser::PythonRequirementParser.new(
@@ -64,6 +94,7 @@ module Dependabot
64
94
  )
65
95
  end
66
96
 
97
+ sig { returns(LanguageVersionManager) }
67
98
  def language_version_manager
68
99
  @language_version_manager ||=
69
100
  LanguageVersionManager.new(