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 "excon"
5
6
  require "open3"
6
7
  require "dependabot/dependency"
@@ -19,12 +20,14 @@ module Dependabot
19
20
  module Python
20
21
  class UpdateChecker
21
22
  class PipenvVersionResolver
23
+ extend T::Sig
24
+
22
25
  GIT_DEPENDENCY_UNREACHABLE_REGEX = /git clone --filter=blob:none --quiet (?<url>[^\s]+).*/
23
26
  GIT_REFERENCE_NOT_FOUND_REGEX = /git checkout -q (?<tag>[^\s]+).*/
24
27
  PIPENV_INSTALLATION_ERROR_NEW = "Getting requirements to build wheel exited with 1"
25
28
 
26
29
  # Can be removed when Python 3.11 support is dropped
27
- PIPENV_INSTALLATION_ERROR_OLD = Regexp.quote("python setup.py egg_info exited with 1")
30
+ PIPENV_INSTALLATION_ERROR_OLD = T.let(Regexp.quote("python setup.py egg_info exited with 1"), String)
28
31
 
29
32
  PIPENV_INSTALLATION_ERROR = /#{PIPENV_INSTALLATION_ERROR_NEW}|#{PIPENV_INSTALLATION_ERROR_OLD}/
30
33
  PIPENV_INSTALLATION_ERROR_REGEX =
@@ -32,18 +35,34 @@ module Dependabot
32
35
 
33
36
  PIPENV_RANGE_WARNING = /Python version range specifier '(?<ver>.*)' is not supported/
34
37
 
38
+ sig { returns(Dependabot::Dependency) }
35
39
  attr_reader :dependency
40
+
41
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
36
42
  attr_reader :dependency_files
43
+
44
+ sig { returns(T::Array[Dependabot::Credential]) }
37
45
  attr_reader :credentials
46
+
47
+ sig { returns(T.nilable(String)) }
38
48
  attr_reader :repo_contents_path
39
49
 
50
+ sig do
51
+ params(
52
+ dependency: Dependabot::Dependency,
53
+ dependency_files: T::Array[Dependabot::DependencyFile],
54
+ credentials: T::Array[Dependabot::Credential],
55
+ repo_contents_path: T.nilable(String)
56
+ ).void
57
+ end
40
58
  def initialize(dependency:, dependency_files:, credentials:, repo_contents_path:)
41
- @dependency = dependency
42
- @dependency_files = dependency_files
43
- @credentials = credentials
44
- @repo_contents_path = repo_contents_path
59
+ @dependency = T.let(dependency, Dependabot::Dependency)
60
+ @dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
61
+ @credentials = T.let(credentials, T::Array[Dependabot::Credential])
62
+ @repo_contents_path = T.let(repo_contents_path, T.nilable(String))
45
63
  end
46
64
 
65
+ sig { params(requirement: T.nilable(String)).returns(T.nilable(Dependabot::Python::Version)) }
47
66
  def latest_resolvable_version(requirement: nil)
48
67
  version_string =
49
68
  fetch_latest_resolvable_version_string(requirement: requirement)
@@ -51,17 +70,19 @@ module Dependabot
51
70
  version_string.nil? ? nil : Python::Version.new(version_string)
52
71
  end
53
72
 
73
+ sig { params(version: Gem::Version).returns(T::Boolean) }
54
74
  def resolvable?(version:)
55
- @resolvable ||= {}
56
- return @resolvable[version] if @resolvable.key?(version)
75
+ @resolvable ||= T.let({}, T.nilable(T::Hash[Gem::Version, T::Boolean]))
76
+ return T.must(@resolvable[version]) if @resolvable.key?(version)
57
77
 
58
78
  @resolvable[version] = !!fetch_latest_resolvable_version_string(requirement: "==#{version}")
59
79
  end
60
80
 
61
81
  private
62
82
 
83
+ sig { params(requirement: T.nilable(String)).returns(T.nilable(String)) }
63
84
  def fetch_latest_resolvable_version_string(requirement:)
64
- @latest_resolvable_version_string ||= {}
85
+ @latest_resolvable_version_string ||= T.let({}, T.nilable(T::Hash[T.nilable(String), T.nilable(String)]))
65
86
  return @latest_resolvable_version_string[requirement] if @latest_resolvable_version_string.key?(requirement)
66
87
 
67
88
  @latest_resolvable_version_string[requirement] ||=
@@ -81,6 +102,7 @@ module Dependabot
81
102
  # rubocop:disable Metrics/PerceivedComplexity
82
103
  # rubocop:disable Metrics/AbcSize
83
104
  # rubocop:disable Metrics/MethodLength
105
+ sig { params(error: Dependabot::SharedHelpers::HelperSubprocessFailed).returns(T.nilable(String)) }
84
106
  def handle_pipenv_errors(error)
85
107
  if error.message.include?("no version found at all") ||
86
108
  error.message.include?("Invalid specifier:") ||
@@ -99,16 +121,17 @@ module Dependabot
99
121
  end
100
122
 
101
123
  if error.message.match?(GIT_REFERENCE_NOT_FOUND_REGEX)
102
- tag = error.message.match(GIT_REFERENCE_NOT_FOUND_REGEX).named_captures.fetch("tag")
124
+ match_result = error.message.match(GIT_REFERENCE_NOT_FOUND_REGEX)
125
+ tag = T.must(match_result).named_captures.fetch("tag")
103
126
  # Unfortunately the error message doesn't include the package name.
104
127
  # TODO: Talk with pipenv maintainers about exposing the package name, it used to be part of the error output
105
128
  raise GitDependencyReferenceNotFound, "(unknown package at #{tag})"
106
129
  end
107
130
 
108
131
  if error.message.match?(GIT_DEPENDENCY_UNREACHABLE_REGEX)
109
- url = error.message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX)
110
- .named_captures.fetch("url")
111
- raise GitDependenciesNotReachable, url
132
+ match_result = error.message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX)
133
+ url = T.must(match_result).named_captures.fetch("url")
134
+ raise GitDependenciesNotReachable, T.must(url)
112
135
  end
113
136
 
114
137
  if error.message.include?("Could not find a version") || error.message.include?("ResolutionFailure")
@@ -151,6 +174,7 @@ module Dependabot
151
174
  # Note: We raise errors from this method, rather than returning a
152
175
  # boolean, so that all deps for this repo will raise identical
153
176
  # errors when failing to update
177
+ sig { returns(T::Boolean) }
154
178
  def check_original_requirements_resolvable
155
179
  SharedHelpers.in_a_temporary_repo_directory(base_directory, repo_contents_path) do
156
180
  write_temporary_dependency_files(update_pipfile: false)
@@ -160,13 +184,16 @@ module Dependabot
160
184
  true
161
185
  rescue SharedHelpers::HelperSubprocessFailed => e
162
186
  handle_pipenv_errors_resolving_original_reqs(e)
187
+ false
163
188
  end
164
189
  end
165
190
 
191
+ sig { returns(String) }
166
192
  def base_directory
167
- dependency_files.first.directory
193
+ T.must(dependency_files.first).directory
168
194
  end
169
195
 
196
+ sig { params(error: Dependabot::SharedHelpers::HelperSubprocessFailed).void }
170
197
  def handle_pipenv_errors_resolving_original_reqs(error)
171
198
  if error.message.include?("Could not find a version") ||
172
199
  error.message.include?("package versions have conflicting dependencies")
@@ -193,6 +220,7 @@ module Dependabot
193
220
  raise
194
221
  end
195
222
 
223
+ sig { params(message: String).returns(String) }
196
224
  def clean_error_message(message)
197
225
  # Pipenv outputs a lot of things to STDERR, so we need to clean
198
226
  # up the error message
@@ -213,9 +241,11 @@ module Dependabot
213
241
  msg.gsub(/http.*?(?=\s)/, "<redacted>")
214
242
  end
215
243
 
244
+ sig { params(error_message: String).void }
216
245
  def handle_pipenv_installation_error(error_message)
217
246
  # Find the dependency that's causing resolution to fail
218
- dependency_name = error_message.match(PIPENV_INSTALLATION_ERROR_REGEX).named_captures["name"]
247
+ match_result = error_message.match(PIPENV_INSTALLATION_ERROR_REGEX)
248
+ dependency_name = T.must(match_result).named_captures["name"]
219
249
  raise unless dependency_name
220
250
 
221
251
  msg = "Pipenv failed to install \"#{dependency_name}\". This could be caused by missing system " \
@@ -226,6 +256,7 @@ module Dependabot
226
256
  raise DependencyFileNotResolvable, msg
227
257
  end
228
258
 
259
+ sig { params(update_pipfile: T::Boolean).void }
229
260
  def write_temporary_dependency_files(update_pipfile: true)
230
261
  dependency_files.each do |file|
231
262
  path = file.name
@@ -256,6 +287,7 @@ module Dependabot
256
287
  )
257
288
  end
258
289
 
290
+ sig { void }
259
291
  def install_required_python
260
292
  # Initialize a git repo to appease pip-tools
261
293
  begin
@@ -267,7 +299,12 @@ module Dependabot
267
299
  language_version_manager.install_required_python
268
300
  end
269
301
 
302
+ sig { params(file: Dependabot::DependencyFile).returns(String) }
270
303
  def sanitized_setup_file_content(file)
304
+ @sanitized_setup_file_content = T.let(
305
+ @sanitized_setup_file_content,
306
+ T.nilable(T::Hash[String, String])
307
+ )
271
308
  @sanitized_setup_file_content ||= {}
272
309
  @sanitized_setup_file_content[file.name] ||=
273
310
  Python::FileUpdater::SetupFileSanitizer
@@ -275,77 +312,97 @@ module Dependabot
275
312
  .sanitized_content
276
313
  end
277
314
 
315
+ sig { params(file: Dependabot::DependencyFile).returns(T.nilable(Dependabot::DependencyFile)) }
278
316
  def setup_cfg(file)
279
317
  config_name = file.name.sub(/\.py$/, ".cfg")
280
318
  dependency_files.find { |f| f.name == config_name }
281
319
  end
282
320
 
321
+ sig { returns(String) }
283
322
  def pipfile_content
284
- content = pipfile.content
323
+ pipfile_obj = T.must(pipfile)
324
+ content = T.must(pipfile_obj.content)
285
325
  content = add_private_sources(content)
286
326
  content = update_python_requirement(content)
287
- content = update_ssl_requirement(content, pipfile.content)
327
+ content = update_ssl_requirement(content, T.must(pipfile_obj.content))
288
328
 
289
329
  content
290
330
  end
291
331
 
332
+ sig { params(pipfile_content: String).returns(String) }
292
333
  def update_python_requirement(pipfile_content)
293
334
  Python::FileUpdater::PipfilePreparer
294
335
  .new(pipfile_content: pipfile_content)
295
336
  .update_python_requirement(language_version_manager.python_major_minor)
296
337
  end
297
338
 
339
+ sig { params(pipfile_content: String, parsed_file: String).returns(String) }
298
340
  def update_ssl_requirement(pipfile_content, parsed_file)
299
341
  Python::FileUpdater::PipfilePreparer
300
342
  .new(pipfile_content: pipfile_content)
301
343
  .update_ssl_requirement(parsed_file)
302
344
  end
303
345
 
346
+ sig { params(pipfile_content: String).returns(String) }
304
347
  def add_private_sources(pipfile_content)
305
348
  Python::FileUpdater::PipfilePreparer
306
349
  .new(pipfile_content: pipfile_content)
307
350
  .replace_sources(credentials)
308
351
  end
309
352
 
353
+ sig { params(command: String).returns(String) }
310
354
  def run_command(command)
311
355
  SharedHelpers.run_shell_command(command, stderr_to_stdout: true)
312
356
  end
313
357
 
358
+ sig { returns(Dependabot::Python::FileParser::PythonRequirementParser) }
314
359
  def python_requirement_parser
315
- @python_requirement_parser ||=
360
+ @python_requirement_parser ||= T.let(
316
361
  FileParser::PythonRequirementParser.new(
317
362
  dependency_files: dependency_files
318
- )
363
+ ),
364
+ T.nilable(Dependabot::Python::FileParser::PythonRequirementParser)
365
+ )
319
366
  end
320
367
 
368
+ sig { returns(Dependabot::Python::LanguageVersionManager) }
321
369
  def language_version_manager
322
- @language_version_manager ||=
370
+ @language_version_manager ||= T.let(
323
371
  LanguageVersionManager.new(
324
372
  python_requirement_parser: python_requirement_parser
325
- )
373
+ ),
374
+ T.nilable(Dependabot::Python::LanguageVersionManager)
375
+ )
326
376
  end
327
377
 
378
+ sig { returns(Dependabot::Python::PipenvRunner) }
328
379
  def pipenv_runner
329
- @pipenv_runner ||=
380
+ @pipenv_runner ||= T.let(
330
381
  PipenvRunner.new(
331
382
  dependency: dependency,
332
383
  lockfile: lockfile,
333
384
  language_version_manager: language_version_manager
334
- )
385
+ ),
386
+ T.nilable(Dependabot::Python::PipenvRunner)
387
+ )
335
388
  end
336
389
 
390
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
337
391
  def pipfile
338
392
  dependency_files.find { |f| f.name == "Pipfile" }
339
393
  end
340
394
 
395
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
341
396
  def lockfile
342
397
  dependency_files.find { |f| f.name == "Pipfile.lock" }
343
398
  end
344
399
 
400
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
345
401
  def setup_files
346
402
  dependency_files.select { |f| f.name.end_with?("setup.py") }
347
403
  end
348
404
 
405
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
349
406
  def setup_cfg_files
350
407
  dependency_files.select { |f| f.name.end_with?("setup.cfg") }
351
408
  end
@@ -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 "excon"
5
6
  require "toml-rb"
6
7
  require "open3"
@@ -43,23 +44,50 @@ module Dependabot
43
44
 
44
45
  INCOMPATIBLE_CONSTRAINTS = /Incompatible constraints in requirements of (?<dep>.+?) ((?<ver>.+?)):/
45
46
 
47
+ PACKAGE_RESOLVER_ERRORS = T.let({
48
+ package_info_error: /Unable to determine package info/,
49
+ self_dep_error: /Package '(?<path>.*)' is listed as a dependency of itself./,
50
+ incompatible_constraints: /Incompatible constraints in requirements/
51
+ }.freeze, T::Hash[T.nilable(String), Regexp])
52
+
53
+ sig { returns(Dependabot::Dependency) }
46
54
  attr_reader :dependency
55
+
56
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
47
57
  attr_reader :dependency_files
58
+
59
+ sig { returns(T::Array[Dependabot::Credential]) }
48
60
  attr_reader :credentials
61
+
62
+ sig { returns(T.nilable(String)) }
49
63
  attr_reader :repo_contents_path
50
64
 
51
65
  sig { returns(Dependabot::Python::PoetryErrorHandler) }
52
66
  attr_reader :error_handler
53
67
 
54
- def initialize(dependency:, dependency_files:, credentials:, repo_contents_path:)
55
- @dependency = dependency
56
- @dependency_files = dependency_files
57
- @credentials = credentials
58
- @repo_contents_path = repo_contents_path
59
- @error_handler = PoetryErrorHandler.new(dependencies: dependency,
60
- dependency_files: dependency_files)
68
+ sig do
69
+ params(
70
+ dependency: Dependabot::Dependency,
71
+ dependency_files: T::Array[Dependabot::DependencyFile],
72
+ credentials: T::Array[Dependabot::Credential],
73
+ repo_contents_path: T.nilable(String)
74
+ ).void
61
75
  end
62
-
76
+ def initialize(dependency:, dependency_files:, credentials:, repo_contents_path:)
77
+ @dependency = T.let(dependency, Dependabot::Dependency)
78
+ @dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
79
+ @credentials = T.let(credentials, T::Array[Dependabot::Credential])
80
+ @repo_contents_path = T.let(repo_contents_path, T.nilable(String))
81
+ @error_handler = T.let(PoetryErrorHandler.new(dependencies: dependency, dependency_files: dependency_files),
82
+ Dependabot::Python::PoetryErrorHandler)
83
+ @resolvable = T.let({}, T::Hash[Gem::Version, T::Boolean])
84
+ @latest_resolvable_version_string = T.let({}, T::Hash[T.nilable(String), T.nilable(String)])
85
+ @original_reqs_resolvable = T.let(nil, T.nilable(T::Boolean))
86
+ @python_requirement_parser = T.let(nil, T.nilable(FileParser::PythonRequirementParser))
87
+ @language_version_manager = T.let(nil, T.nilable(LanguageVersionManager))
88
+ end
89
+
90
+ sig { params(requirement: T.nilable(String)).returns(T.nilable(Dependabot::Python::Version)) }
63
91
  def latest_resolvable_version(requirement: nil)
64
92
  version_string =
65
93
  fetch_latest_resolvable_version_string(requirement: requirement)
@@ -67,9 +95,9 @@ module Dependabot
67
95
  version_string.nil? ? nil : Python::Version.new(version_string)
68
96
  end
69
97
 
98
+ sig { params(version: Gem::Version).returns(T::Boolean) }
70
99
  def resolvable?(version:)
71
- @resolvable ||= {}
72
- return @resolvable[version] if @resolvable.key?(version)
100
+ return T.must(@resolvable[version]) if @resolvable.key?(version)
73
101
 
74
102
  @resolvable[version] = if fetch_latest_resolvable_version_string(requirement: "==#{version}")
75
103
  true
@@ -84,8 +112,8 @@ module Dependabot
84
112
 
85
113
  private
86
114
 
115
+ sig { params(requirement: T.nilable(String)).returns(T.nilable(String)) }
87
116
  def fetch_latest_resolvable_version_string(requirement:)
88
- @latest_resolvable_version_string ||= {}
89
117
  return @latest_resolvable_version_string[requirement] if @latest_resolvable_version_string.key?(requirement)
90
118
 
91
119
  @latest_resolvable_version_string[requirement] ||=
@@ -112,6 +140,7 @@ module Dependabot
112
140
  end
113
141
  end
114
142
 
143
+ sig { params(updated_lockfile: T::Hash[String, T.untyped]).returns(T.nilable(String)) }
115
144
  def fetch_version_from_parsed_lockfile(updated_lockfile)
116
145
  version =
117
146
  updated_lockfile.fetch("package", [])
@@ -124,25 +153,26 @@ module Dependabot
124
153
  end
125
154
 
126
155
  # rubocop:disable Metrics/AbcSize
156
+ sig { params(error: StandardError).returns(T.nilable(String)) }
127
157
  def handle_poetry_errors(error)
128
158
  error_handler.handle_poetry_error(error)
129
159
 
130
160
  if error.message.gsub(/\s/, "").match?(GIT_REFERENCE_NOT_FOUND_REGEX)
131
161
  message = error.message.gsub(/\s/, "")
132
162
  match = message.match(GIT_REFERENCE_NOT_FOUND_REGEX)
133
- name = if (url = match.named_captures.fetch("url"))
163
+ name = if (url = T.must(match).named_captures.fetch("url"))
134
164
  File.basename(T.must(URI.parse(url).path))
135
165
  else
136
- message.match(GIT_REFERENCE_NOT_FOUND_REGEX)
137
- .named_captures.fetch("name")
166
+ T.must(message.match(GIT_REFERENCE_NOT_FOUND_REGEX))
167
+ .named_captures.fetch("name")
138
168
  end
139
- raise GitDependencyReferenceNotFound, name
169
+ raise GitDependencyReferenceNotFound, T.must(name)
140
170
  end
141
171
 
142
172
  if error.message.match?(GIT_DEPENDENCY_UNREACHABLE_REGEX)
143
- url = error.message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX)
144
- .named_captures.fetch("url")
145
- raise GitDependenciesNotReachable, url
173
+ url = T.must(error.message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX))
174
+ .named_captures.fetch("url")
175
+ raise GitDependenciesNotReachable, T.must(url)
146
176
  end
147
177
 
148
178
  raise unless error.message.include?("SolverProblemError") ||
@@ -163,6 +193,7 @@ module Dependabot
163
193
 
164
194
  # Using `--lock` avoids doing an install.
165
195
  # Using `--no-interaction` avoids asking for passwords.
196
+ sig { void }
166
197
  def run_poetry_update_command
167
198
  run_poetry_command(
168
199
  "pyenv exec poetry update #{dependency.name} --lock --no-interaction",
@@ -170,6 +201,7 @@ module Dependabot
170
201
  )
171
202
  end
172
203
 
204
+ sig { returns(T::Boolean) }
173
205
  def check_original_requirements_resolvable
174
206
  return @original_reqs_resolvable if @original_reqs_resolvable
175
207
 
@@ -189,11 +221,18 @@ module Dependabot
189
221
  end
190
222
  end
191
223
 
224
+ sig { params(message: String).returns(String) }
192
225
  def clean_error_message(message)
193
226
  # Redact any URLs, as they may include credentials
194
227
  message.gsub(/http.*?(?=\s)/, "<redacted>")
195
228
  end
196
229
 
230
+ sig do
231
+ params(
232
+ updated_req: T.nilable(String),
233
+ update_pyproject: T::Boolean
234
+ ).void
235
+ end
197
236
  def write_temporary_dependency_files(updated_req: nil,
198
237
  update_pyproject: true)
199
238
  dependency_files.each do |file|
@@ -216,14 +255,16 @@ module Dependabot
216
255
  end
217
256
  end
218
257
 
258
+ sig { void }
219
259
  def add_auth_env_vars
220
260
  Python::FileUpdater::PyprojectPreparer
221
- .new(pyproject_content: pyproject.content)
261
+ .new(pyproject_content: T.must(T.must(pyproject).content))
222
262
  .add_auth_env_vars(credentials)
223
263
  end
224
264
 
265
+ sig { params(updated_requirement: T.nilable(String)).returns(String) }
225
266
  def updated_pyproject_content(updated_requirement:)
226
- content = pyproject.content
267
+ content = T.must(T.must(pyproject).content)
227
268
  content = sanitize_pyproject_content(content)
228
269
  content = update_python_requirement(content)
229
270
  content = freeze_other_dependencies(content)
@@ -231,31 +272,36 @@ module Dependabot
231
272
  content
232
273
  end
233
274
 
275
+ sig { returns(String) }
234
276
  def sanitized_pyproject_content
235
- content = pyproject.content
277
+ content = T.must(T.must(pyproject).content)
236
278
  content = sanitize_pyproject_content(content)
237
279
  content = update_python_requirement(content)
238
280
  content
239
281
  end
240
282
 
283
+ sig { params(pyproject_content: String).returns(String) }
241
284
  def sanitize_pyproject_content(pyproject_content)
242
285
  Python::FileUpdater::PyprojectPreparer
243
286
  .new(pyproject_content: pyproject_content)
244
287
  .sanitize
245
288
  end
246
289
 
290
+ sig { params(pyproject_content: String).returns(String) }
247
291
  def update_python_requirement(pyproject_content)
248
292
  Python::FileUpdater::PyprojectPreparer
249
293
  .new(pyproject_content: pyproject_content)
250
294
  .update_python_requirement(language_version_manager.python_version)
251
295
  end
252
296
 
297
+ sig { params(pyproject_content: String).returns(String) }
253
298
  def freeze_other_dependencies(pyproject_content)
254
299
  Python::FileUpdater::PyprojectPreparer
255
300
  .new(pyproject_content: pyproject_content, lockfile: lockfile)
256
301
  .freeze_top_level_dependencies_except([dependency])
257
302
  end
258
303
 
304
+ sig { params(pyproject_content: String, updated_requirement: T.nilable(String)).returns(String) }
259
305
  def set_target_dependency_req(pyproject_content, updated_requirement)
260
306
  return pyproject_content unless updated_requirement
261
307
 
@@ -275,7 +321,7 @@ module Dependabot
275
321
  end
276
322
 
277
323
  # If this is a sub-dependency, add the new requirement
278
- unless dependency.requirements.find { |r| r[:file] == pyproject.name }
324
+ unless dependency.requirements.find { |r| r[:file] == T.must(pyproject).name }
279
325
  poetry_object[subdep_type] ||= {}
280
326
  poetry_object[subdep_type][dependency.name] = updated_requirement
281
327
  end
@@ -283,6 +329,7 @@ module Dependabot
283
329
  TomlRB.dump(pyproject_object)
284
330
  end
285
331
 
332
+ sig { params(toml_node: T::Hash[String, T.untyped], requirement: String).void }
286
333
  def update_dependency_requirement(toml_node, requirement)
287
334
  names = toml_node.keys
288
335
  pkg_name = names.find { |nm| normalise(nm) == dependency.name }
@@ -295,10 +342,12 @@ module Dependabot
295
342
  end
296
343
  end
297
344
 
345
+ sig { returns(String) }
298
346
  def subdep_type
299
347
  dependency.production? ? "dependencies" : "dev-dependencies"
300
348
  end
301
349
 
350
+ sig { returns(FileParser::PythonRequirementParser) }
302
351
  def python_requirement_parser
303
352
  @python_requirement_parser ||=
304
353
  FileParser::PythonRequirementParser.new(
@@ -306,6 +355,7 @@ module Dependabot
306
355
  )
307
356
  end
308
357
 
358
+ sig { returns(LanguageVersionManager) }
309
359
  def language_version_manager
310
360
  @language_version_manager ||=
311
361
  LanguageVersionManager.new(
@@ -313,22 +363,27 @@ module Dependabot
313
363
  )
314
364
  end
315
365
 
366
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
316
367
  def pyproject
317
368
  dependency_files.find { |f| f.name == "pyproject.toml" }
318
369
  end
319
370
 
371
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
320
372
  def poetry_lock
321
373
  dependency_files.find { |f| f.name == "poetry.lock" }
322
374
  end
323
375
 
376
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
324
377
  def lockfile
325
378
  poetry_lock
326
379
  end
327
380
 
381
+ sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
328
382
  def run_poetry_command(command, fingerprint: nil)
329
383
  SharedHelpers.run_shell_command(command, fingerprint: fingerprint)
330
384
  end
331
385
 
386
+ sig { params(name: String).returns(String) }
332
387
  def normalise(name)
333
388
  NameNormaliser.normalise(name)
334
389
  end
@@ -395,8 +450,8 @@ module Dependabot
395
450
  ).void
396
451
  end
397
452
  def initialize(dependencies:, dependency_files:)
398
- @dependencies = dependencies
399
- @dependency_files = dependency_files
453
+ @dependencies = T.let(dependencies, Dependabot::Dependency)
454
+ @dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
400
455
  end
401
456
 
402
457
  private