dependabot-python 0.234.0 → 0.236.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a898b6d459367fc728deb010aed7f9adbba8064ffe506547b3076d8320024642
4
- data.tar.gz: 9fc76c80711472410d5919bab2ff8cca12ab161b7dfae2d23c83fe5afe3e30a2
3
+ metadata.gz: a9f6ef3950d4558611300b72d3839af555ea94e389aeb114a1257290901419e8
4
+ data.tar.gz: c896fc1ec74464344a70a3bf00add5181424b599c4553bf1d8dcb1e9708d8fcc
5
5
  SHA512:
6
- metadata.gz: 5c90405c02d5c636ee64d0ecd43b9fbe504b8f6b888d22f81806dddaca8799f590fcf7b46545d06584c3b45f68ffff58f13d0e2b0699441223419eb1e4d35bb9
7
- data.tar.gz: 7d10aa0fd61a0707b00bac29c8bd12ac533a49b37cfd1bb58a0830ad73eb50ad5767771889eb78236c175dc0e9cf400522395093894f715a6a80c90c523298f1
6
+ metadata.gz: 0c25f7375610f6be67b98731d756af8c16b551971dfef4661f59e48fd3b8cfb09ac962b60458bcb99ddcc40c58a6246c5e95062eb9d3724d0d7d93be5860e71a
7
+ data.tar.gz: 54538b41299885f7187c6c3fe8d2967eb2bf6001fdc18dfb897cc1b70841060de2b48f834e480406d4e8c595c5897d734856e549e9cfe04d820a66dee8e39c09
@@ -105,6 +105,11 @@ def parse_requirements(directory):
105
105
 
106
106
  pattern = r"-[cr] (.*) \(line \d+\)"
107
107
  abs_path = re.search(pattern, install_req.comes_from).group(1)
108
+
109
+ # Ignore dependencies from remote constraint files
110
+ if not os.path.isfile(abs_path):
111
+ continue
112
+
108
113
  rel_path = os.path.relpath(abs_path, directory)
109
114
 
110
115
  requirement_packages.append({
@@ -6,6 +6,7 @@ require "toml-rb"
6
6
  require "dependabot/file_fetchers"
7
7
  require "dependabot/file_fetchers/base"
8
8
  require "dependabot/python/language_version_manager"
9
+ require "dependabot/python/pip_compile_file_matcher"
9
10
  require "dependabot/python/requirement_parser"
10
11
  require "dependabot/python/file_parser/pyproject_files_parser"
11
12
  require "dependabot/python/file_parser/python_requirement_parser"
@@ -75,7 +76,7 @@ module Dependabot
75
76
 
76
77
  fetched_files << setup_file if setup_file
77
78
  fetched_files << setup_cfg_file if setup_cfg_file
78
- fetched_files += path_setup_files
79
+ fetched_files += project_files
79
80
  fetched_files << pip_conf if pip_conf
80
81
  fetched_files << python_version_file if python_version_file
81
82
 
@@ -113,8 +114,7 @@ module Dependabot
113
114
  pipfile ||
114
115
  pyproject
115
116
 
116
- path = Pathname.new(File.join(directory, "requirements.txt"))
117
- .cleanpath.to_path
117
+ path = cleanpath(File.join(directory, "requirements.txt"))
118
118
  raise Dependabot::DependencyFileNotFound, path
119
119
  end
120
120
 
@@ -269,7 +269,7 @@ module Dependabot
269
269
 
270
270
  paths.flat_map do |path|
271
271
  path = File.join(current_dir, path) unless current_dir == "."
272
- path = Pathname.new(path).cleanpath.to_path
272
+ path = cleanpath(path)
273
273
 
274
274
  next if previously_fetched_files.map(&:name).include?(path)
275
275
  next if file.name == path
@@ -293,43 +293,47 @@ module Dependabot
293
293
 
294
294
  paths.map do |path|
295
295
  path = File.join(current_dir, path) unless current_dir == "."
296
- Pathname.new(path).cleanpath.to_path
296
+ cleanpath(path)
297
297
  end
298
298
  end.flatten.uniq
299
299
 
300
300
  constraints_paths.map { |path| fetch_file_from_host(path) }
301
301
  end
302
302
 
303
- def path_setup_files
304
- path_setup_files = []
305
- unfetchable_files = []
303
+ def project_files
304
+ project_files = []
305
+ unfetchable_deps = []
306
306
 
307
- path_setup_file_paths.each do |path|
308
- path_setup_files += fetch_path_setup_file(path)
307
+ path_dependencies.each do |dep|
308
+ path = dep[:path]
309
+ project_files += fetch_project_file(path)
309
310
  rescue Dependabot::DependencyFileNotFound => e
310
- unfetchable_files << e.file_path.gsub(%r{^/}, "")
311
+ unfetchable_deps << if sdist_or_wheel?(path)
312
+ e.file_path.gsub(%r{^/}, "")
313
+ else
314
+ "\"#{dep[:name]}\" at #{cleanpath(File.join(directory, dep[:file]))}"
315
+ end
311
316
  end
312
317
 
313
- poetry_path_setup_file_paths.each do |path|
314
- path_setup_files += fetch_path_setup_file(path, allow_pyproject: true)
318
+ poetry_path_dependencies.each do |path|
319
+ project_files += fetch_project_file(path)
315
320
  rescue Dependabot::DependencyFileNotFound => e
316
- unfetchable_files << e.file_path.gsub(%r{^/}, "")
321
+ unfetchable_deps << e.file_path.gsub(%r{^/}, "")
317
322
  end
318
323
 
319
- raise Dependabot::PathDependenciesNotReachable, unfetchable_files if unfetchable_files.any?
324
+ raise Dependabot::PathDependenciesNotReachable, unfetchable_deps if unfetchable_deps.any?
320
325
 
321
- path_setup_files
326
+ project_files
322
327
  end
323
328
 
324
- def fetch_path_setup_file(path, allow_pyproject: false)
325
- path_setup_files = []
329
+ def fetch_project_file(path)
330
+ project_files = []
331
+
332
+ path = cleanpath(File.join(path, "setup.py")) unless sdist_or_wheel?(path)
326
333
 
327
- unless path.end_with?(".tar.gz", ".whl", ".zip")
328
- path = Pathname.new(File.join(path, "setup.py")).cleanpath.to_path
329
- end
330
334
  return [] if path == "setup.py" && setup_file
331
335
 
332
- path_setup_files <<
336
+ project_files <<
333
337
  begin
334
338
  fetch_file_from_host(
335
339
  path,
@@ -337,19 +341,20 @@ module Dependabot
337
341
  ).tap { |f| f.support_file = true }
338
342
  rescue Dependabot::DependencyFileNotFound
339
343
  # For projects with pyproject.toml attempt to fetch a pyproject.toml
340
- # at the given path instead of a setup.py. We do not require a
341
- # setup.py to be present, so if none can be found, simply return
342
- return [] unless allow_pyproject
343
-
344
+ # at the given path instead of a setup.py.
344
345
  fetch_file_from_host(
345
346
  path.gsub("setup.py", "pyproject.toml"),
346
347
  fetch_submodules: true
347
348
  ).tap { |f| f.support_file = true }
348
349
  end
349
350
 
350
- return path_setup_files unless path.end_with?(".py")
351
+ return project_files unless path.end_with?(".py")
351
352
 
352
- path_setup_files + cfg_files_for_setup_py(path)
353
+ project_files + cfg_files_for_setup_py(path)
354
+ end
355
+
356
+ def sdist_or_wheel?(path)
357
+ path.end_with?(".tar.gz", ".whl", ".zip")
353
358
  end
354
359
 
355
360
  def cfg_files_for_setup_py(path)
@@ -378,60 +383,63 @@ module Dependabot
378
383
  end
379
384
  end
380
385
 
381
- def path_setup_file_paths
382
- requirement_txt_path_setup_file_paths +
383
- requirement_in_path_setup_file_paths +
384
- pipfile_path_setup_file_paths
386
+ def path_dependencies
387
+ requirement_txt_path_dependencies +
388
+ requirement_in_path_dependencies +
389
+ pipfile_path_dependencies
385
390
  end
386
391
 
387
- def requirement_txt_path_setup_file_paths
392
+ def requirement_txt_path_dependencies
388
393
  (requirements_txt_files + child_requirement_txt_files)
389
- .map { |req_file| parse_path_setup_paths(req_file) }
390
- .flatten.uniq
394
+ .map { |req_file| parse_requirement_path_dependencies(req_file) }
395
+ .flatten.uniq { |dep| dep[:path] }
391
396
  end
392
397
 
393
- def requirement_in_path_setup_file_paths
398
+ def requirement_in_path_dependencies
394
399
  requirements_in_files
395
- .map { |req_file| parse_path_setup_paths(req_file) }
396
- .flatten.uniq
400
+ .map { |req_file| parse_requirement_path_dependencies(req_file) }
401
+ .flatten.uniq { |dep| dep[:path] }
397
402
  end
398
403
 
399
- def parse_path_setup_paths(req_file)
404
+ def parse_requirement_path_dependencies(req_file)
405
+ # If this is a pip-compile lockfile, rely on whatever path dependencies we found in the main manifest
406
+ return [] if pip_compile_file_matcher.lockfile_for_pip_compile_file?(req_file)
407
+
400
408
  uneditable_reqs =
401
409
  req_file.content
402
- .scan(/^['"]?(?:file:)?(?<path>\..*?)(?=\[|#|'|"|$)/)
403
- .flatten
404
- .map(&:strip)
405
- .reject { |p| p.include?("://") }
410
+ .scan(/(?<name>^['"]?(?:file:)?(?<path>\..*?)(?=\[|#|'|"|$))/)
411
+ .filter_map do |n, p|
412
+ { name: n.strip, path: p.strip, file: req_file.name } unless p.include?("://")
413
+ end
406
414
 
407
415
  editable_reqs =
408
416
  req_file.content
409
- .scan(/^(?:-e)\s+['"]?(?:file:)?(?<path>.*?)(?=\[|#|'|"|$)/)
410
- .flatten
411
- .map(&:strip)
412
- .reject { |p| p.include?("://") || p.include?("git@") }
417
+ .scan(/(?<name>^(?:-e)\s+['"]?(?:file:)?(?<path>.*?)(?=\[|#|'|"|$))/)
418
+ .filter_map do |n, p|
419
+ { name: n.strip, path: p.strip, file: req_file.name } unless p.include?("://") || p.include?("git@")
420
+ end
413
421
 
414
422
  uneditable_reqs + editable_reqs
415
423
  end
416
424
 
417
- def pipfile_path_setup_file_paths
425
+ def pipfile_path_dependencies
418
426
  return [] unless pipfile
419
427
 
420
- paths = []
428
+ deps = []
421
429
  DEPENDENCY_TYPES.each do |dep_type|
422
430
  next unless parsed_pipfile[dep_type]
423
431
 
424
432
  parsed_pipfile[dep_type].each do |_, req|
425
433
  next unless req.is_a?(Hash) && req["path"]
426
434
 
427
- paths << req["path"]
435
+ deps << { name: req["path"], path: req["path"], file: pipfile.name }
428
436
  end
429
437
  end
430
438
 
431
- paths
439
+ deps
432
440
  end
433
441
 
434
- def poetry_path_setup_file_paths
442
+ def poetry_path_dependencies
435
443
  return [] unless pyproject
436
444
 
437
445
  paths = []
@@ -447,6 +455,14 @@ module Dependabot
447
455
 
448
456
  paths
449
457
  end
458
+
459
+ def cleanpath(path)
460
+ Pathname.new(path).cleanpath.to_path
461
+ end
462
+
463
+ def pip_compile_file_matcher
464
+ @pip_compile_file_matcher ||= PipCompileFileMatcher.new(requirements_in_files)
465
+ end
450
466
  end
451
467
  end
452
468
  end
@@ -8,7 +8,6 @@ require "dependabot/file_parsers/base/dependency_set"
8
8
  require "dependabot/python/file_parser"
9
9
  require "dependabot/python/requirement"
10
10
  require "dependabot/errors"
11
- require "dependabot/python/helpers"
12
11
  require "dependabot/python/name_normaliser"
13
12
 
14
13
  module Dependabot
@@ -28,7 +27,7 @@ module Dependabot
28
27
  dependency_set = Dependabot::FileParsers::Base::DependencySet.new
29
28
 
30
29
  dependency_set += pyproject_dependencies if using_poetry? || using_pep621?
31
- dependency_set += lockfile_dependencies if lockfile
30
+ dependency_set += lockfile_dependencies if using_poetry? && lockfile
32
31
 
33
32
  dependency_set
34
33
  end
@@ -39,6 +38,16 @@ module Dependabot
39
38
 
40
39
  def pyproject_dependencies
41
40
  if using_poetry?
41
+ missing_keys = missing_poetry_keys
42
+
43
+ if missing_keys.any?
44
+ raise DependencyFileNotParseable.new(
45
+ pyproject.path,
46
+ "#{pyproject.path} is missing the following sections:\n" \
47
+ " * #{missing_keys.map { |key| "tool.poetry.#{key}" }.join("\n * ")}\n"
48
+ )
49
+ end
50
+
42
51
  poetry_dependencies
43
52
  else
44
53
  pep621_dependencies
@@ -53,11 +62,11 @@ module Dependabot
53
62
  dependencies = Dependabot::FileParsers::Base::DependencySet.new
54
63
 
55
64
  POETRY_DEPENDENCY_TYPES.each do |type|
56
- deps_hash = parsed_pyproject.dig("tool", "poetry", type) || {}
65
+ deps_hash = poetry_root[type] || {}
57
66
  dependencies += parse_poetry_dependency_group(type, deps_hash)
58
67
  end
59
68
 
60
- groups = parsed_pyproject.dig("tool", "poetry", "group") || {}
69
+ groups = poetry_root["group"] || {}
61
70
  groups.each do |group, group_spec|
62
71
  dependencies += parse_poetry_dependency_group(group, group_spec["dependencies"])
63
72
  end
@@ -138,7 +147,11 @@ module Dependabot
138
147
  end
139
148
 
140
149
  def using_poetry?
141
- !parsed_pyproject.dig("tool", "poetry").nil?
150
+ !poetry_root.nil?
151
+ end
152
+
153
+ def missing_poetry_keys
154
+ %w(name version description authors).reject { |key| poetry_root.key?(key) }
142
155
  end
143
156
 
144
157
  def using_pep621?
@@ -146,6 +159,10 @@ module Dependabot
146
159
  !parsed_pyproject.dig("project", "optional-dependencies").nil?
147
160
  end
148
161
 
162
+ def poetry_root
163
+ parsed_pyproject.dig("tool", "poetry")
164
+ end
165
+
149
166
  def using_pdm?
150
167
  using_pep621? && pdm_lock
151
168
  end
@@ -187,7 +204,7 @@ module Dependabot
187
204
  File.write(lockfile.name, lockfile.content)
188
205
 
189
206
  begin
190
- output = Helpers.run_poetry_command("pyenv exec poetry show --only main")
207
+ output = SharedHelpers.run_shell_command("pyenv exec poetry show --only main")
191
208
 
192
209
  output.split("\n").map { |line| line.split.first }
193
210
  rescue SharedHelpers::HelperSubprocessFailed
@@ -103,21 +103,7 @@ module Dependabot
103
103
  end
104
104
 
105
105
  def run_command(command, env: {})
106
- start = Time.now
107
- command = SharedHelpers.escape_command(command)
108
- stdout, process = Open3.capture2e(env, command)
109
- time_taken = Time.now - start
110
-
111
- return stdout if process.success?
112
-
113
- raise SharedHelpers::HelperSubprocessFailed.new(
114
- message: stdout,
115
- error_context: {
116
- command: command,
117
- time_taken: time_taken,
118
- process_exit_value: process.to_s
119
- }
120
- )
106
+ SharedHelpers.run_shell_command(command, env: env, stderr_to_stdout: true)
121
107
  end
122
108
 
123
109
  def requirement_class
@@ -10,6 +10,7 @@ require "dependabot/python/requirement"
10
10
  require "dependabot/errors"
11
11
  require "dependabot/python/native_helpers"
12
12
  require "dependabot/python/name_normaliser"
13
+ require "dependabot/python/pip_compile_file_matcher"
13
14
 
14
15
  module Dependabot
15
16
  module Python
@@ -74,21 +75,29 @@ module Dependabot
74
75
  # probably blocked. Ignore it.
75
76
  next if blocking_marker?(dep)
76
77
 
78
+ name = dep["name"]
79
+ file = dep["file"]
80
+ version = dep["version"]
81
+ original_file = get_original_file(file)
82
+
77
83
  requirements =
78
- if lockfile_for_pip_compile_file?(dep["file"]) then []
84
+ if original_file && pip_compile_file_matcher.lockfile_for_pip_compile_file?(original_file) then []
79
85
  else
80
86
  [{
81
87
  requirement: dep["requirement"],
82
- file: Pathname.new(dep["file"]).cleanpath.to_path,
88
+ file: Pathname.new(file).cleanpath.to_path,
83
89
  source: nil,
84
- groups: group_from_filename(dep["file"])
90
+ groups: group_from_filename(file)
85
91
  }]
86
92
  end
87
93
 
94
+ # PyYAML < 6.0 will cause `pip-compile` to fail due to incompatiblity with Cython 3. Workaround it.
95
+ SharedHelpers.run_shell_command("pyenv exec pip install cython<3.0") if old_pyyaml?(name, version)
96
+
88
97
  dependencies <<
89
98
  Dependency.new(
90
- name: normalised_name(dep["name"], dep["extras"]),
91
- version: dep["version"]&.include?("*") ? nil : dep["version"],
99
+ name: normalised_name(name, dep["extras"]),
100
+ version: version&.include?("*") ? nil : version,
92
101
  requirements: requirements,
93
102
  package_manager: "pip"
94
103
  )
@@ -96,6 +105,13 @@ module Dependabot
96
105
  dependencies
97
106
  end
98
107
 
108
+ def old_pyyaml?(name, version)
109
+ major_version = version&.split(".")&.first
110
+ return false unless major_version
111
+
112
+ name == "pyyaml" && major_version < "6"
113
+ end
114
+
99
115
  def group_from_filename(filename)
100
116
  if filename.include?("dev") then ["dev-dependencies"]
101
117
  else
@@ -118,17 +134,6 @@ module Dependabot
118
134
  .dependency_set
119
135
  end
120
136
 
121
- def lockfile_for_pip_compile_file?(filename)
122
- return false unless pip_compile_files.any?
123
- return false unless filename.end_with?(".txt")
124
-
125
- file = dependency_files.find { |f| f.name == filename }
126
- return true if file&.content&.match?(output_file_regex(filename))
127
-
128
- basename = filename.gsub(/\.txt$/, "")
129
- pip_compile_files.any? { |f| f.name == basename + ".in" }
130
- end
131
-
132
137
  def parsed_requirement_files
133
138
  SharedHelpers.in_a_temporary_directory do
134
139
  write_temporary_dependency_files
@@ -201,10 +206,6 @@ module Dependabot
201
206
  @pipfile_lock ||= get_original_file("Pipfile.lock")
202
207
  end
203
208
 
204
- def output_file_regex(filename)
205
- "--output-file[=\s]+#{Regexp.escape(filename)}(?:\s|$)"
206
- end
207
-
208
209
  def pyproject
209
210
  @pyproject ||= get_original_file("pyproject.toml")
210
211
  end
@@ -225,6 +226,10 @@ module Dependabot
225
226
  @pip_compile_files ||=
226
227
  dependency_files.select { |f| f.name.end_with?(".in") }
227
228
  end
229
+
230
+ def pip_compile_file_matcher
231
+ @pip_compile_file_matcher ||= PipCompileFileMatcher.new(pip_compile_files)
232
+ end
228
233
  end
229
234
  end
230
235
  end
@@ -27,6 +27,8 @@ module Dependabot
27
27
  WARNINGS = /\s*# WARNING:.*\Z/m
28
28
  UNSAFE_NOTE = /\s*# The following packages are considered to be unsafe.*\Z/m
29
29
  RESOLVER_REGEX = /(?<=--resolver=)(\w+)/
30
+ NATIVE_COMPILATION_ERROR =
31
+ "pip._internal.exceptions.InstallationSubprocessError: Getting requirements to build wheel exited with 1"
30
32
 
31
33
  attr_reader :dependencies, :dependency_files, :credentials
32
34
 
@@ -34,6 +36,7 @@ module Dependabot
34
36
  @dependencies = dependencies
35
37
  @dependency_files = dependency_files
36
38
  @credentials = credentials
39
+ @build_isolation = true
37
40
  end
38
41
 
39
42
  def updated_dependency_files
@@ -67,28 +70,7 @@ module Dependabot
67
70
  language_version_manager.install_required_python
68
71
 
69
72
  filenames_to_compile.each do |filename|
70
- # Shell out to pip-compile, generate a new set of requirements.
71
- # This is slow, as pip-compile needs to do installs.
72
- options = pip_compile_options(filename)
73
- options_fingerprint = pip_compile_options_fingerprint(options)
74
-
75
- name_part = "pyenv exec pip-compile " \
76
- "#{options} -P " \
77
- "#{dependency.name}"
78
- fingerprint_name_part = "pyenv exec pip-compile " \
79
- "#{options_fingerprint} -P " \
80
- "<dependency_name>"
81
-
82
- version_part = "#{dependency.version} #{filename}"
83
- fingerprint_version_part = "<dependency_version> <filename>"
84
-
85
- # Don't escape pyenv `dep-name==version` syntax
86
- run_pip_compile_command(
87
- "#{SharedHelpers.escape_command(name_part)}==" \
88
- "#{SharedHelpers.escape_command(version_part)}",
89
- allow_unsafe_shell_command: true,
90
- fingerprint: "#{fingerprint_name_part}==#{fingerprint_version_part}"
91
- )
73
+ compile_file(filename)
92
74
  end
93
75
 
94
76
  # Remove any .python-version file before parsing the reqs
@@ -108,6 +90,44 @@ module Dependabot
108
90
  end
109
91
  end
110
92
 
93
+ def compile_file(filename)
94
+ # Shell out to pip-compile, generate a new set of requirements.
95
+ # This is slow, as pip-compile needs to do installs.
96
+ options = pip_compile_options(filename)
97
+ options_fingerprint = pip_compile_options_fingerprint(options)
98
+
99
+ name_part = "pyenv exec pip-compile " \
100
+ "#{options} -P " \
101
+ "#{dependency.name}"
102
+ fingerprint_name_part = "pyenv exec pip-compile " \
103
+ "#{options_fingerprint} -P " \
104
+ "<dependency_name>"
105
+
106
+ version_part = "#{dependency.version} #{filename}"
107
+ fingerprint_version_part = "<dependency_version> <filename>"
108
+
109
+ # Don't escape pyenv `dep-name==version` syntax
110
+ run_pip_compile_command(
111
+ "#{SharedHelpers.escape_command(name_part)}==" \
112
+ "#{SharedHelpers.escape_command(version_part)}",
113
+ allow_unsafe_shell_command: true,
114
+ fingerprint: "#{fingerprint_name_part}==#{fingerprint_version_part}"
115
+ )
116
+ rescue SharedHelpers::HelperSubprocessFailed => e
117
+ retry_count ||= 0
118
+ retry_count += 1
119
+ if compilation_error?(e) && retry_count <= 1
120
+ @build_isolation = false
121
+ retry
122
+ end
123
+
124
+ raise
125
+ end
126
+
127
+ def compilation_error?(error)
128
+ error.message.include?(NATIVE_COMPILATION_ERROR)
129
+ end
130
+
111
131
  def update_manifest_files
112
132
  dependency_files.filter_map do |file|
113
133
  next unless file.name.end_with?(".in")
@@ -146,30 +166,21 @@ module Dependabot
146
166
  end
147
167
 
148
168
  def run_command(cmd, env: python_env, allow_unsafe_shell_command: false, fingerprint:)
149
- start = Time.now
150
- command = if allow_unsafe_shell_command
151
- cmd
152
- else
153
- SharedHelpers.escape_command(cmd)
154
- end
155
- stdout, process = Open3.capture2e(env, command)
156
- time_taken = Time.now - start
157
-
158
- return stdout if process.success?
169
+ SharedHelpers.run_shell_command(
170
+ cmd,
171
+ env: env,
172
+ allow_unsafe_shell_command: allow_unsafe_shell_command,
173
+ fingerprint: fingerprint,
174
+ stderr_to_stdout: true
175
+ )
176
+ rescue SharedHelpers::HelperSubprocessFailed => e
177
+ stdout = e.message
159
178
 
160
179
  if stdout.match?(INCOMPATIBLE_VERSIONS_REGEX)
161
180
  raise DependencyFileNotResolvable, stdout.match(INCOMPATIBLE_VERSIONS_REGEX)
162
181
  end
163
182
 
164
- raise SharedHelpers::HelperSubprocessFailed.new(
165
- message: stdout,
166
- error_context: {
167
- command: command,
168
- fingerprint: fingerprint,
169
- time_taken: time_taken,
170
- process_exit_value: process.to_s
171
- }
172
- )
183
+ raise
173
184
  end
174
185
 
175
186
  def run_pip_compile_command(command, allow_unsafe_shell_command: false, fingerprint:)
@@ -412,7 +423,7 @@ module Dependabot
412
423
  end
413
424
 
414
425
  def pip_compile_options(filename)
415
- options = ["--build-isolation"]
426
+ options = @build_isolation ? ["--build-isolation"] : ["--no-build-isolation"]
416
427
  options += pip_compile_index_options
417
428
 
418
429
  if (requirements_file = compiled_file_for_filename(filename))
@@ -245,28 +245,16 @@ module Dependabot
245
245
  File.write("dev-req.txt", dev_req_content)
246
246
  end
247
247
 
248
- def run_command(command, env: {})
249
- start = Time.now
250
- command = SharedHelpers.escape_command(command)
251
- stdout, _, process = Open3.capture3(env, command)
252
- time_taken = Time.now - start
253
-
254
- # Raise an error with the output from the shell session if Pipenv
255
- # returns a non-zero status
256
- return stdout if process.success?
257
-
258
- raise SharedHelpers::HelperSubprocessFailed.new(
259
- message: stdout,
260
- error_context: {
261
- command: command,
262
- time_taken: time_taken,
263
- process_exit_value: process.to_s
264
- }
265
- )
248
+ def run_command(command, env: {}, fingerprint: nil)
249
+ SharedHelpers.run_shell_command(command, env: env, fingerprint: fingerprint)
266
250
  end
267
251
 
268
252
  def run_pipenv_command(command, env: pipenv_env_variables)
269
- run_command("pyenv local #{language_version_manager.python_major_minor}")
253
+ run_command(
254
+ "pyenv local #{language_version_manager.python_major_minor}",
255
+ fingerprint: "pyenv local <python_major_minor>"
256
+ )
257
+
270
258
  run_command(command, env: env)
271
259
  end
272
260
 
@@ -10,7 +10,6 @@ require "dependabot/python/version"
10
10
  require "dependabot/python/requirement"
11
11
  require "dependabot/python/file_parser/python_requirement_parser"
12
12
  require "dependabot/python/file_updater"
13
- require "dependabot/python/helpers"
14
13
  require "dependabot/python/native_helpers"
15
14
  require "dependabot/python/name_normaliser"
16
15
 
@@ -61,34 +60,38 @@ module Dependabot
61
60
  end
62
61
 
63
62
  def updated_pyproject_content
64
- dependencies
65
- .select { |dep| requirement_changed?(pyproject, dep) }
66
- .reduce(pyproject.content.dup) do |content, dep|
67
- updated_requirement =
68
- dep.requirements.find { |r| r[:file] == pyproject.name }
69
- .fetch(:requirement)
70
-
71
- old_req =
72
- dep.previous_requirements
73
- .find { |r| r[:file] == pyproject.name }
74
- .fetch(:requirement)
75
-
76
- declaration_regex = declaration_regex(dep)
77
- updated_content = if content.match?(declaration_regex)
78
- content.gsub(declaration_regex(dep)) do |match|
79
- match.gsub(old_req, updated_requirement)
80
- end
81
- else
82
- content.gsub(table_declaration_regex(dep)) do |match|
83
- match.gsub(/(\s*version\s*=\s*["'])#{Regexp.escape(old_req)}/,
84
- '\1' + updated_requirement)
85
- end
86
- end
87
-
88
- raise "Content did not change!" if content == updated_content
89
-
90
- updated_content
63
+ content = pyproject.content
64
+ return content unless requirement_changed?(pyproject, dependency)
65
+
66
+ updated_content = content.dup
67
+
68
+ dependency.requirements.zip(dependency.previous_requirements).each do |new_r, old_r|
69
+ next unless new_r[:file] == pyproject.name && old_r[:file] == pyproject.name
70
+
71
+ updated_content = replace_dep(dependency, updated_content, new_r, old_r)
72
+ end
73
+
74
+ raise "Content did not change!" if content == updated_content
75
+
76
+ updated_content
77
+ end
78
+
79
+ def replace_dep(dep, content, new_r, old_r)
80
+ new_req = new_r[:requirement]
81
+ old_req = old_r[:requirement]
82
+
83
+ declaration_regex = declaration_regex(dep, old_r)
84
+ declaration_match = content.match(declaration_regex)
85
+ if declaration_match
86
+ declaration = declaration_match[:declaration]
87
+ new_declaration = declaration.sub(old_req, new_req)
88
+ content.sub(declaration, new_declaration)
89
+ else
90
+ content.gsub(table_declaration_regex(dep, new_r)) do |match|
91
+ match.gsub(/(\s*version\s*=\s*["'])#{Regexp.escape(old_req)}/,
92
+ '\1' + new_req)
91
93
  end
94
+ end
92
95
  end
93
96
 
94
97
  def updated_lockfile_content
@@ -204,7 +207,7 @@ module Dependabot
204
207
  end
205
208
 
206
209
  def run_poetry_command(command, fingerprint: nil)
207
- Helpers.run_poetry_command(command, fingerprint: fingerprint)
210
+ SharedHelpers.run_shell_command(command, fingerprint: fingerprint)
208
211
  end
209
212
 
210
213
  def write_temporary_dependency_files(pyproject_content)
@@ -241,12 +244,14 @@ module Dependabot
241
244
  end
242
245
  end
243
246
 
244
- def declaration_regex(dep)
245
- /(?:^\s*|["'])#{escape(dep)}["']?\s*=.*$/i
247
+ def declaration_regex(dep, old_req)
248
+ group = old_req[:groups].first
249
+
250
+ /#{group}(?:\.dependencies)?\]\s*\n.*?(?<declaration>(?:^\s*|["'])#{escape(dep)}["']?\s*=[^\n]*)$/mi
246
251
  end
247
252
 
248
- def table_declaration_regex(dep)
249
- /tool\.poetry\.[^\n]+\.#{escape(dep)}\]\n.*?\s*version\s* =.*?\n/m
253
+ def table_declaration_regex(dep, old_req)
254
+ /tool\.poetry\.#{old_req[:groups].first}\.#{escape(dep)}\]\n.*?\s*version\s* =.*?\n/m
250
255
  end
251
256
 
252
257
  def escape(dep)
@@ -82,6 +82,8 @@ module Dependabot
82
82
  "git" => locked_details&.dig("source", "url"),
83
83
  "rev" => locked_details&.dig("source", "reference")
84
84
  }
85
+ subdirectory = locked_details&.dig("source", "subdirectory")
86
+ poetry_object[key][dep_name]["subdirectory"] = subdirectory if subdirectory
85
87
  elsif poetry_object[key][dep_name].is_a?(Hash)
86
88
  poetry_object[key][dep_name]["version"] = locked_version
87
89
  elsif poetry_object[key][dep_name].is_a?(Array)
@@ -48,10 +48,11 @@ module Dependabot
48
48
  end
49
49
 
50
50
  def updated_requirement_or_setup_file_content(new_req, old_req)
51
- content = get_original_file(new_req.fetch(:file)).content
51
+ original_file = get_original_file(new_req.fetch(:file))
52
+ raise "Could not find a dependency file for #{new_req}" unless original_file
52
53
 
53
54
  RequirementReplacer.new(
54
- content: content,
55
+ content: original_file.content,
55
56
  dependency_name: dependency.name,
56
57
  old_requirement: old_req.fetch(:requirement),
57
58
  new_requirement: new_req.fetch(:requirement),
@@ -0,0 +1,32 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Dependabot
5
+ module Python
6
+ class PipCompileFileMatcher
7
+ def initialize(requirements_in_files)
8
+ @requirements_in_files = requirements_in_files
9
+ end
10
+
11
+ def lockfile_for_pip_compile_file?(file)
12
+ return false unless requirements_in_files.any?
13
+
14
+ name = file.name
15
+ return false unless name.end_with?(".txt")
16
+
17
+ return true if file.content.match?(output_file_regex(name))
18
+
19
+ basename = name.gsub(/\.txt$/, "")
20
+ requirements_in_files.any? { |f| f.name == basename + ".in" }
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :requirements_in_files
26
+
27
+ def output_file_regex(filename)
28
+ "--output-file[=\s]+#{Regexp.escape(filename)}(?:\s|$)"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Dependabot
@@ -9,7 +9,7 @@ module Dependabot
9
9
  COMPARISON = /===|==|>=|<=|<|>|~=|!=/
10
10
  VERSION = /([1-9][0-9]*!)?[0-9]+[a-zA-Z0-9\-_.*]*(\+[0-9a-zA-Z]+(\.[0-9a-zA-Z]+)*)?/
11
11
 
12
- REQUIREMENT = /(?<comparison>#{COMPARISON})\s*\\?\s*(?<version>#{VERSION})/
12
+ REQUIREMENT = /(?<comparison>#{COMPARISON})\s*\\?\s*v?(?<version>#{VERSION})/
13
13
  HASH = /--hash=(?<algorithm>.*?):(?<hash>.*?)(?=\s|\\|$)/
14
14
  REQUIREMENTS = /#{REQUIREMENT}(\s*,\s*\\?\s*#{REQUIREMENT})*/
15
15
  HASHES = /#{HASH}(\s*\\?\s*#{HASH})*/
@@ -27,7 +27,7 @@ module Dependabot
27
27
  GIT_DEPENDENCY_UNREACHABLE_REGEX = /git clone --filter=blob:none --quiet (?<url>[^\s]+).* /
28
28
  GIT_REFERENCE_NOT_FOUND_REGEX = /Did not find branch or tag '(?<tag>[^\n"]+)'/m
29
29
  NATIVE_COMPILATION_ERROR =
30
- "pip._internal.exceptions.InstallationSubprocessError: Command errored out with exit status 1:"
30
+ "pip._internal.exceptions.InstallationSubprocessError: Getting requirements to build wheel exited with 1"
31
31
  # See https://packaging.python.org/en/latest/tutorials/packaging-projects/#configuring-metadata
32
32
  PYTHON_PACKAGE_NAME_REGEX = /[A-Za-z0-9_\-]+/
33
33
  RESOLUTION_IMPOSSIBLE_ERROR = "ResolutionImpossible"
@@ -43,17 +43,21 @@ module Dependabot
43
43
  end
44
44
 
45
45
  def latest_resolvable_version(requirement: nil)
46
+ @latest_resolvable_version_string ||= {}
47
+ return @latest_resolvable_version_string[requirement] if @latest_resolvable_version_string.key?(requirement)
48
+
46
49
  version_string =
47
50
  fetch_latest_resolvable_version_string(requirement: requirement)
48
51
 
49
- version_string.nil? ? nil : Python::Version.new(version_string)
52
+ @latest_resolvable_version_string[requirement] ||=
53
+ version_string.nil? ? nil : Python::Version.new(version_string)
50
54
  end
51
55
 
52
56
  def resolvable?(version:)
53
57
  @resolvable ||= {}
54
58
  return @resolvable[version] if @resolvable.key?(version)
55
59
 
56
- @resolvable[version] = if fetch_latest_resolvable_version_string(requirement: "==#{version}")
60
+ @resolvable[version] = if latest_resolvable_version(requirement: "==#{version}")
57
61
  true
58
62
  else
59
63
  false
@@ -63,57 +67,59 @@ module Dependabot
63
67
  private
64
68
 
65
69
  def fetch_latest_resolvable_version_string(requirement:)
66
- @latest_resolvable_version_string ||= {}
67
- return @latest_resolvable_version_string[requirement] if @latest_resolvable_version_string.key?(requirement)
70
+ SharedHelpers.in_a_temporary_directory do
71
+ SharedHelpers.with_git_configured(credentials: credentials) do
72
+ write_temporary_dependency_files(updated_req: requirement)
73
+ language_version_manager.install_required_python
68
74
 
69
- @latest_resolvable_version_string[requirement] ||=
70
- SharedHelpers.in_a_temporary_directory do
71
- SharedHelpers.with_git_configured(credentials: credentials) do
72
- write_temporary_dependency_files(updated_req: requirement)
73
- language_version_manager.install_required_python
74
-
75
- filenames_to_compile.each do |filename|
76
- # Shell out to pip-compile.
77
- # This is slow, as pip-compile needs to do installs.
78
- options = pip_compile_options(filename)
79
- options_fingerprint = pip_compile_options_fingerprint(options)
80
-
81
- run_pip_compile_command(
82
- "pyenv exec pip-compile -v #{options} -P #{dependency.name} #{filename}",
83
- fingerprint: "pyenv exec pip-compile -v #{options_fingerprint} -P <dependency_name> <filename>"
84
- )
85
-
86
- next if dependency.top_level?
87
-
88
- # Run pip-compile a second time for transient dependencies
89
- # to make sure we do not update dependencies that are
90
- # superfluous. pip-compile does not detect these when
91
- # updating a specific dependency with the -P option.
92
- # Running pip-compile a second time will automatically remove
93
- # superfluous dependencies. Dependabot then marks those with
94
- # update_not_possible.
95
- write_original_manifest_files
96
- run_pip_compile_command(
97
- "pyenv exec pip-compile #{options} #{filename}",
98
- fingerprint: "pyenv exec pip-compile #{options_fingerprint} <filename>"
99
- )
100
- end
101
-
102
- # Remove any .python-version file before parsing the reqs
103
- FileUtils.remove_entry(".python-version", true)
104
-
105
- parse_updated_files
106
- end
107
- rescue SharedHelpers::HelperSubprocessFailed => e
108
- retry_count ||= 0
109
- retry_count += 1
110
- if compilation_error?(e) && retry_count <= 1
111
- @build_isolation = false
112
- retry
75
+ filenames_to_compile.each do |filename|
76
+ return nil unless compile_file(filename)
113
77
  end
114
78
 
115
- handle_pip_compile_errors(e)
79
+ # Remove any .python-version file before parsing the reqs
80
+ FileUtils.remove_entry(".python-version", true)
81
+
82
+ parse_updated_files
116
83
  end
84
+ end
85
+ end
86
+
87
+ def compile_file(filename)
88
+ # Shell out to pip-compile.
89
+ # This is slow, as pip-compile needs to do installs.
90
+ options = pip_compile_options(filename)
91
+ options_fingerprint = pip_compile_options_fingerprint(options)
92
+
93
+ run_pip_compile_command(
94
+ "pyenv exec pip-compile -v #{options} -P #{dependency.name} #{filename}",
95
+ fingerprint: "pyenv exec pip-compile -v #{options_fingerprint} -P <dependency_name> <filename>"
96
+ )
97
+
98
+ return true if dependency.top_level?
99
+
100
+ # Run pip-compile a second time for transient dependencies
101
+ # to make sure we do not update dependencies that are
102
+ # superfluous. pip-compile does not detect these when
103
+ # updating a specific dependency with the -P option.
104
+ # Running pip-compile a second time will automatically remove
105
+ # superfluous dependencies. Dependabot then marks those with
106
+ # update_not_possible.
107
+ write_original_manifest_files
108
+ run_pip_compile_command(
109
+ "pyenv exec pip-compile #{options} #{filename}",
110
+ fingerprint: "pyenv exec pip-compile #{options_fingerprint} <filename>"
111
+ )
112
+
113
+ true
114
+ rescue SharedHelpers::HelperSubprocessFailed => e
115
+ retry_count ||= 0
116
+ retry_count += 1
117
+ if compilation_error?(e) && retry_count <= 1
118
+ @build_isolation = false
119
+ retry
120
+ end
121
+
122
+ handle_pip_compile_errors(e.message)
117
123
  end
118
124
 
119
125
  def compilation_error?(error)
@@ -122,8 +128,8 @@ module Dependabot
122
128
 
123
129
  # rubocop:disable Metrics/AbcSize
124
130
  # rubocop:disable Metrics/PerceivedComplexity
125
- def handle_pip_compile_errors(error)
126
- if error.message.include?(RESOLUTION_IMPOSSIBLE_ERROR)
131
+ def handle_pip_compile_errors(message)
132
+ if message.include?(RESOLUTION_IMPOSSIBLE_ERROR)
127
133
  check_original_requirements_resolvable
128
134
  # If the original requirements are resolvable but we get an
129
135
  # incompatibility error after unlocking then it's likely to be
@@ -131,14 +137,14 @@ module Dependabot
131
137
  return nil
132
138
  end
133
139
 
134
- if error.message.include?("UnsupportedConstraint")
140
+ if message.include?("UnsupportedConstraint")
135
141
  # If there's an unsupported constraint, check if it existed
136
142
  # previously (and raise if it did)
137
143
  check_original_requirements_resolvable
138
144
  end
139
145
 
140
- if (error.message.include?('Command "python setup.py egg_info') ||
141
- error.message.include?(
146
+ if (message.include?('Command "python setup.py egg_info') ||
147
+ message.include?(
142
148
  "exit status 1: python setup.py egg_info"
143
149
  )) &&
144
150
  check_original_requirements_resolvable
@@ -147,16 +153,16 @@ module Dependabot
147
153
  return
148
154
  end
149
155
 
150
- if error.message.include?(RESOLUTION_IMPOSSIBLE_ERROR) &&
151
- !error.message.match?(/#{Regexp.quote(dependency.name)}/i)
156
+ if message.include?(RESOLUTION_IMPOSSIBLE_ERROR) &&
157
+ !message.match?(/#{Regexp.quote(dependency.name)}/i)
152
158
  # Sometimes pip-tools gets confused and can't work around
153
159
  # sub-dependency incompatibilities. Ignore those cases.
154
160
  return nil
155
161
  end
156
162
 
157
- if error.message.match?(GIT_REFERENCE_NOT_FOUND_REGEX)
158
- tag = error.message.match(GIT_REFERENCE_NOT_FOUND_REGEX).named_captures.fetch("tag")
159
- constraints_section = error.message.split("Finding the best candidates:").first
163
+ if message.match?(GIT_REFERENCE_NOT_FOUND_REGEX)
164
+ tag = message.match(GIT_REFERENCE_NOT_FOUND_REGEX).named_captures.fetch("tag")
165
+ constraints_section = message.split("Finding the best candidates:").first
160
166
  egg_regex = /#{Regexp.escape(tag)}#egg=(#{PYTHON_PACKAGE_NAME_REGEX})/
161
167
  name_match = constraints_section.scan(egg_regex)
162
168
 
@@ -166,15 +172,15 @@ module Dependabot
166
172
  raise GitDependencyReferenceNotFound, "(unknown package at #{tag})"
167
173
  end
168
174
 
169
- if error.message.match?(GIT_DEPENDENCY_UNREACHABLE_REGEX)
170
- url = error.message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX)
171
- .named_captures.fetch("url")
175
+ if message.match?(GIT_DEPENDENCY_UNREACHABLE_REGEX)
176
+ url = message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX)
177
+ .named_captures.fetch("url")
172
178
  raise GitDependenciesNotReachable, url
173
179
  end
174
180
 
175
- raise Dependabot::OutOfDisk if error.message.end_with?("[Errno 28] No space left on device")
181
+ raise Dependabot::OutOfDisk if message.end_with?("[Errno 28] No space left on device")
176
182
 
177
- raise Dependabot::OutOfMemory if error.message.end_with?("MemoryError")
183
+ raise Dependabot::OutOfMemory if message.end_with?("MemoryError")
178
184
 
179
185
  raise
180
186
  end
@@ -217,22 +223,7 @@ module Dependabot
217
223
  end
218
224
 
219
225
  def run_command(command, env: python_env, fingerprint:)
220
- start = Time.now
221
- command = SharedHelpers.escape_command(command)
222
- stdout, process = Open3.capture2e(env, command)
223
- time_taken = Time.now - start
224
-
225
- return stdout if process.success?
226
-
227
- raise SharedHelpers::HelperSubprocessFailed.new(
228
- message: stdout,
229
- error_context: {
230
- command: command,
231
- fingerprint: fingerprint,
232
- time_taken: time_taken,
233
- process_exit_value: process.to_s
234
- }
235
- )
226
+ SharedHelpers.run_shell_command(command, env: env, fingerprint: fingerprint, stderr_to_stdout: true)
236
227
  end
237
228
 
238
229
  def pip_compile_options_fingerprint(options)
@@ -381,26 +381,16 @@ module Dependabot
381
381
  .replace_sources(credentials)
382
382
  end
383
383
 
384
- def run_command(command, env: {})
385
- start = Time.now
386
- command = SharedHelpers.escape_command(command)
387
- stdout, process = Open3.capture2e(env, command)
388
- time_taken = Time.now - start
389
-
390
- return stdout if process.success?
391
-
392
- raise SharedHelpers::HelperSubprocessFailed.new(
393
- message: stdout,
394
- error_context: {
395
- command: command,
396
- time_taken: time_taken,
397
- process_exit_value: process.to_s
398
- }
399
- )
384
+ def run_command(command, env: {}, fingerprint: nil)
385
+ SharedHelpers.run_shell_command(command, env: env, fingerprint: fingerprint, stderr_to_stdout: true)
400
386
  end
401
387
 
402
388
  def run_pipenv_command(command, env: pipenv_env_variables)
403
- run_command("pyenv local #{language_version_manager.python_major_minor}")
389
+ run_command(
390
+ "pyenv local #{language_version_manager.python_major_minor}",
391
+ fingerprint: "pyenv local <python_major_minor>"
392
+ )
393
+
404
394
  run_command(command, env: env)
405
395
  end
406
396
 
@@ -14,7 +14,6 @@ require "dependabot/python/file_updater/pyproject_preparer"
14
14
  require "dependabot/python/update_checker"
15
15
  require "dependabot/python/version"
16
16
  require "dependabot/python/requirement"
17
- require "dependabot/python/helpers"
18
17
  require "dependabot/python/native_helpers"
19
18
  require "dependabot/python/authed_url_builder"
20
19
  require "dependabot/python/name_normaliser"
@@ -309,7 +308,7 @@ module Dependabot
309
308
  end
310
309
 
311
310
  def run_poetry_command(command, fingerprint: nil)
312
- Helpers.run_poetry_command(command, fingerprint: fingerprint)
311
+ SharedHelpers.run_shell_command(command, fingerprint: fingerprint)
313
312
  end
314
313
 
315
314
  def normalise(name)
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # These all need to be required so the various classes can be registered in a
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-python
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.234.0
4
+ version: 0.236.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-12 00:00:00.000000000 Z
11
+ date: 2023-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dependabot-common
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.234.0
19
+ version: 0.236.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.234.0
26
+ version: 0.236.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: debug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -224,11 +224,11 @@ files:
224
224
  - lib/dependabot/python/file_updater/requirement_file_updater.rb
225
225
  - lib/dependabot/python/file_updater/requirement_replacer.rb
226
226
  - lib/dependabot/python/file_updater/setup_file_sanitizer.rb
227
- - lib/dependabot/python/helpers.rb
228
227
  - lib/dependabot/python/language_version_manager.rb
229
228
  - lib/dependabot/python/metadata_finder.rb
230
229
  - lib/dependabot/python/name_normaliser.rb
231
230
  - lib/dependabot/python/native_helpers.rb
231
+ - lib/dependabot/python/pip_compile_file_matcher.rb
232
232
  - lib/dependabot/python/requirement.rb
233
233
  - lib/dependabot/python/requirement_parser.rb
234
234
  - lib/dependabot/python/update_checker.rb
@@ -245,7 +245,7 @@ licenses:
245
245
  - Nonstandard
246
246
  metadata:
247
247
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
248
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.234.0
248
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.236.0
249
249
  post_install_message:
250
250
  rdoc_options: []
251
251
  require_paths:
@@ -1,35 +0,0 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- require "time"
5
- require "open3"
6
-
7
- require "dependabot/errors"
8
- require "dependabot/shared_helpers"
9
-
10
- module Dependabot
11
- module Python
12
- module Helpers
13
- def self.run_poetry_command(command, fingerprint: nil)
14
- start = Time.now
15
- command = SharedHelpers.escape_command(command)
16
- stdout, stderr, process = Open3.capture3(command)
17
- time_taken = Time.now - start
18
-
19
- # Raise an error with the output from the shell session if Poetry
20
- # returns a non-zero status
21
- return stdout if process.success?
22
-
23
- raise SharedHelpers::HelperSubprocessFailed.new(
24
- message: stderr,
25
- error_context: {
26
- command: command,
27
- fingerprint: fingerprint,
28
- time_taken: time_taken,
29
- process_exit_value: process.to_s
30
- }
31
- )
32
- end
33
- end
34
- end
35
- end