dependabot-python 0.332.0 → 0.333.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.
- checksums.yaml +4 -4
- data/lib/dependabot/python/file_fetcher.rb +164 -84
- data/lib/dependabot/python/file_parser/pipfile_files_parser.rb +3 -4
- data/lib/dependabot/python/file_parser/pyproject_files_parser.rb +4 -6
- data/lib/dependabot/python/file_parser/setup_file_parser.rb +2 -2
- data/lib/dependabot/python/file_parser.rb +2 -2
- data/lib/dependabot/python/file_updater/pip_compile_file_updater.rb +1 -0
- data/lib/dependabot/python/file_updater/pipfile_file_updater.rb +1 -0
- data/lib/dependabot/python/file_updater/pipfile_preparer.rb +1 -1
- data/lib/dependabot/python/language.rb +1 -0
- data/lib/dependabot/python/metadata_finder.rb +4 -1
- data/lib/dependabot/python/update_checker/requirements_updater.rb +1 -1
- data/lib/dependabot/python/update_checker.rb +138 -38
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb861b2b496cfd6879573eee03425670d74ec6fad92a93721b76f2d98392d8c9
|
4
|
+
data.tar.gz: ba2b3dbcbedb42506cbb78884340ee0f7a38a39d67f9093f27c8b45d84ffae9b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9048dbfa79c5754a2ddb7fe88b72974d414155c7206fa25d1b4db274802ef10de60dd04bb9ad40fab7e5337c70becfffbff99d2487f2425524d3e26b0d4f4792
|
7
|
+
data.tar.gz: b039ecae109ea713a2ef2669b3841ea8cb97a9e4854d1bf69ed93c993eb2240b89af5d9993d8dac92540da348dcb6e461d3e6f492df4e6fe081f3458f6fd8998
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "toml-rb"
|
@@ -12,9 +12,11 @@ require "dependabot/python/requirement_parser"
|
|
12
12
|
require "dependabot/python/file_parser/pyproject_files_parser"
|
13
13
|
require "dependabot/python/file_parser/python_requirement_parser"
|
14
14
|
require "dependabot/errors"
|
15
|
+
require "dependabot/file_filtering"
|
15
16
|
|
16
17
|
module Dependabot
|
17
18
|
module Python
|
19
|
+
# rubocop:disable Metrics/ClassLength
|
18
20
|
class FileFetcher < Dependabot::FileFetchers::Base
|
19
21
|
extend T::Sig
|
20
22
|
extend T::Helpers
|
@@ -23,6 +25,7 @@ module Dependabot
|
|
23
25
|
CONSTRAINT_REGEX = /^-c\s?(?<path>.*\.(?:txt|in))/
|
24
26
|
DEPENDENCY_TYPES = %w(packages dev-packages).freeze
|
25
27
|
|
28
|
+
sig { override.params(filenames: T::Array[String]).returns(T::Boolean) }
|
26
29
|
def self.required_files_in?(filenames)
|
27
30
|
return true if filenames.any? { |name| name.end_with?(".txt", ".in") }
|
28
31
|
|
@@ -40,11 +43,13 @@ module Dependabot
|
|
40
43
|
filenames.include?("setup.cfg")
|
41
44
|
end
|
42
45
|
|
46
|
+
sig { override.returns(String) }
|
43
47
|
def self.required_files_message
|
44
48
|
"Repo must contain a requirements.txt, setup.py, setup.cfg, pyproject.toml, " \
|
45
49
|
"or a Pipfile."
|
46
50
|
end
|
47
51
|
|
52
|
+
sig { override.returns(T::Hash[Symbol, T::Hash[Symbol, T::Hash[String, String]]]) }
|
48
53
|
def ecosystem_versions
|
49
54
|
# Hmm... it's weird that this calls file parser methods, but here we are in the file fetcher... for all
|
50
55
|
# ecosystems our goal is to extract the user specified versions, so we'll need to do file parsing... so should
|
@@ -84,25 +89,34 @@ module Dependabot
|
|
84
89
|
fetched_files << pip_conf if pip_conf
|
85
90
|
fetched_files << python_version_file if python_version_file
|
86
91
|
|
87
|
-
uniq_files(fetched_files)
|
92
|
+
uniques = uniq_files(fetched_files)
|
93
|
+
filtered_files = uniques.reject do |file|
|
94
|
+
Dependabot::FileFiltering.should_exclude_path?(file.name, "file from final collection", @exclude_paths)
|
95
|
+
end
|
96
|
+
|
97
|
+
filtered_files
|
88
98
|
end
|
89
99
|
|
90
100
|
private
|
91
101
|
|
102
|
+
sig { params(fetched_files: T::Array[Dependabot::DependencyFile]).returns(T::Array[Dependabot::DependencyFile]) }
|
92
103
|
def uniq_files(fetched_files)
|
93
104
|
uniq_files = fetched_files.reject(&:support_file?).uniq
|
94
105
|
uniq_files += fetched_files
|
95
106
|
.reject { |f| uniq_files.map(&:name).include?(f.name) }
|
96
107
|
end
|
97
108
|
|
109
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
98
110
|
def pipenv_files
|
99
111
|
[pipfile, pipfile_lock].compact
|
100
112
|
end
|
101
113
|
|
114
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
102
115
|
def pyproject_files
|
103
116
|
[pyproject, poetry_lock, pdm_lock].compact
|
104
117
|
end
|
105
118
|
|
119
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
106
120
|
def requirement_files
|
107
121
|
[
|
108
122
|
*requirements_txt_files,
|
@@ -111,114 +125,141 @@ module Dependabot
|
|
111
125
|
]
|
112
126
|
end
|
113
127
|
|
128
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
114
129
|
def setup_file
|
115
|
-
|
116
|
-
|
117
|
-
|
130
|
+
@setup_file ||= T.let(
|
131
|
+
fetch_file_if_present("setup.py"),
|
132
|
+
T.nilable(Dependabot::DependencyFile)
|
133
|
+
)
|
118
134
|
end
|
119
135
|
|
136
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
120
137
|
def setup_cfg_file
|
121
|
-
|
122
|
-
|
123
|
-
|
138
|
+
@setup_cfg_file ||= T.let(
|
139
|
+
fetch_file_if_present("setup.cfg"),
|
140
|
+
T.nilable(Dependabot::DependencyFile)
|
141
|
+
)
|
124
142
|
end
|
125
143
|
|
144
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
126
145
|
def pip_conf
|
127
|
-
|
128
|
-
|
129
|
-
|
146
|
+
@pip_conf ||= T.let(
|
147
|
+
fetch_support_file("pip.conf"),
|
148
|
+
T.nilable(Dependabot::DependencyFile)
|
149
|
+
)
|
130
150
|
end
|
131
151
|
|
152
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
132
153
|
def python_version_file
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
return @python_version_file if @python_version_file
|
138
|
-
return if [".", "/"].include?(directory)
|
154
|
+
@python_version_file ||= T.let(begin
|
155
|
+
file = fetch_support_file(".python-version")
|
156
|
+
return file if file
|
157
|
+
return if [".", "/"].include?(directory)
|
139
158
|
|
140
|
-
|
141
|
-
|
142
|
-
@python_version_file =
|
159
|
+
# Check the top-level for a .python-version file, too
|
160
|
+
reverse_path = Pathname.new(directory[0]).relative_path_from(directory)
|
143
161
|
fetch_support_file(File.join(reverse_path, ".python-version"))
|
144
|
-
|
162
|
+
&.tap { |f| f.name = ".python-version" }
|
163
|
+
end, T.nilable(Dependabot::DependencyFile))
|
145
164
|
end
|
146
165
|
|
166
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
147
167
|
def pipfile
|
148
|
-
|
149
|
-
|
150
|
-
|
168
|
+
@pipfile ||= T.let(
|
169
|
+
fetch_file_if_present("Pipfile"),
|
170
|
+
T.nilable(Dependabot::DependencyFile)
|
171
|
+
)
|
151
172
|
end
|
152
173
|
|
174
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
153
175
|
def pipfile_lock
|
154
|
-
|
155
|
-
|
156
|
-
|
176
|
+
@pipfile_lock ||= T.let(
|
177
|
+
fetch_file_if_present("Pipfile.lock"),
|
178
|
+
T.nilable(Dependabot::DependencyFile)
|
179
|
+
)
|
157
180
|
end
|
158
181
|
|
182
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
159
183
|
def pyproject
|
160
|
-
|
161
|
-
|
162
|
-
|
184
|
+
@pyproject ||= T.let(
|
185
|
+
fetch_file_if_present("pyproject.toml"),
|
186
|
+
T.nilable(Dependabot::DependencyFile)
|
187
|
+
)
|
163
188
|
end
|
164
189
|
|
190
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
165
191
|
def poetry_lock
|
166
|
-
|
167
|
-
|
168
|
-
|
192
|
+
@poetry_lock ||= T.let(
|
193
|
+
fetch_file_if_present("poetry.lock"),
|
194
|
+
T.nilable(Dependabot::DependencyFile)
|
195
|
+
)
|
169
196
|
end
|
170
197
|
|
198
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
171
199
|
def pdm_lock
|
172
|
-
|
173
|
-
|
174
|
-
|
200
|
+
@pdm_lock ||= T.let(
|
201
|
+
fetch_file_if_present("pdm.lock"),
|
202
|
+
T.nilable(Dependabot::DependencyFile)
|
203
|
+
)
|
175
204
|
end
|
176
205
|
|
206
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
177
207
|
def requirements_txt_files
|
178
208
|
req_txt_and_in_files.select { |f| f.name.end_with?(".txt") }
|
179
209
|
end
|
180
210
|
|
211
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
181
212
|
def requirements_in_files
|
182
213
|
req_txt_and_in_files.select { |f| f.name.end_with?(".in") } +
|
183
214
|
child_requirement_in_files
|
184
215
|
end
|
185
216
|
|
217
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
186
218
|
def parsed_pipfile
|
187
219
|
raise "No Pipfile" unless pipfile
|
188
220
|
|
189
|
-
@parsed_pipfile ||=
|
221
|
+
@parsed_pipfile ||= T.let(
|
222
|
+
TomlRB.parse(T.must(pipfile).content),
|
223
|
+
T.nilable(T::Hash[String, T.untyped])
|
224
|
+
)
|
190
225
|
rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
|
191
|
-
raise Dependabot::DependencyFileNotParseable, pipfile.path
|
226
|
+
raise Dependabot::DependencyFileNotParseable, T.must(pipfile).path
|
192
227
|
end
|
193
228
|
|
229
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
194
230
|
def parsed_pyproject
|
195
231
|
raise "No pyproject.toml" unless pyproject
|
196
232
|
|
197
|
-
@parsed_pyproject ||=
|
233
|
+
@parsed_pyproject ||= T.let(
|
234
|
+
TomlRB.parse(T.must(pyproject).content),
|
235
|
+
T.nilable(T::Hash[String, T.untyped])
|
236
|
+
)
|
198
237
|
rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
|
199
|
-
raise Dependabot::DependencyFileNotParseable, pyproject.path
|
238
|
+
raise Dependabot::DependencyFileNotParseable, T.must(pyproject).path
|
200
239
|
end
|
201
240
|
|
241
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
202
242
|
def req_txt_and_in_files
|
203
|
-
|
243
|
+
@req_txt_and_in_files ||= T.let(begin
|
244
|
+
files = T.let([], T::Array[Dependabot::DependencyFile])
|
204
245
|
|
205
|
-
|
246
|
+
repo_contents
|
247
|
+
.select { |f| f.type == "file" }
|
248
|
+
.select { |f| f.name.end_with?(".txt", ".in") }
|
249
|
+
.reject { |f| f.size > 500_000 }
|
250
|
+
.map { |f| fetch_file_from_host(f.name) }
|
251
|
+
.select { |f| requirements_file?(f) }
|
252
|
+
.each { |f| files << f }
|
206
253
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
.reject { |f| f.size > 500_000 }
|
211
|
-
.map { |f| fetch_file_from_host(f.name) }
|
212
|
-
.select { |f| requirements_file?(f) }
|
213
|
-
.each { |f| @req_txt_and_in_files << f }
|
254
|
+
repo_contents
|
255
|
+
.select { |f| f.type == "dir" }
|
256
|
+
.each { |f| files.concat(req_files_for_dir(f)) }
|
214
257
|
|
215
|
-
|
216
|
-
|
217
|
-
.each { |f| @req_txt_and_in_files += req_files_for_dir(f) }
|
218
|
-
|
219
|
-
@req_txt_and_in_files
|
258
|
+
files
|
259
|
+
end, T.nilable(T::Array[Dependabot::DependencyFile]))
|
220
260
|
end
|
221
261
|
|
262
|
+
sig { params(requirements_dir: T.untyped).returns(T::Array[Dependabot::DependencyFile]) }
|
222
263
|
def req_files_for_dir(requirements_dir)
|
223
264
|
dir = directory.gsub(%r{(^/|/$)}, "")
|
224
265
|
relative_reqs_dir =
|
@@ -232,32 +273,41 @@ module Dependabot
|
|
232
273
|
.select { |f| requirements_file?(f) }
|
233
274
|
end
|
234
275
|
|
276
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
235
277
|
def child_requirement_txt_files
|
236
278
|
child_requirement_files.select { |f| f.name.end_with?(".txt") }
|
237
279
|
end
|
238
280
|
|
281
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
239
282
|
def child_requirement_in_files
|
240
283
|
child_requirement_files.select { |f| f.name.end_with?(".in") }
|
241
284
|
end
|
242
285
|
|
286
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
243
287
|
def child_requirement_files
|
244
|
-
@child_requirement_files ||=
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
child_files
|
255
|
-
end
|
288
|
+
@child_requirement_files ||= T.let(begin
|
289
|
+
fetched_files = req_txt_and_in_files.dup
|
290
|
+
req_txt_and_in_files.flat_map do |requirement_file|
|
291
|
+
child_files = fetch_child_requirement_files(
|
292
|
+
file: requirement_file,
|
293
|
+
previously_fetched_files: fetched_files
|
294
|
+
)
|
295
|
+
|
296
|
+
fetched_files += child_files
|
297
|
+
child_files
|
256
298
|
end
|
299
|
+
end, T.nilable(T::Array[Dependabot::DependencyFile]))
|
257
300
|
end
|
258
301
|
|
302
|
+
sig do
|
303
|
+
params(
|
304
|
+
file: Dependabot::DependencyFile,
|
305
|
+
previously_fetched_files: T::Array[Dependabot::DependencyFile]
|
306
|
+
)
|
307
|
+
.returns(T::Array[Dependabot::DependencyFile])
|
308
|
+
end
|
259
309
|
def fetch_child_requirement_files(file:, previously_fetched_files:)
|
260
|
-
paths = file.content.scan(CHILD_REQUIREMENT_REGEX).flatten
|
310
|
+
paths = T.must(file.content).scan(CHILD_REQUIREMENT_REGEX).flatten
|
261
311
|
current_dir = File.dirname(file.name)
|
262
312
|
|
263
313
|
paths.flat_map do |path|
|
@@ -267,6 +317,14 @@ module Dependabot
|
|
267
317
|
next if previously_fetched_files.map(&:name).include?(path)
|
268
318
|
next if file.name == path
|
269
319
|
|
320
|
+
if Dependabot::Experiments.enabled?(:enable_exclude_paths_subdirectory_manifest_files) &&
|
321
|
+
!@exclude_paths.empty? && Dependabot::FileFiltering.exclude_path?(path, @exclude_paths)
|
322
|
+
raise Dependabot::DependencyFileNotEvaluatable,
|
323
|
+
"Cannot process requirements: '#{file.name}' references excluded file '#{path}'. " \
|
324
|
+
"Please either remove the reference from '#{file.name}' " \
|
325
|
+
"or update your exclude_paths configuration."
|
326
|
+
end
|
327
|
+
|
270
328
|
fetched_file = fetch_file_from_host(path)
|
271
329
|
grandchild_requirement_files = fetch_child_requirement_files(
|
272
330
|
file: fetched_file,
|
@@ -276,13 +334,14 @@ module Dependabot
|
|
276
334
|
end.compact
|
277
335
|
end
|
278
336
|
|
337
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
279
338
|
def constraints_files
|
280
339
|
all_requirement_files = requirements_txt_files +
|
281
340
|
child_requirement_txt_files
|
282
341
|
|
283
342
|
constraints_paths = all_requirement_files.map do |req_file|
|
284
343
|
current_dir = File.dirname(req_file.name)
|
285
|
-
paths = req_file.content.scan(CONSTRAINT_REGEX).flatten
|
344
|
+
paths = T.must(req_file.content).scan(CONSTRAINT_REGEX).flatten
|
286
345
|
|
287
346
|
paths.map do |path|
|
288
347
|
path = File.join(current_dir, path) unless current_dir == "."
|
@@ -293,15 +352,16 @@ module Dependabot
|
|
293
352
|
constraints_paths.map { |path| fetch_file_from_host(path) }
|
294
353
|
end
|
295
354
|
|
355
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
296
356
|
def project_files
|
297
357
|
project_files = T.let([], T::Array[Dependabot::DependencyFile])
|
298
358
|
unfetchable_deps = []
|
299
359
|
|
300
360
|
path_dependencies.each do |dep|
|
301
|
-
path = dep[:path]
|
361
|
+
path = T.must(dep[:path])
|
302
362
|
project_files += fetch_project_file(path)
|
303
363
|
rescue Dependabot::DependencyFileNotFound => e
|
304
|
-
unfetchable_deps << if sdist_or_wheel?(path)
|
364
|
+
unfetchable_deps << if sdist_or_wheel?(T.must(path))
|
305
365
|
e.file_path&.gsub(%r{^/}, "")
|
306
366
|
else
|
307
367
|
"\"#{dep[:name]}\" at #{cleanpath(File.join(directory, dep[:file]))}"
|
@@ -319,6 +379,7 @@ module Dependabot
|
|
319
379
|
project_files
|
320
380
|
end
|
321
381
|
|
382
|
+
sig { params(path: String).returns(T::Array[Dependabot::DependencyFile]) }
|
322
383
|
def fetch_project_file(path)
|
323
384
|
project_files = []
|
324
385
|
|
@@ -346,10 +407,12 @@ module Dependabot
|
|
346
407
|
project_files + cfg_files_for_setup_py(path)
|
347
408
|
end
|
348
409
|
|
410
|
+
sig { params(path: String).returns(T::Boolean) }
|
349
411
|
def sdist_or_wheel?(path)
|
350
412
|
path.end_with?(".tar.gz", ".whl", ".zip")
|
351
413
|
end
|
352
414
|
|
415
|
+
sig { params(path: String).returns(T::Array[Dependabot::DependencyFile]) }
|
353
416
|
def cfg_files_for_setup_py(path)
|
354
417
|
cfg_path = path.gsub(/\.py$/, ".cfg")
|
355
418
|
|
@@ -364,11 +427,12 @@ module Dependabot
|
|
364
427
|
end
|
365
428
|
end
|
366
429
|
|
430
|
+
sig { params(file: Dependabot::DependencyFile).returns(T::Boolean) }
|
367
431
|
def requirements_file?(file)
|
368
|
-
return false unless file.content.valid_encoding?
|
432
|
+
return false unless T.must(file.content).valid_encoding?
|
369
433
|
return true if file.name.match?(/requirements/x)
|
370
434
|
|
371
|
-
file.content.lines.all? do |line|
|
435
|
+
T.must(file.content).lines.all? do |line|
|
372
436
|
next true if line.strip.empty?
|
373
437
|
next true if line.strip.start_with?("#", "-r ", "-c ", "-e ", "--")
|
374
438
|
|
@@ -376,45 +440,54 @@ module Dependabot
|
|
376
440
|
end
|
377
441
|
end
|
378
442
|
|
443
|
+
sig { returns(T::Array[T::Hash[Symbol, String]]) }
|
379
444
|
def path_dependencies
|
380
445
|
requirement_txt_path_dependencies +
|
381
446
|
requirement_in_path_dependencies +
|
382
447
|
pipfile_path_dependencies
|
383
448
|
end
|
384
449
|
|
450
|
+
sig { returns(T::Array[T::Hash[Symbol, String]]) }
|
385
451
|
def requirement_txt_path_dependencies
|
386
452
|
(requirements_txt_files + child_requirement_txt_files)
|
387
453
|
.map { |req_file| parse_requirement_path_dependencies(req_file) }
|
388
454
|
.flatten.uniq { |dep| dep[:path] }
|
389
455
|
end
|
390
456
|
|
457
|
+
sig { returns(T::Array[T::Hash[Symbol, String]]) }
|
391
458
|
def requirement_in_path_dependencies
|
392
459
|
requirements_in_files
|
393
460
|
.map { |req_file| parse_requirement_path_dependencies(req_file) }
|
394
461
|
.flatten.uniq { |dep| dep[:path] }
|
395
462
|
end
|
396
463
|
|
464
|
+
sig { params(req_file: Dependabot::DependencyFile).returns(T::Array[T::Hash[Symbol, String]]) }
|
397
465
|
def parse_requirement_path_dependencies(req_file)
|
398
466
|
# If this is a pip-compile lockfile, rely on whatever path dependencies we found in the main manifest
|
399
467
|
return [] if pip_compile_file_matcher.lockfile_for_pip_compile_file?(req_file)
|
400
468
|
|
401
469
|
uneditable_reqs =
|
402
|
-
req_file.content
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
470
|
+
T.must(req_file.content)
|
471
|
+
.scan(/(?<name>^['"]?(?:file:)?(?<path>\..*?)(?=\[|#|'|"|$))/)
|
472
|
+
.filter_map do |match_array|
|
473
|
+
n, p = match_array
|
474
|
+
{ name: n.to_s.strip, path: p.to_s.strip, file: req_file.name } unless p.to_s.include?("://")
|
475
|
+
end
|
407
476
|
|
408
477
|
editable_reqs =
|
409
|
-
req_file.content
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
478
|
+
T.must(req_file.content)
|
479
|
+
.scan(/(?<name>^(?:-e)\s+['"]?(?:file:)?(?<path>.*?)(?=\[|#|'|"|$))/)
|
480
|
+
.filter_map do |match_array|
|
481
|
+
n, p = match_array
|
482
|
+
unless p.to_s.include?("://") || p.to_s.include?("git@")
|
483
|
+
{ name: n.to_s.strip, path: p.to_s.strip, file: req_file.name }
|
484
|
+
end
|
485
|
+
end
|
414
486
|
|
415
487
|
uneditable_reqs + editable_reqs
|
416
488
|
end
|
417
489
|
|
490
|
+
sig { returns(T::Array[T::Hash[Symbol, String]]) }
|
418
491
|
def pipfile_path_dependencies
|
419
492
|
return [] unless pipfile
|
420
493
|
|
@@ -425,13 +498,14 @@ module Dependabot
|
|
425
498
|
parsed_pipfile[dep_type].each do |_, req|
|
426
499
|
next unless req.is_a?(Hash) && req["path"]
|
427
500
|
|
428
|
-
deps << { name: req["path"], path: req["path"], file: pipfile.name }
|
501
|
+
deps << { name: req["path"], path: req["path"], file: T.must(pipfile).name }
|
429
502
|
end
|
430
503
|
end
|
431
504
|
|
432
505
|
deps
|
433
506
|
end
|
434
507
|
|
508
|
+
sig { returns(T::Array[String]) }
|
435
509
|
def poetry_path_dependencies
|
436
510
|
return [] unless pyproject
|
437
511
|
|
@@ -449,14 +523,20 @@ module Dependabot
|
|
449
523
|
paths
|
450
524
|
end
|
451
525
|
|
526
|
+
sig { params(path: String).returns(String) }
|
452
527
|
def cleanpath(path)
|
453
528
|
Pathname.new(path).cleanpath.to_path
|
454
529
|
end
|
455
530
|
|
531
|
+
sig { returns(Dependabot::Python::PipCompileFileMatcher) }
|
456
532
|
def pip_compile_file_matcher
|
457
|
-
@pip_compile_file_matcher ||=
|
533
|
+
@pip_compile_file_matcher ||= T.let(
|
534
|
+
PipCompileFileMatcher.new(requirements_in_files),
|
535
|
+
T.nilable(Dependabot::Python::PipCompileFileMatcher)
|
536
|
+
)
|
458
537
|
end
|
459
538
|
end
|
539
|
+
# rubocop:enable Metrics/ClassLength
|
460
540
|
end
|
461
541
|
end
|
462
542
|
|
@@ -14,6 +14,7 @@ module Dependabot
|
|
14
14
|
class FileParser
|
15
15
|
class PipfileFilesParser
|
16
16
|
extend T::Sig
|
17
|
+
|
17
18
|
DEPENDENCY_GROUP_KEYS = T.let([
|
18
19
|
{
|
19
20
|
pipfile: "packages",
|
@@ -73,8 +74,7 @@ module Dependabot
|
|
73
74
|
groups: [group]
|
74
75
|
}],
|
75
76
|
package_manager: "pip",
|
76
|
-
metadata: { original_name: dep_name }
|
77
|
-
origin_files: [T.must(pipfile).name]
|
77
|
+
metadata: { original_name: dep_name }
|
78
78
|
)
|
79
79
|
end
|
80
80
|
end
|
@@ -107,8 +107,7 @@ module Dependabot
|
|
107
107
|
version: version&.gsub(/^===?/, ""),
|
108
108
|
requirements: [],
|
109
109
|
package_manager: "pip",
|
110
|
-
subdependency_metadata: [{ production: key != "develop" }]
|
111
|
-
origin_files: [T.must(pipfile_lock).name]
|
110
|
+
subdependency_metadata: [{ production: key != "develop" }]
|
112
111
|
)
|
113
112
|
end
|
114
113
|
end
|
@@ -15,6 +15,7 @@ module Dependabot
|
|
15
15
|
class FileParser
|
16
16
|
class PyprojectFilesParser
|
17
17
|
extend T::Sig
|
18
|
+
|
18
19
|
POETRY_DEPENDENCY_TYPES = %w(dependencies dev-dependencies).freeze
|
19
20
|
|
20
21
|
# https://python-poetry.org/docs/dependency-specification/
|
@@ -98,8 +99,7 @@ module Dependabot
|
|
98
99
|
source: nil,
|
99
100
|
groups: [dep["requirement_type"]].compact
|
100
101
|
}],
|
101
|
-
package_manager: "pip"
|
102
|
-
origin_files: [Pathname.new(dep["file"]).cleanpath.to_path]
|
102
|
+
package_manager: "pip"
|
103
103
|
)
|
104
104
|
end
|
105
105
|
|
@@ -124,8 +124,7 @@ module Dependabot
|
|
124
124
|
name: normalise(name),
|
125
125
|
version: version_from_lockfile(name),
|
126
126
|
requirements: requirements,
|
127
|
-
package_manager: "pip"
|
128
|
-
origin_files: [T.must(pyproject).name]
|
127
|
+
package_manager: "pip"
|
129
128
|
)
|
130
129
|
end
|
131
130
|
dependencies
|
@@ -210,8 +209,7 @@ module Dependabot
|
|
210
209
|
package_manager: "pip",
|
211
210
|
subdependency_metadata: [{
|
212
211
|
production: production_dependency_names.include?(name)
|
213
|
-
}]
|
214
|
-
origin_files: [lockfile.name]
|
212
|
+
}]
|
215
213
|
)
|
216
214
|
end
|
217
215
|
|
@@ -15,6 +15,7 @@ module Dependabot
|
|
15
15
|
class FileParser
|
16
16
|
class SetupFileParser
|
17
17
|
extend T::Sig
|
18
|
+
|
18
19
|
INSTALL_REQUIRES_REGEX = /install_requires\s*=\s*\[/m
|
19
20
|
SETUP_REQUIRES_REGEX = /setup_requires\s*=\s*\[/m
|
20
21
|
TESTS_REQUIRE_REGEX = /tests_require\s*=\s*\[/m
|
@@ -50,8 +51,7 @@ module Dependabot
|
|
50
51
|
source: nil,
|
51
52
|
groups: [dep["requirement_type"]]
|
52
53
|
}],
|
53
|
-
package_manager: "pip"
|
54
|
-
origin_files: [Pathname.new(dep["file"]).cleanpath.to_path]
|
54
|
+
package_manager: "pip"
|
55
55
|
)
|
56
56
|
end
|
57
57
|
dependencies
|
@@ -19,6 +19,7 @@ module Dependabot
|
|
19
19
|
module Python
|
20
20
|
class FileParser < Dependabot::FileParsers::Base # rubocop:disable Metrics/ClassLength
|
21
21
|
extend T::Sig
|
22
|
+
|
22
23
|
require_relative "file_parser/pipfile_files_parser"
|
23
24
|
require_relative "file_parser/pyproject_files_parser"
|
24
25
|
require_relative "file_parser/setup_file_parser"
|
@@ -281,8 +282,7 @@ module Dependabot
|
|
281
282
|
name: normalised_name(name, dep["extras"]),
|
282
283
|
version: version&.include?("*") ? nil : version,
|
283
284
|
requirements: requirements,
|
284
|
-
package_manager: "pip"
|
285
|
-
origin_files: [Pathname.new(file).cleanpath.to_path]
|
285
|
+
package_manager: "pip"
|
286
286
|
)
|
287
287
|
end
|
288
288
|
dependencies
|
@@ -57,7 +57,7 @@ module Dependabot
|
|
57
57
|
|
58
58
|
# provide a default "true" value to file generator in case no value is provided in manifest file
|
59
59
|
pipfile_object["source"].each do |key|
|
60
|
-
key["verify_ssl"] = verify_ssl.nil?
|
60
|
+
key["verify_ssl"] = verify_ssl.nil? || verify_ssl
|
61
61
|
end
|
62
62
|
|
63
63
|
TomlRB.dump(pipfile_object)
|
@@ -11,6 +11,7 @@ module Dependabot
|
|
11
11
|
|
12
12
|
class Language < Dependabot::Ecosystem::VersionManager
|
13
13
|
extend T::Sig
|
14
|
+
|
14
15
|
# This list must match the versions specified at the top of `python/Dockerfile`
|
15
16
|
# ARG PY_3_13=3.13.2
|
16
17
|
# When updating this list, also update uv/lib/dependabot/uv/language.rb
|
@@ -14,6 +14,7 @@ module Dependabot
|
|
14
14
|
module Python
|
15
15
|
class MetadataFinder < Dependabot::MetadataFinders::Base
|
16
16
|
extend T::Sig
|
17
|
+
|
17
18
|
MAIN_PYPI_URL = "https://pypi.org/pypi"
|
18
19
|
|
19
20
|
sig do
|
@@ -196,7 +197,9 @@ module Dependabot
|
|
196
197
|
.map { |c| AuthedUrlBuilder.authed_url(credential: c) }
|
197
198
|
|
198
199
|
(credential_urls + [MAIN_PYPI_URL]).map do |base_url|
|
199
|
-
|
200
|
+
# Convert /simple/ endpoints to /pypi/ for JSON API access
|
201
|
+
json_base_url = base_url.sub(%r{/simple/?$}i, "/pypi")
|
202
|
+
json_base_url.gsub(%r{/$}, "") + "/#{normalised_dependency_name}/json"
|
200
203
|
end
|
201
204
|
end
|
202
205
|
|
@@ -389,7 +389,7 @@ module Dependabot
|
|
389
389
|
elsif req_string.strip.start_with?("~=", "==")
|
390
390
|
version.segments.count - 2
|
391
391
|
elsif req_string.strip.start_with?("~")
|
392
|
-
req_string.split(".").
|
392
|
+
req_string.split(".").one? ? 0 : 1
|
393
393
|
else
|
394
394
|
raise "Don't know how to convert #{req_string} to range"
|
395
395
|
end
|
@@ -1,8 +1,9 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "excon"
|
5
5
|
require "toml-rb"
|
6
|
+
require "sorbet-runtime"
|
6
7
|
|
7
8
|
require "dependabot/dependency"
|
8
9
|
require "dependabot/errors"
|
@@ -17,6 +18,8 @@ require "dependabot/update_checkers/base"
|
|
17
18
|
module Dependabot
|
18
19
|
module Python
|
19
20
|
class UpdateChecker < Dependabot::UpdateCheckers::Base
|
21
|
+
extend T::Sig
|
22
|
+
|
20
23
|
require_relative "update_checker/poetry_version_resolver"
|
21
24
|
require_relative "update_checker/pipenv_version_resolver"
|
22
25
|
require_relative "update_checker/pip_compile_version_resolver"
|
@@ -30,12 +33,17 @@ module Dependabot
|
|
30
33
|
).freeze
|
31
34
|
VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-_]+)*/
|
32
35
|
|
36
|
+
sig { override.returns(T.nilable(Gem::Version)) }
|
33
37
|
def latest_version
|
34
|
-
@latest_version ||=
|
38
|
+
@latest_version ||= T.let(
|
39
|
+
fetch_latest_version,
|
40
|
+
T.nilable(Gem::Version)
|
41
|
+
)
|
35
42
|
end
|
36
43
|
|
44
|
+
sig { override.returns(T.nilable(Gem::Version)) }
|
37
45
|
def latest_resolvable_version
|
38
|
-
@latest_resolvable_version ||=
|
46
|
+
@latest_resolvable_version ||= T.let(
|
39
47
|
if resolver_type == :requirements
|
40
48
|
resolver.latest_resolvable_version
|
41
49
|
elsif resolver_type == :pip_compile && resolver.resolvable?(version: latest_version)
|
@@ -44,33 +52,41 @@ module Dependabot
|
|
44
52
|
resolver.latest_resolvable_version(
|
45
53
|
requirement: unlocked_requirement_string
|
46
54
|
)
|
47
|
-
end
|
55
|
+
end,
|
56
|
+
T.nilable(Gem::Version)
|
57
|
+
)
|
48
58
|
end
|
49
59
|
|
60
|
+
sig { override.returns(T.nilable(Gem::Version)) }
|
50
61
|
def latest_resolvable_version_with_no_unlock
|
51
|
-
@latest_resolvable_version_with_no_unlock ||=
|
62
|
+
@latest_resolvable_version_with_no_unlock ||= T.let(
|
52
63
|
if resolver_type == :requirements
|
53
64
|
resolver.latest_resolvable_version_with_no_unlock
|
54
65
|
else
|
55
66
|
resolver.latest_resolvable_version(
|
56
67
|
requirement: current_requirement_string
|
57
68
|
)
|
58
|
-
end
|
69
|
+
end,
|
70
|
+
T.nilable(Gem::Version)
|
71
|
+
)
|
59
72
|
end
|
60
73
|
|
74
|
+
sig { override.returns(T.nilable(Gem::Version)) }
|
61
75
|
def lowest_security_fix_version
|
62
76
|
latest_version_finder.lowest_security_fix_version
|
63
77
|
end
|
64
78
|
|
79
|
+
sig { override.returns(T.nilable(Gem::Version)) }
|
65
80
|
def lowest_resolvable_security_fix_version
|
66
81
|
raise "Dependency not vulnerable!" unless vulnerable?
|
67
82
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
83
|
+
@lowest_resolvable_security_fix_version ||= T.let(
|
84
|
+
fetch_lowest_resolvable_security_fix_version,
|
85
|
+
T.nilable(Gem::Version)
|
86
|
+
)
|
72
87
|
end
|
73
88
|
|
89
|
+
sig { override.returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
74
90
|
def updated_requirements
|
75
91
|
RequirementsUpdater.new(
|
76
92
|
requirements: requirements,
|
@@ -80,10 +96,12 @@ module Dependabot
|
|
80
96
|
).updated_requirements
|
81
97
|
end
|
82
98
|
|
99
|
+
sig { override.returns(T::Boolean) }
|
83
100
|
def requirements_unlocked_or_can_be?
|
84
101
|
!requirements_update_strategy.lockfile_only?
|
85
102
|
end
|
86
103
|
|
104
|
+
sig { override.returns(Dependabot::RequirementsUpdateStrategy) }
|
87
105
|
def requirements_update_strategy
|
88
106
|
# If passed in as an option (in the base class) honour that option
|
89
107
|
return @requirements_update_strategy if @requirements_update_strategy
|
@@ -94,15 +112,18 @@ module Dependabot
|
|
94
112
|
|
95
113
|
private
|
96
114
|
|
115
|
+
sig { override.returns(T::Boolean) }
|
97
116
|
def latest_version_resolvable_with_full_unlock?
|
98
117
|
# Full unlock checks aren't implemented for Python (yet)
|
99
118
|
false
|
100
119
|
end
|
101
120
|
|
121
|
+
sig { override.returns(T::Array[Dependabot::Dependency]) }
|
102
122
|
def updated_dependencies_after_full_unlock
|
103
123
|
raise NotImplementedError
|
104
124
|
end
|
105
125
|
|
126
|
+
sig { returns(T.nilable(Gem::Version)) }
|
106
127
|
def fetch_lowest_resolvable_security_fix_version
|
107
128
|
fix_version = lowest_security_fix_version
|
108
129
|
return latest_resolvable_version if fix_version.nil?
|
@@ -112,6 +133,7 @@ module Dependabot
|
|
112
133
|
resolver.resolvable?(version: fix_version) ? fix_version : nil
|
113
134
|
end
|
114
135
|
|
136
|
+
sig { returns(T.untyped) }
|
115
137
|
def resolver
|
116
138
|
if Dependabot::Experiments.enabled?(:enable_file_parser_python_local)
|
117
139
|
Dependabot.logger.info("Python package resolver : #{resolver_type}")
|
@@ -126,6 +148,7 @@ module Dependabot
|
|
126
148
|
end
|
127
149
|
end
|
128
150
|
|
151
|
+
sig { returns(Symbol) }
|
129
152
|
def resolver_type
|
130
153
|
reqs = requirements
|
131
154
|
|
@@ -147,6 +170,7 @@ module Dependabot
|
|
147
170
|
end
|
148
171
|
end
|
149
172
|
|
173
|
+
sig { returns(Symbol) }
|
150
174
|
def subdependency_resolver
|
151
175
|
return :pipenv if pipfile_lock
|
152
176
|
return :poetry if poetry_lock
|
@@ -155,12 +179,14 @@ module Dependabot
|
|
155
179
|
raise "Claimed to be a sub-dependency, but no lockfile exists!"
|
156
180
|
end
|
157
181
|
|
182
|
+
sig { returns(Symbol) }
|
158
183
|
def pyproject_resolver
|
159
184
|
return :poetry if poetry_based?
|
160
185
|
|
161
186
|
:requirements
|
162
187
|
end
|
163
188
|
|
189
|
+
sig { params(reqs: T::Array[T::Hash[Symbol, T.untyped]]).returns(T::Boolean) }
|
164
190
|
def exact_requirement?(reqs)
|
165
191
|
reqs = reqs.map { |r| r.fetch(:requirement) }
|
166
192
|
reqs = reqs.compact
|
@@ -168,31 +194,62 @@ module Dependabot
|
|
168
194
|
reqs.any? { |r| Python::Requirement.new(r).exact? }
|
169
195
|
end
|
170
196
|
|
197
|
+
sig { returns(PipenvVersionResolver) }
|
171
198
|
def pipenv_version_resolver
|
172
|
-
@pipenv_version_resolver ||=
|
199
|
+
@pipenv_version_resolver ||= T.let(
|
200
|
+
PipenvVersionResolver.new(
|
201
|
+
dependency: dependency,
|
202
|
+
dependency_files: dependency_files,
|
203
|
+
credentials: credentials,
|
204
|
+
repo_contents_path: repo_contents_path
|
205
|
+
),
|
206
|
+
T.nilable(PipenvVersionResolver)
|
207
|
+
)
|
173
208
|
end
|
174
209
|
|
210
|
+
sig { returns(PipCompileVersionResolver) }
|
175
211
|
def pip_compile_version_resolver
|
176
|
-
@pip_compile_version_resolver ||=
|
177
|
-
PipCompileVersionResolver.new(
|
212
|
+
@pip_compile_version_resolver ||= T.let(
|
213
|
+
PipCompileVersionResolver.new(
|
214
|
+
dependency: dependency,
|
215
|
+
dependency_files: dependency_files,
|
216
|
+
credentials: credentials,
|
217
|
+
repo_contents_path: repo_contents_path
|
218
|
+
),
|
219
|
+
T.nilable(PipCompileVersionResolver)
|
220
|
+
)
|
178
221
|
end
|
179
222
|
|
223
|
+
sig { returns(PoetryVersionResolver) }
|
180
224
|
def poetry_version_resolver
|
181
|
-
@poetry_version_resolver ||=
|
225
|
+
@poetry_version_resolver ||= T.let(
|
226
|
+
PoetryVersionResolver.new(
|
227
|
+
dependency: dependency,
|
228
|
+
dependency_files: dependency_files,
|
229
|
+
credentials: credentials,
|
230
|
+
repo_contents_path: repo_contents_path
|
231
|
+
),
|
232
|
+
T.nilable(PoetryVersionResolver)
|
233
|
+
)
|
182
234
|
end
|
183
235
|
|
236
|
+
sig { returns(PipVersionResolver) }
|
184
237
|
def pip_version_resolver
|
185
|
-
@pip_version_resolver ||=
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
238
|
+
@pip_version_resolver ||= T.let(
|
239
|
+
PipVersionResolver.new(
|
240
|
+
dependency: dependency,
|
241
|
+
dependency_files: dependency_files,
|
242
|
+
credentials: credentials,
|
243
|
+
ignored_versions: ignored_versions,
|
244
|
+
update_cooldown: @update_cooldown,
|
245
|
+
raise_on_ignored: @raise_on_ignored,
|
246
|
+
security_advisories: security_advisories
|
247
|
+
),
|
248
|
+
T.nilable(PipVersionResolver)
|
193
249
|
)
|
194
250
|
end
|
195
251
|
|
252
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
196
253
|
def resolver_args
|
197
254
|
{
|
198
255
|
dependency: dependency,
|
@@ -202,6 +259,7 @@ module Dependabot
|
|
202
259
|
}
|
203
260
|
end
|
204
261
|
|
262
|
+
sig { returns(T.nilable(String)) }
|
205
263
|
def current_requirement_string
|
206
264
|
reqs = requirements
|
207
265
|
return if reqs.none?
|
@@ -215,6 +273,7 @@ module Dependabot
|
|
215
273
|
requirement&.fetch(:requirement)
|
216
274
|
end
|
217
275
|
|
276
|
+
sig { returns(String) }
|
218
277
|
def unlocked_requirement_string
|
219
278
|
lower_bound_req = updated_version_req_lower_bound
|
220
279
|
|
@@ -231,6 +290,7 @@ module Dependabot
|
|
231
290
|
lower_bound_req + ",<=#{latest_version}"
|
232
291
|
end
|
233
292
|
|
293
|
+
sig { returns(String) }
|
234
294
|
def updated_version_req_lower_bound
|
235
295
|
return ">=#{dependency.version}" if dependency.version
|
236
296
|
|
@@ -245,111 +305,151 @@ module Dependabot
|
|
245
305
|
">=#{version_for_requirement || 0}"
|
246
306
|
end
|
247
307
|
|
308
|
+
sig { returns(T.nilable(Gem::Version)) }
|
248
309
|
def fetch_latest_version
|
249
310
|
latest_version_finder.latest_version
|
250
311
|
end
|
251
312
|
|
313
|
+
sig { returns(LatestVersionFinder) }
|
252
314
|
def latest_version_finder
|
253
|
-
@latest_version_finder ||=
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
315
|
+
@latest_version_finder ||= T.let(
|
316
|
+
LatestVersionFinder.new(
|
317
|
+
dependency: dependency,
|
318
|
+
dependency_files: dependency_files,
|
319
|
+
credentials: credentials,
|
320
|
+
ignored_versions: ignored_versions,
|
321
|
+
raise_on_ignored: @raise_on_ignored,
|
322
|
+
cooldown_options: @update_cooldown,
|
323
|
+
security_advisories: security_advisories
|
324
|
+
),
|
325
|
+
T.nilable(LatestVersionFinder)
|
261
326
|
)
|
262
327
|
end
|
263
328
|
|
329
|
+
sig { returns(T::Boolean) }
|
264
330
|
def poetry_based?
|
265
331
|
updating_pyproject? && !poetry_details.nil?
|
266
332
|
end
|
267
333
|
|
334
|
+
sig { returns(T::Boolean) }
|
268
335
|
def library?
|
269
336
|
return false unless updating_pyproject?
|
337
|
+
return false unless library_details
|
270
338
|
|
271
|
-
return false if library_details["name"].nil?
|
339
|
+
return false if T.must(library_details)["name"].nil?
|
272
340
|
|
273
341
|
# Hit PyPi and check whether there are details for a library with a
|
274
342
|
# matching name and description
|
275
343
|
index_response = Dependabot::RegistryClient.get(
|
276
|
-
url: "https://pypi.org/pypi/#{normalised_name(library_details['name'])}/json/"
|
344
|
+
url: "https://pypi.org/pypi/#{normalised_name(T.must(library_details)['name'])}/json/"
|
277
345
|
)
|
278
346
|
|
279
347
|
return false unless index_response.status == 200
|
280
348
|
|
281
349
|
pypi_info = JSON.parse(index_response.body)["info"] || {}
|
282
|
-
pypi_info["summary"] == library_details["description"]
|
350
|
+
pypi_info["summary"] == T.must(library_details)["description"]
|
283
351
|
rescue Excon::Error::Timeout, Excon::Error::Socket
|
284
352
|
false
|
285
353
|
rescue URI::InvalidURIError
|
286
354
|
false
|
287
355
|
end
|
288
356
|
|
357
|
+
sig { returns(T::Boolean) }
|
289
358
|
def updating_pipfile?
|
290
359
|
requirement_files.any?("Pipfile")
|
291
360
|
end
|
292
361
|
|
362
|
+
sig { returns(T::Boolean) }
|
293
363
|
def updating_pyproject?
|
294
364
|
requirement_files.any?("pyproject.toml")
|
295
365
|
end
|
296
366
|
|
367
|
+
sig { returns(T::Boolean) }
|
297
368
|
def updating_in_file?
|
298
369
|
requirement_files.any? { |f| f.end_with?(".in") }
|
299
370
|
end
|
300
371
|
|
372
|
+
sig { returns(T::Boolean) }
|
301
373
|
def updating_requirements_file?
|
302
374
|
requirement_files.any? { |f| f =~ /\.txt$|\.in$/ }
|
303
375
|
end
|
304
376
|
|
377
|
+
sig { returns(T::Array[String]) }
|
305
378
|
def requirement_files
|
306
379
|
requirements.map { |r| r.fetch(:file) }
|
307
380
|
end
|
308
381
|
|
382
|
+
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
309
383
|
def requirements
|
310
384
|
dependency.requirements
|
311
385
|
end
|
312
386
|
|
387
|
+
sig { params(name: String).returns(String) }
|
313
388
|
def normalised_name(name)
|
314
389
|
NameNormaliser.normalise(name)
|
315
390
|
end
|
316
391
|
|
392
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
317
393
|
def pipfile
|
318
394
|
dependency_files.find { |f| f.name == "Pipfile" }
|
319
395
|
end
|
320
396
|
|
397
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
321
398
|
def pipfile_lock
|
322
399
|
dependency_files.find { |f| f.name == "Pipfile.lock" }
|
323
400
|
end
|
324
401
|
|
402
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
325
403
|
def pyproject
|
326
404
|
dependency_files.find { |f| f.name == "pyproject.toml" }
|
327
405
|
end
|
328
406
|
|
407
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
329
408
|
def poetry_lock
|
330
409
|
dependency_files.find { |f| f.name == "poetry.lock" }
|
331
410
|
end
|
332
411
|
|
412
|
+
sig { returns(T.nilable(T::Hash[String, T.untyped])) }
|
333
413
|
def library_details
|
334
|
-
@library_details ||=
|
414
|
+
@library_details ||= T.let(
|
415
|
+
poetry_details || standard_details || build_system_details,
|
416
|
+
T.nilable(T::Hash[String, T.untyped])
|
417
|
+
)
|
335
418
|
end
|
336
419
|
|
420
|
+
sig { returns(T.nilable(T::Hash[String, T.untyped])) }
|
337
421
|
def poetry_details
|
338
|
-
@poetry_details ||=
|
422
|
+
@poetry_details ||= T.let(
|
423
|
+
toml_content.dig("tool", "poetry"),
|
424
|
+
T.nilable(T::Hash[String, T.untyped])
|
425
|
+
)
|
339
426
|
end
|
340
427
|
|
428
|
+
sig { returns(T.nilable(T::Hash[String, T.untyped])) }
|
341
429
|
def standard_details
|
342
|
-
@standard_details ||=
|
430
|
+
@standard_details ||= T.let(
|
431
|
+
toml_content["project"],
|
432
|
+
T.nilable(T::Hash[String, T.untyped])
|
433
|
+
)
|
343
434
|
end
|
344
435
|
|
436
|
+
sig { returns(T.nilable(T::Hash[String, T.untyped])) }
|
345
437
|
def build_system_details
|
346
|
-
@build_system_details ||=
|
438
|
+
@build_system_details ||= T.let(
|
439
|
+
toml_content["build-system"],
|
440
|
+
T.nilable(T::Hash[String, T.untyped])
|
441
|
+
)
|
347
442
|
end
|
348
443
|
|
444
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
349
445
|
def toml_content
|
350
|
-
@toml_content ||=
|
446
|
+
@toml_content ||= T.let(
|
447
|
+
TomlRB.parse(T.must(pyproject).content),
|
448
|
+
T.nilable(T::Hash[String, T.untyped])
|
449
|
+
)
|
351
450
|
end
|
352
451
|
|
452
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
353
453
|
def pip_compile_files
|
354
454
|
dependency_files.select { |f| f.name.end_with?(".in") }
|
355
455
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-python
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.333.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
@@ -15,14 +15,14 @@ dependencies:
|
|
15
15
|
requirements:
|
16
16
|
- - '='
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: 0.
|
18
|
+
version: 0.333.0
|
19
19
|
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
23
|
- - '='
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: 0.
|
25
|
+
version: 0.333.0
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: debug
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -211,14 +211,14 @@ dependencies:
|
|
211
211
|
requirements:
|
212
212
|
- - "~>"
|
213
213
|
- !ruby/object:Gem::Version
|
214
|
-
version: '3.
|
214
|
+
version: '3.25'
|
215
215
|
type: :development
|
216
216
|
prerelease: false
|
217
217
|
version_requirements: !ruby/object:Gem::Requirement
|
218
218
|
requirements:
|
219
219
|
- - "~>"
|
220
220
|
- !ruby/object:Gem::Version
|
221
|
-
version: '3.
|
221
|
+
version: '3.25'
|
222
222
|
- !ruby/object:Gem::Dependency
|
223
223
|
name: webrick
|
224
224
|
requirement: !ruby/object:Gem::Requirement
|
@@ -290,7 +290,7 @@ licenses:
|
|
290
290
|
- MIT
|
291
291
|
metadata:
|
292
292
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
293
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
293
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.333.0
|
294
294
|
rdoc_options: []
|
295
295
|
require_paths:
|
296
296
|
- lib
|