dependabot-python 0.394.0 → 0.395.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: 0d1b1deb24d07d3b15f0c1cb2a434bc4014459ff9e52173a6b1aa1608bed9bef
4
- data.tar.gz: c2a82f5e34e3f06ecfd16ad0516aa925d7cea4d564b9436befaf50ea5eaeb05f
3
+ metadata.gz: 0a1dec50c1cd900eb433c984eb046d5a4b4e327c036d83c09e4d90ce1dd93572
4
+ data.tar.gz: 608eb3b97ebcc5ee85f66fda06bb4f1f2407e244f3f582c333ec9cf604edd90e
5
5
  SHA512:
6
- metadata.gz: c6f557bb606f5cdd0105035e2569dfb80410716ae0b6eb514c46d92837f4677107e2f22c309dd90fcd983c0b4a0d23a8ef596036f242560670de569b90a01b43
7
- data.tar.gz: 7426a6a6748ef14ed637e82fa5757e5a11fc2c56fadb9e549b559a35e0bd831985978be092f6908068ae5cdd787d330ff95bc7e9b54dc0add588523a4a194d6d
6
+ metadata.gz: 5b2de818a1e9b73a7b1af590c4f9bdf3c5ecf64d8bdd36e5043831e9d781f6fb829fe3282e0080e129595eb5adc49631c4d956bc1501dcd623fbeab8a8ba7b2a
7
+ data.tar.gz: 90ae8a95010a4a414c1fdffd2d9ad371c3ff3418852191297d03699abadb62b5d7aa4b7ed9dae5e288bd246c7475f7ee29b8d7ed11f84b822b9efb2cc30ab903
@@ -98,6 +98,11 @@ module Dependabot
98
98
  const :url, T.nilable(String), default: nil
99
99
  end
100
100
 
101
+ class ProjectMetadata < T::ImmutableStruct
102
+ const :name, T.nilable(String), default: nil
103
+ const :description, T.nilable(String), default: nil
104
+ end
105
+
101
106
  sig { params(data: PyprojectValueParser::ObjectHash).void }
102
107
  def initialize(data)
103
108
  @data = data
@@ -105,18 +110,42 @@ module Dependabot
105
110
 
106
111
  sig { params(file: Dependabot::DependencyFile).returns(PyprojectDocument) }
107
112
  def self.from_file(file)
108
- content = T.must(file.content)
109
- parsed = T.cast(TomlRB.parse(content), Object)
110
- new(PyprojectValueParser.object_hash(parsed, "pyproject.toml"))
113
+ from_content(T.must(file.content))
111
114
  rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
112
115
  raise Dependabot::DependencyFileNotParseable, file.path
113
116
  end
114
117
 
118
+ sig { params(content: String).returns(PyprojectDocument) }
119
+ def self.from_content(content)
120
+ parsed = T.cast(TomlRB.parse(content), Object)
121
+ new(PyprojectValueParser.object_hash(parsed, "pyproject.toml"))
122
+ end
123
+
115
124
  sig { returns(T::Boolean) }
116
125
  def poetry?
117
126
  !poetry_root.nil?
118
127
  end
119
128
 
129
+ sig { returns(T::Boolean) }
130
+ def project?
131
+ !section(@data, "project", "project").nil?
132
+ end
133
+
134
+ sig { returns(T.nilable(ProjectMetadata)) }
135
+ def poetry_metadata
136
+ metadata_from(poetry_root, "tool.poetry")
137
+ end
138
+
139
+ sig { returns(T.nilable(ProjectMetadata)) }
140
+ def project_metadata
141
+ metadata_from(section(@data, "project", "project"), "project")
142
+ end
143
+
144
+ sig { returns(T.nilable(ProjectMetadata)) }
145
+ def build_system_metadata
146
+ metadata_from(section(@data, "build-system", "build-system"), "build-system")
147
+ end
148
+
120
149
  sig { returns(T::Boolean) }
121
150
  def pep621?
122
151
  project = section(@data, "project", "project")
@@ -200,6 +229,19 @@ module Dependabot
200
229
 
201
230
  private
202
231
 
232
+ sig do
233
+ params(data: T.nilable(PyprojectValueParser::ObjectHash), context: String)
234
+ .returns(T.nilable(ProjectMetadata))
235
+ end
236
+ def metadata_from(data, context)
237
+ return unless data
238
+
239
+ ProjectMetadata.new(
240
+ name: PyprojectValueParser.optional_string(data["name"], "#{context}.name"),
241
+ description: PyprojectValueParser.optional_string(data["description"], "#{context}.description")
242
+ )
243
+ end
244
+
203
245
  sig { returns(T.nilable(PyprojectValueParser::ObjectHash)) }
204
246
  def poetry_root
205
247
  tool = section(@data, "tool", "tool")
@@ -17,7 +17,7 @@ require "dependabot/python/authed_url_builder"
17
17
  module Dependabot
18
18
  module Python
19
19
  class FileUpdater
20
- # rubocop:disable Metrics/ClassLength
20
+ # rubocop:disable-next Metrics/ClassLength
21
21
  class PipCompileFileUpdater
22
22
  extend T::Sig
23
23
 
@@ -466,6 +466,7 @@ module Dependabot
466
466
  end
467
467
 
468
468
  sig { params(name: String, version: String, algorithm: String).returns(T::Array[String]) }
469
+ # rubocop:disable-next Metrics/PerceivedComplexity
469
470
  def package_hashes_for(name:, version:, algorithm:)
470
471
  index_urls = @index_urls || [nil]
471
472
  hashes = []
@@ -477,12 +478,17 @@ module Dependabot
477
478
  args << index_url if index_url
478
479
 
479
480
  begin
481
+ helper_result = SharedHelpers.run_helper_subprocess(
482
+ command: "pyenv exec python3 #{NativeHelpers.python_helper_path}",
483
+ function: "get_dependency_hash",
484
+ args: args
485
+ )
486
+
487
+ # Skip this index if helper returned nil (can happen with unavailable registries)
488
+ next if helper_result.nil?
489
+
480
490
  native_helper_hashes = T.cast(
481
- SharedHelpers.run_helper_subprocess(
482
- command: "pyenv exec python3 #{NativeHelpers.python_helper_path}",
483
- function: "get_dependency_hash",
484
- args: args
485
- ),
491
+ helper_result,
486
492
  T::Array[T::Hash[String, String]]
487
493
  ).map { |h| "--hash=#{algorithm}:#{h['hash']}" }
488
494
 
@@ -494,9 +500,10 @@ module Dependabot
494
500
  end
495
501
  end
496
502
 
503
+ raise DependencyFileNotResolvable, "Unable to find hashes for package #{name}" if hashes.empty?
504
+
497
505
  hashes
498
506
  end
499
-
500
507
  sig { params(requirement_string: String).returns(T.nilable(String)) }
501
508
  def hash_separator(requirement_string)
502
509
  hash_regex = RequirementParser::HASH
@@ -807,7 +814,6 @@ module Dependabot
807
814
  dependency_files.select { |f| f.name.end_with?("setup.cfg") }
808
815
  end
809
816
  end
810
- # rubocop:enable Metrics/ClassLength
811
817
  end
812
818
  end
813
819
  end
@@ -19,7 +19,7 @@ module Dependabot
19
19
  class UpdateChecker
20
20
  # This resolver intentionally co-locates resolution, marker handling, and
21
21
  # constraints matching to keep compatibility decisions in one place.
22
- # rubocop:disable Metrics/ClassLength
22
+ # rubocop:disable-next Metrics/ClassLength
23
23
  class PipVersionResolver
24
24
  extend T::Sig
25
25
 
@@ -575,7 +575,6 @@ module Dependabot
575
575
  T.must(@pyproject_content_cache[pyproject.name])
576
576
  end
577
577
  end
578
- # rubocop:enable Metrics/ClassLength
579
578
  end
580
579
  end
581
580
  end
@@ -12,7 +12,7 @@ require "dependabot/requirements_update_strategy"
12
12
  module Dependabot
13
13
  module Python
14
14
  class UpdateChecker
15
- # rubocop:disable Metrics/ClassLength
15
+ # rubocop:disable-next Metrics/ClassLength
16
16
  class RequirementsUpdater
17
17
  extend T::Sig
18
18
 
@@ -358,7 +358,7 @@ module Dependabot
358
358
  new_segments
359
359
  .first(count)
360
360
  .map.with_index { |s, i| i < precision ? s : "*" }
361
- .join(".")
361
+ .join(".")
362
362
  end
363
363
 
364
364
  sig { params(requirement_strings: T::Array[String]).returns(String) }
@@ -542,7 +542,6 @@ module Dependabot
542
542
  Python::Requirement
543
543
  end
544
544
  end
545
- # rubocop:enable Metrics/ClassLength
546
545
  end
547
546
  end
548
547
  end
@@ -2,12 +2,13 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "excon"
5
- require "toml-rb"
5
+ require "json"
6
6
  require "sorbet-runtime"
7
7
 
8
8
  require "dependabot/dependency"
9
9
  require "dependabot/dependency_requirement"
10
10
  require "dependabot/errors"
11
+ require "dependabot/python/file_parser"
11
12
  require "dependabot/python/name_normaliser"
12
13
  require "dependabot/python/requirement_parser"
13
14
  require "dependabot/python/requirement"
@@ -18,7 +19,7 @@ require "dependabot/update_checkers/base"
18
19
 
19
20
  module Dependabot
20
21
  module Python
21
- # rubocop:disable Metrics/ClassLength
22
+ # rubocop:disable-next Metrics/ClassLength
22
23
  class UpdateChecker < Dependabot::UpdateCheckers::Base
23
24
  extend T::Sig
24
25
 
@@ -29,6 +30,8 @@ module Dependabot
29
30
  require_relative "update_checker/requirements_updater"
30
31
  require_relative "update_checker/latest_version_finder"
31
32
 
33
+ PyprojectDocument = FileParser::PyprojectDocument
34
+
32
35
  MAIN_PYPI_INDEXES = %w(
33
36
  https://pypi.python.org/simple/
34
37
  https://pypi.org/simple/
@@ -248,7 +251,7 @@ module Dependabot
248
251
  # For hybrid projects with both [tool.poetry] and [project] sections but no lockfile,
249
252
  # use the requirements resolver to handle PEP 621 dependencies
250
253
  # For pure Poetry projects, use Poetry resolver even without lockfile
251
- return :poetry if poetry_based? && (poetry_lock || !standard_details)
254
+ return :poetry if poetry_based? && (poetry_lock || !pyproject_document.project?)
252
255
 
253
256
  :requirements
254
257
  end
@@ -395,7 +398,7 @@ module Dependabot
395
398
 
396
399
  sig { returns(T::Boolean) }
397
400
  def poetry_based?
398
- updating_pyproject? && !poetry_details.nil?
401
+ updating_pyproject? && pyproject_document.poetry?
399
402
  end
400
403
 
401
404
  sig { returns(T::Boolean) }
@@ -410,22 +413,41 @@ module Dependabot
410
413
  def check_pypi_for_library_match
411
414
  return false unless updating_pyproject?
412
415
 
413
- library_details_temp = library_details
414
- return false unless library_details_temp && !library_details_temp["name"].nil?
416
+ metadata = library_details
417
+ name = metadata&.name
418
+ return false unless name
415
419
 
416
- has_library_metadata = !library_details_temp["description"].nil?
420
+ local_description = metadata.description
421
+ has_library_metadata = !local_description.nil?
417
422
 
418
- response = Dependabot::RegistryClient.get(
419
- url: "https://pypi.org/pypi/#{normalised_name(library_details_temp['name'])}/json/"
420
- )
421
- return has_library_metadata unless response.status == 200
423
+ begin
424
+ response = Dependabot::RegistryClient.get(
425
+ url: "https://pypi.org/pypi/#{normalised_name(name)}/json/"
426
+ )
427
+ return has_library_metadata unless response.status == 200
422
428
 
423
- local_description = library_details_temp["description"]
424
- return true if local_description.nil?
429
+ return true if local_description.nil?
425
430
 
426
- (JSON.parse(response.body)["info"] || {})["summary"] == local_description
427
- rescue Excon::Error::Timeout, Excon::Error::Socket, URI::InvalidURIError
428
- has_library_metadata
431
+ pypi_summary(response.body) == local_description
432
+ rescue Excon::Error::Timeout, Excon::Error::Socket, URI::InvalidURIError
433
+ has_library_metadata
434
+ end
435
+ end
436
+
437
+ sig { params(body: String).returns(T.nilable(String)) }
438
+ def pypi_summary(body)
439
+ metadata = T.cast(JSON.parse(body), Object)
440
+ raise TypeError, "PyPI metadata must be an object" unless metadata.is_a?(Hash)
441
+
442
+ info = T.cast(metadata["info"], Object)
443
+ return if info.nil?
444
+ raise TypeError, "PyPI info must be an object" unless info.is_a?(Hash)
445
+
446
+ summary = T.cast(info["summary"], Object)
447
+ return if summary.nil?
448
+ return summary if summary.is_a?(String)
449
+
450
+ raise TypeError, "PyPI info.summary must be a string"
429
451
  end
430
452
 
431
453
  sig { returns(T::Boolean) }
@@ -483,43 +505,21 @@ module Dependabot
483
505
  dependency_files.find { |f| f.name == "poetry.lock" }
484
506
  end
485
507
 
486
- sig { returns(T.nilable(T::Hash[String, T.untyped])) }
508
+ sig { returns(T.nilable(PyprojectDocument::ProjectMetadata)) }
487
509
  def library_details
488
510
  @library_details ||= T.let(
489
- poetry_details || standard_details || build_system_details,
490
- T.nilable(T::Hash[String, T.untyped])
491
- )
492
- end
493
-
494
- sig { returns(T.nilable(T::Hash[String, T.untyped])) }
495
- def poetry_details
496
- @poetry_details ||= T.let(
497
- toml_content.dig("tool", "poetry"),
498
- T.nilable(T::Hash[String, T.untyped])
499
- )
500
- end
501
-
502
- sig { returns(T.nilable(T::Hash[String, T.untyped])) }
503
- def standard_details
504
- @standard_details ||= T.let(
505
- toml_content["project"],
506
- T.nilable(T::Hash[String, T.untyped])
507
- )
508
- end
509
-
510
- sig { returns(T.nilable(T::Hash[String, T.untyped])) }
511
- def build_system_details
512
- @build_system_details ||= T.let(
513
- toml_content["build-system"],
514
- T.nilable(T::Hash[String, T.untyped])
511
+ pyproject_document.poetry_metadata ||
512
+ pyproject_document.project_metadata ||
513
+ pyproject_document.build_system_metadata,
514
+ T.nilable(PyprojectDocument::ProjectMetadata)
515
515
  )
516
516
  end
517
517
 
518
- sig { returns(T::Hash[String, T.untyped]) }
519
- def toml_content
520
- @toml_content ||= T.let(
521
- TomlRB.parse(T.must(pyproject).content),
522
- T.nilable(T::Hash[String, T.untyped])
518
+ sig { returns(PyprojectDocument) }
519
+ def pyproject_document
520
+ @pyproject_document ||= T.let(
521
+ PyprojectDocument.from_content(T.must(T.must(pyproject).content)),
522
+ T.nilable(PyprojectDocument)
523
523
  )
524
524
  end
525
525
 
@@ -528,7 +528,6 @@ module Dependabot
528
528
  dependency_files.select { |f| f.name.end_with?(".in") }
529
529
  end
530
530
  end
531
- # rubocop:enable Metrics/ClassLength
532
531
  end
533
532
  end
534
533
 
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.394.0
4
+ version: 0.395.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.394.0
18
+ version: 0.395.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.394.0
25
+ version: 0.395.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: debug
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -178,19 +178,19 @@ dependencies:
178
178
  - !ruby/object:Gem::Version
179
179
  version: '1.1'
180
180
  - !ruby/object:Gem::Dependency
181
- name: turbo_tests
181
+ name: turbo_tests2
182
182
  requirement: !ruby/object:Gem::Requirement
183
183
  requirements:
184
184
  - - "~>"
185
185
  - !ruby/object:Gem::Version
186
- version: 2.2.5
186
+ version: 3.2.7
187
187
  type: :development
188
188
  prerelease: false
189
189
  version_requirements: !ruby/object:Gem::Requirement
190
190
  requirements:
191
191
  - - "~>"
192
192
  - !ruby/object:Gem::Version
193
- version: 2.2.5
193
+ version: 3.2.7
194
194
  - !ruby/object:Gem::Dependency
195
195
  name: vcr
196
196
  requirement: !ruby/object:Gem::Requirement
@@ -327,7 +327,7 @@ licenses:
327
327
  - MIT
328
328
  metadata:
329
329
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
330
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.394.0
330
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.395.0
331
331
  rdoc_options: []
332
332
  require_paths:
333
333
  - lib