dependabot-python 0.279.0 → 0.281.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: a5bf717c8f68ca481074e67a3358e97f609347162e8ebdefb1b961af9b3d401b
4
- data.tar.gz: b1565b6a609f5c0ed0aab1254cea234cd807c86bae05fcda662ebf2b13fbe480
3
+ metadata.gz: 1273f0793f6c0432a5a66468ec19a460c854eda211c935c45af189d1c7b11e9d
4
+ data.tar.gz: c938f342ad448550e12036631ecefe4623d5d16afebf21aa01bfe85a0c048ab2
5
5
  SHA512:
6
- metadata.gz: 6d0928e2b3ec818b2de96783f85e25c3afd9df6c68e9aa10a85177a62e2794bad043b0dc6b174c8e5f5e020505c3e4a33d465e663aede6c7a23080acbf5b761f
7
- data.tar.gz: 59598f676aff1150e4bd6db41c3911b05c0d056267309dedc1fa3bd49bce20d7d0e3d5c8a10e8d5838db14163fbae77461e7a7a7d3f705b539daa5b5b667c4ff
6
+ metadata.gz: a2ae4f68e9cdba267b00989165544e24384d8cd10b58c99802066214fb0b354a9c331af03e8b611a0195eda2d6d760c8cb354584b29b3a7b0d7f42e8e14067a7
7
+ data.tar.gz: 39a45d259c975b32debd5f3d21edd53622fb863630da7fe0bdafb6170f6adde5e3f556c36896cac357475b14797ac878855c4b9d171f7d29799bc8bd8ac20854
@@ -36,7 +36,17 @@ module Dependabot
36
36
  line = matches[1]
37
37
  end
38
38
 
39
- unless (matches = PATTERN.match(line))
39
+ pattern = PATTERN
40
+
41
+ if Dependabot::Experiments.enabled?(:python_new_version)
42
+ quoted = OPS.keys.sort_by(&:length).reverse
43
+ .map { |k| Regexp.quote(k) }.join("|")
44
+ version_pattern = Python::Version::NEW_VERSION_PATTERN
45
+ pattern_raw = "\\s*(?<op>#{quoted})?\\s*(?<version>#{version_pattern})\\s*".freeze
46
+ pattern = /\A#{pattern_raw}\z/
47
+ end
48
+
49
+ unless (matches = pattern.match(line))
40
50
  msg = "Illformed requirement [#{obj.inspect}]"
41
51
  raise BadRequirementError, msg
42
52
  end
@@ -128,7 +138,8 @@ module Dependabot
128
138
  upper_bound = parts.map.with_index do |part, i|
129
139
  if i < first_non_zero_index then part
130
140
  elsif i == first_non_zero_index then (part.to_i + 1).to_s
131
- elsif i > first_non_zero_index && i == 2 then "0.a"
141
+ # .dev has lowest precedence: https://packaging.python.org/en/latest/specifications/version-specifiers/#summary-of-permitted-suffixes-and-relative-ordering
142
+ elsif i > first_non_zero_index && i == 2 then "0.dev"
132
143
  else
133
144
  0
134
145
  end
@@ -151,7 +162,7 @@ module Dependabot
151
162
  .first(req_string.split(".").index { |s| s.include?("*") } + 1)
152
163
  .join(".")
153
164
  .gsub(/\*(?!$)/, "0")
154
- .gsub(/\*$/, "0.a")
165
+ .gsub(/\*$/, "0.dev")
155
166
  .tap { |s| exact_op ? s.gsub!(/^(?<!!)=*/, "~>") : s }
156
167
  end
157
168
  end
@@ -353,8 +353,21 @@ module Dependabot
353
353
  # package version mentioned in .toml not found in package index
354
354
  PACKAGE_NOT_FOUND = /Package (?<pkg>.*) ((?<req_ver>.*)) not found./
355
355
 
356
- # error code 401 while accessing registry
357
- ERROR_401 = /401 Client Error/
356
+ # client access error codes while accessing package index
357
+ CLIENT_ERROR_CODES = T.let({
358
+ error401: /401 Client Error/,
359
+ error403: /403 Client Error/,
360
+ error404: /404 Client Error/,
361
+ http403: /HTTP error 403/,
362
+ http404: /HTTP error 404/
363
+ }.freeze, T::Hash[T.nilable(String), Regexp])
364
+
365
+ # server response error codes while accessing package index
366
+ SERVER_ERROR_CODES = T.let({
367
+ server502: /502 Server Error/,
368
+ server503: /503 Server Error/,
369
+ server504: /504 Server Error/
370
+ }.freeze, T::Hash[T.nilable(String), Regexp])
358
371
 
359
372
  sig do
360
373
  params(
@@ -386,6 +399,8 @@ module Dependabot
386
399
 
387
400
  public
388
401
 
402
+ # rubocop:disable Metrics/AbcSize
403
+ # rubocop:disable Metrics/PerceivedComplexity
389
404
  sig { params(error: Exception).void }
390
405
  def handle_poetry_error(error)
391
406
  Dependabot.logger.warn(error.message)
@@ -403,11 +418,22 @@ module Dependabot
403
418
 
404
419
  raise DependencyFileNotResolvable, error.message if error.message.match(PYTHON_RANGE_NOT_SATISFIED)
405
420
 
406
- return unless error.message.match?(ERROR_401)
421
+ SERVER_ERROR_CODES.each do |(_error_codes, error_regex)|
422
+ next unless error.message.match?(error_regex)
407
423
 
408
- url = URI.extract(error.message).first.then { sanitize_url(_1) }
409
- raise PrivateSourceAuthenticationFailure, url
424
+ index_url = URI.extract(error.message.to_s).last .then { sanitize_url(_1) }
425
+ raise InconsistentRegistryResponse, index_url
426
+ end
427
+
428
+ CLIENT_ERROR_CODES.each do |(_error_codes, error_regex)|
429
+ next unless error.message.match?(error_regex)
430
+
431
+ index_url = URI.extract(error.message.to_s).last .then { sanitize_url(_1) }
432
+ raise PrivateSourceAuthenticationFailure, index_url
433
+ end
410
434
  end
435
+ # rubocop:enable Metrics/AbcSize
436
+ # rubocop:enable Metrics/PerceivedComplexity
411
437
  end
412
438
  end
413
439
  end
@@ -4,71 +4,333 @@
4
4
  require "dependabot/version"
5
5
  require "dependabot/utils"
6
6
 
7
- # Python versions can include a local version identifier, which Ruby can't
8
- # parse. This class augments Gem::Version with local version identifier info.
9
- # See https://www.python.org/dev/peps/pep-0440 for details.
7
+ # See https://packaging.python.org/en/latest/specifications/version-specifiers for spec details.
10
8
 
11
9
  module Dependabot
12
10
  module Python
13
11
  class Version < Dependabot::Version
12
+ sig { returns(Integer) }
14
13
  attr_reader :epoch
14
+
15
+ sig { returns(T::Array[Integer]) }
16
+ attr_reader :release_segment
17
+
18
+ sig { returns(T.nilable(T::Array[T.any(String, Integer)])) }
19
+ attr_reader :dev
20
+
21
+ sig { returns(T.nilable(T::Array[T.any(String, Integer)])) }
22
+ attr_reader :pre
23
+
24
+ sig { returns(T.nilable(T::Array[T.any(String, Integer)])) }
25
+ attr_reader :post
26
+
27
+ sig { returns(T.nilable(T::Array[T.any(String, Integer)])) }
28
+ attr_reader :local
29
+
15
30
  attr_reader :local_version
16
31
  attr_reader :post_release_version
17
32
 
33
+ INFINITY = 1000
34
+ NEGATIVE_INFINITY = -INFINITY
35
+
18
36
  # See https://peps.python.org/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions
37
+ NEW_VERSION_PATTERN = /
38
+ v?
39
+ (?:
40
+ (?:(?<epoch>[0-9]+)!)? # epoch
41
+ (?<release>[0-9]+(?:\.[0-9]+)*) # release
42
+ (?<pre> # prerelease
43
+ [-_\.]?
44
+ (?<pre_l>(a|b|c|rc|alpha|beta|pre|preview))
45
+ [-_\.]?
46
+ (?<pre_n>[0-9]+)?
47
+ )?
48
+ (?<post> # post release
49
+ (?:-(?<post_n1>[0-9]+))
50
+ |
51
+ (?:
52
+ [-_\.]?
53
+ (?<post_l>post|rev|r)
54
+ [-_\.]?
55
+ (?<post_n2>[0-9]+)?
56
+ )
57
+ )?
58
+ (?<dev> # dev release
59
+ [-_\.]?
60
+ (?<dev_l>dev)
61
+ [-_\.]?
62
+ (?<dev_n>[0-9]+)?
63
+ )?
64
+ )
65
+ (?:\+(?<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version
66
+ /ix
67
+
19
68
  VERSION_PATTERN = 'v?([1-9][0-9]*!)?[0-9]+[0-9a-zA-Z]*(?>\.[0-9a-zA-Z]+)*' \
20
69
  '(-[0-9A-Za-z]+(\.[0-9a-zA-Z]+)*)?' \
21
70
  '(\+[0-9a-zA-Z]+(\.[0-9a-zA-Z]+)*)?'
22
- ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/
23
71
 
72
+ ANCHORED_VERSION_PATTERN = /\A\s*#{VERSION_PATTERN}\s*\z/
73
+
74
+ sig { override.params(version: VersionParameter).returns(T::Boolean) }
24
75
  def self.correct?(version)
25
76
  return false if version.nil?
26
77
 
27
- version.to_s.match?(ANCHORED_VERSION_PATTERN)
78
+ if Dependabot::Experiments.enabled?(:python_new_version)
79
+ version.to_s.match?(/\A\s*#{NEW_VERSION_PATTERN}\s*\z/o)
80
+ else
81
+ version.to_s.match?(ANCHORED_VERSION_PATTERN)
82
+ end
28
83
  end
29
84
 
30
- def initialize(version)
85
+ sig { override.params(version: VersionParameter).void }
86
+ def initialize(version) # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity
87
+ raise Dependabot::BadRequirementError, "Malformed version string - string is nil" if version.nil?
88
+
31
89
  @version_string = version.to_s
32
- version, @local_version = @version_string.split("+")
33
- version ||= ""
34
- version = version.gsub(/^v/, "")
35
- if version.include?("!")
36
- @epoch, version = version.split("!")
90
+
91
+ raise Dependabot::BadRequirementError, "Malformed version string - string is empty" if @version_string.empty?
92
+
93
+ matches = anchored_version_pattern.match(@version_string.downcase)
94
+
95
+ unless matches
96
+ raise Dependabot::BadRequirementError,
97
+ "Malformed version string - #{@version_string} does not match regex"
98
+ end
99
+
100
+ if Dependabot::Experiments.enabled?(:python_new_version)
101
+ @epoch = matches["epoch"].to_i
102
+ @release_segment = matches["release"]&.split(".")&.map(&:to_i) || []
103
+ @pre = parse_letter_version(matches["pre_l"], matches["pre_n"])
104
+ @post = parse_letter_version(matches["post_l"], matches["post_n1"] || matches["post_n2"])
105
+ @dev = parse_letter_version(matches["dev_l"], matches["dev_n"])
106
+ @local = parse_local_version(matches["local"])
107
+ super(matches["release"] || "")
37
108
  else
38
- @epoch = "0"
109
+ version, @local_version = @version_string.split("+")
110
+ version ||= ""
111
+ version = version.gsub(/^v/, "")
112
+ if version.include?("!")
113
+ epoch, version = version.split("!")
114
+ @epoch = epoch.to_i
115
+ else
116
+ @epoch = 0
117
+ end
118
+ version = normalise_prerelease(version)
119
+ version, @post_release_version = version.split(/\.r(?=\d)/)
120
+ version ||= ""
121
+ @local_version = normalise_prerelease(@local_version) if @local_version
122
+ super
39
123
  end
40
- version = normalise_prerelease(version)
41
- version, @post_release_version = version.split(/\.r(?=\d)/)
42
- version ||= ""
43
- @local_version = normalise_prerelease(@local_version) if @local_version
44
- super
45
124
  end
46
125
 
126
+ sig { override.params(version: VersionParameter).returns(Dependabot::Python::Version) }
127
+ def self.new(version)
128
+ T.cast(super, Dependabot::Python::Version)
129
+ end
130
+
131
+ sig { returns(String) }
47
132
  def to_s
48
133
  @version_string
49
134
  end
50
135
 
136
+ sig { returns(String) }
51
137
  def inspect # :nodoc:
52
138
  "#<#{self.class} #{@version_string}>"
53
139
  end
54
140
 
55
- def <=>(other)
56
- other = Version.new(other.to_s) unless other.is_a?(Python::Version)
141
+ sig { returns(T::Boolean) }
142
+ def prerelease?
143
+ return super unless Dependabot::Experiments.enabled?(:python_new_version)
57
144
 
58
- epoch_comparison = epoch_comparison(other)
59
- return epoch_comparison unless epoch_comparison.zero?
145
+ !!(pre || dev)
146
+ end
147
+
148
+ sig { returns(T.any(Gem::Version, Dependabot::Python::Version)) }
149
+ def release
150
+ return super unless Dependabot::Experiments.enabled?(:python_new_version)
151
+
152
+ Dependabot::Python::Version.new(release_segment.join("."))
153
+ end
154
+
155
+ sig { params(other: VersionParameter).returns(Integer) }
156
+ def <=>(other) # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity
157
+ other = Dependabot::Python::Version.new(other.to_s) unless other.is_a?(Dependabot::Python::Version)
158
+ other = T.cast(other, Dependabot::Python::Version)
60
159
 
61
- version_comparison = super
62
- return version_comparison unless version_comparison&.zero?
160
+ if Dependabot::Experiments.enabled?(:python_new_version)
161
+ epoch_comparison = epoch <=> other.epoch
162
+ return epoch_comparison unless epoch_comparison.zero?
63
163
 
64
- post_version_comparison = post_version_comparison(other)
65
- return post_version_comparison unless post_version_comparison.zero?
164
+ release_comparison = release_version_comparison(other)
165
+ return release_comparison unless release_comparison.zero?
166
+
167
+ pre_comparison = compare_keys(pre_cmp_key, other.pre_cmp_key)
168
+ return pre_comparison unless pre_comparison.zero?
169
+
170
+ post_comparison = compare_keys(post_cmp_key, other.post_cmp_key)
171
+ return post_comparison unless post_comparison.zero?
172
+
173
+ dev_comparison = compare_keys(dev_cmp_key, other.dev_cmp_key)
174
+ return dev_comparison unless dev_comparison.zero?
175
+
176
+ compare_keys(local_cmp_key, other.local_cmp_key)
177
+ else
178
+ epoch_comparison = epoch_comparison(other)
179
+ return epoch_comparison unless epoch_comparison.zero?
66
180
 
67
- local_version_comparison(other)
181
+ version_comparison = super
182
+ return T.must(version_comparison) unless version_comparison&.zero?
183
+
184
+ post_version_comparison = post_version_comparison(other)
185
+ return post_version_comparison unless post_version_comparison.zero?
186
+
187
+ local_version_comparison(other)
188
+ end
189
+ end
190
+
191
+ sig do
192
+ params(
193
+ key: T.any(Integer, T::Array[T.any(String, Integer)]),
194
+ other_key: T.any(Integer, T::Array[T.any(String, Integer)])
195
+ ).returns(Integer)
196
+ end
197
+ def compare_keys(key, other_key)
198
+ if key.is_a?(Integer) && other_key.is_a?(Integer)
199
+ key <=> other_key
200
+ elsif key.is_a?(Array) && other_key.is_a?(Array)
201
+ key <=> other_key
202
+ elsif key.is_a?(Integer)
203
+ key == NEGATIVE_INFINITY ? -1 : 1
204
+ elsif other_key.is_a?(Integer)
205
+ other_key == NEGATIVE_INFINITY ? 1 : -1
206
+ end
207
+ end
208
+
209
+ sig { returns(T.any(Integer, T::Array[T.any(String, Integer)])) }
210
+ def pre_cmp_key
211
+ if pre.nil? && post.nil? && dev # sort 1.0.dev0 before 1.0a0
212
+ NEGATIVE_INFINITY
213
+ elsif pre.nil?
214
+ INFINITY # versions without a pre-release should sort after those with one.
215
+ else
216
+ T.must(pre)
217
+ end
218
+ end
219
+
220
+ sig { returns(T.any(Integer, T::Array[T.any(String, Integer)])) }
221
+ def local_cmp_key
222
+ if local.nil?
223
+ # Versions without a local segment should sort before those with one.
224
+ NEGATIVE_INFINITY
225
+ else
226
+ # According to PEP440.
227
+ # - Alphanumeric segments sort before numeric segments
228
+ # - Alphanumeric segments sort lexicographically
229
+ # - Numeric segments sort numerically
230
+ # - Shorter versions sort before longer versions when the prefixes match exactly
231
+ local&.map do |token|
232
+ if token.is_a?(Integer)
233
+ [token, ""]
234
+ else
235
+ [NEGATIVE_INFINITY, token]
236
+ end
237
+ end
238
+ end
239
+ end
240
+
241
+ sig { returns(T.any(Integer, T::Array[T.any(String, Integer)])) }
242
+ def post_cmp_key
243
+ # Versions without a post segment should sort before those with one.
244
+ return NEGATIVE_INFINITY if post.nil?
245
+
246
+ T.must(post)
247
+ end
248
+
249
+ sig { returns(T.any(Integer, T::Array[T.any(String, Integer)])) }
250
+ def dev_cmp_key
251
+ # Versions without a dev segment should sort after those with one.
252
+ return INFINITY if dev.nil?
253
+
254
+ T.must(dev)
68
255
  end
69
256
 
70
257
  private
71
258
 
259
+ sig { params(other: Dependabot::Python::Version).returns(Integer) }
260
+ def release_version_comparison(other)
261
+ tokens, other_tokens = pad_for_comparison(release_segment, other.release_segment)
262
+ tokens <=> other_tokens
263
+ end
264
+
265
+ sig do
266
+ params(
267
+ tokens: T::Array[Integer],
268
+ other_tokens: T::Array[Integer]
269
+ ).returns(T::Array[T::Array[Integer]])
270
+ end
271
+ def pad_for_comparison(tokens, other_tokens)
272
+ tokens = tokens.dup
273
+ other_tokens = other_tokens.dup
274
+
275
+ longer = [tokens, other_tokens].max_by(&:count)
276
+ shorter = [tokens, other_tokens].min_by(&:count)
277
+
278
+ difference = T.must(longer).length - T.must(shorter).length
279
+
280
+ difference.times { T.must(shorter) << 0 }
281
+
282
+ [tokens, other_tokens]
283
+ end
284
+
285
+ sig { params(local: T.nilable(String)).returns(T.nilable(T::Array[T.any(String, Integer)])) }
286
+ def parse_local_version(local)
287
+ return if local.nil?
288
+
289
+ # Takes a string like abc.1.twelve and turns it into ["abc", 1, "twelve"]
290
+ local.split(/[\._-]/).map { |s| /^\d+$/.match?(s) ? s.to_i : s }
291
+ end
292
+
293
+ sig do
294
+ params(
295
+ letter: T.nilable(String), number: T.nilable(String)
296
+ ).returns(T.nilable(T::Array[T.any(String, Integer)]))
297
+ end
298
+ def parse_letter_version(letter = nil, number = nil)
299
+ return if letter.nil? && number.nil?
300
+
301
+ if letter
302
+ # Implicit 0 for cases where prerelease has no numeral
303
+ number ||= 0
304
+
305
+ # Normalize alternate spellings
306
+ if letter == "alpha"
307
+ letter = "a"
308
+ elsif letter == "beta"
309
+ letter = "b"
310
+ elsif %w(c pre preview).include? letter
311
+ letter = "rc"
312
+ elsif %w(rev r).include? letter
313
+ letter = "post"
314
+ end
315
+
316
+ return letter, number.to_i
317
+ end
318
+
319
+ # Number but no letter i.e. implicit post release syntax (e.g. 1.0-1)
320
+ letter = "post"
321
+
322
+ [letter, number.to_i]
323
+ end
324
+
325
+ sig { returns(Regexp) }
326
+ def anchored_version_pattern
327
+ if Dependabot::Experiments.enabled?(:python_new_version)
328
+ /\A\s*#{NEW_VERSION_PATTERN}\s*\z/o
329
+ else
330
+ ANCHORED_VERSION_PATTERN
331
+ end
332
+ end
333
+
72
334
  def epoch_comparison(other)
73
335
  epoch.to_i <=> other.epoch.to_i
74
336
  end
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.279.0
4
+ version: 0.281.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-03 00:00:00.000000000 Z
11
+ date: 2024-10-17 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.279.0
19
+ version: 0.281.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.279.0
26
+ version: 0.281.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: debug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -156,14 +156,14 @@ dependencies:
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: 0.8.1
159
+ version: 0.8.5
160
160
  type: :development
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: 0.8.1
166
+ version: 0.8.5
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: simplecov
169
169
  requirement: !ruby/object:Gem::Requirement
@@ -288,7 +288,7 @@ licenses:
288
288
  - MIT
289
289
  metadata:
290
290
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
291
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.279.0
291
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.281.0
292
292
  post_install_message:
293
293
  rdoc_options: []
294
294
  require_paths: