dependabot-python 0.392.0 → 0.394.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: 70504f97ff35fbf437e2895455b6371d858c38921b3f4839a11aaddc31d04911
4
- data.tar.gz: 131699694996cabca8daec6c5a96f5a4dba34ec11601a22d7a8f54e6a855852f
3
+ metadata.gz: 0d1b1deb24d07d3b15f0c1cb2a434bc4014459ff9e52173a6b1aa1608bed9bef
4
+ data.tar.gz: c2a82f5e34e3f06ecfd16ad0516aa925d7cea4d564b9436befaf50ea5eaeb05f
5
5
  SHA512:
6
- metadata.gz: 56501fd6454a91206147b03fa795570e6a494e5d5c9733c8945656b174893352cd4adaa3bd2b4ee6c87565c3cc59dc861f62500d4c86fdfa3ea82c50afadda3f
7
- data.tar.gz: 8e1983671535f8886b5f7b920893a747d80b16535b74159c07da2ae82c1226dd66276fea515f28503217565955827ab03d51d5ac78dee42dce46d6bcd0cbe869
6
+ metadata.gz: c6f557bb606f5cdd0105035e2569dfb80410716ae0b6eb514c46d92837f4677107e2f22c309dd90fcd983c0b4a0d23a8ef596036f242560670de569b90a01b43
7
+ data.tar.gz: 7426a6a6748ef14ed637e82fa5757e5a11fc2c56fadb9e549b559a35e0bd831985978be092f6908068ae5cdd787d330ff95bc7e9b54dc0add588523a4a194d6d
@@ -1,5 +1,5 @@
1
- pip==26.1.2
2
- pip-tools==7.6.0
1
+ pip==26.2.1
2
+ pip-tools==7.6.1
3
3
  flake8==7.3.0
4
4
  hashin==1.0.5
5
5
  pipenv==2024.4.1
@@ -10,4 +10,4 @@ pytest==9.1.1
10
10
  tomli==2.4.1
11
11
 
12
12
  # Some dependencies will only install if Cython is present
13
- Cython==3.2.9
13
+ Cython==3.3.0
@@ -0,0 +1,53 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/python/file_parser"
7
+
8
+ module Dependabot
9
+ module Python
10
+ class FileParser < Dependabot::FileParsers::Base
11
+ class PepDependency < T::ImmutableStruct
12
+ extend T::Sig
13
+
14
+ const :name, String
15
+ const :version, T.nilable(String), default: nil
16
+ const :markers, T.nilable(String), default: nil
17
+ const :file, String
18
+ const :requirement, T.nilable(String), default: nil
19
+ const :source_requirement, T.nilable(String), default: nil
20
+ const :extras, T::Array[String]
21
+ const :requirement_type, T.nilable(String), default: nil
22
+
23
+ sig { params(result: Object).returns(T::Array[PepDependency]) }
24
+ def self.from_helper_result(result)
25
+ PyprojectValueParser.array(result, "PEP dependency result").map do |value|
26
+ from_object(value)
27
+ end
28
+ end
29
+
30
+ sig { params(value: Object).returns(PepDependency) }
31
+ def self.from_object(value)
32
+ hash = PyprojectValueParser.object_hash(value, "PEP dependency")
33
+ new(
34
+ name: PyprojectValueParser.string(hash["name"], "PEP dependency name"),
35
+ version: PyprojectValueParser.optional_string(hash["version"], "PEP dependency version"),
36
+ markers: PyprojectValueParser.optional_string(hash["markers"], "PEP dependency markers"),
37
+ file: PyprojectValueParser.string(hash["file"], "PEP dependency file"),
38
+ requirement: PyprojectValueParser.optional_string(hash["requirement"], "PEP dependency requirement"),
39
+ source_requirement: PyprojectValueParser.optional_string(
40
+ hash["source_requirement"],
41
+ "PEP dependency source_requirement"
42
+ ),
43
+ extras: PyprojectValueParser.string_array(hash["extras"], "PEP dependency extras"),
44
+ requirement_type: PyprojectValueParser.optional_string(
45
+ hash["requirement_type"],
46
+ "PEP dependency requirement_type"
47
+ )
48
+ )
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,95 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "toml-rb"
5
+ require "sorbet-runtime"
6
+
7
+ require "dependabot/dependency_file"
8
+ require "dependabot/errors"
9
+ require "dependabot/python/file_parser"
10
+
11
+ module Dependabot
12
+ module Python
13
+ class FileParser < Dependabot::FileParsers::Base
14
+ class PoetryLock
15
+ extend T::Sig
16
+
17
+ class Package < T::ImmutableStruct
18
+ extend T::Sig
19
+
20
+ const :name, String
21
+ const :version, String
22
+ const :source_type, T.nilable(String), default: nil
23
+
24
+ sig { params(value: Object).returns(Package) }
25
+ def self.from_object(value)
26
+ hash = PyprojectValueParser.object_hash(value, "Poetry lock package")
27
+ source = hash["source"]
28
+ source_hash =
29
+ if source.nil?
30
+ nil
31
+ else
32
+ PyprojectValueParser.object_hash(source, "Poetry lock package source")
33
+ end
34
+
35
+ new(
36
+ name: PyprojectValueParser.string(hash["name"], "Poetry lock package name"),
37
+ version: PyprojectValueParser.string(hash["version"], "Poetry lock package version"),
38
+ source_type: PyprojectValueParser.optional_string(
39
+ source_hash&.[]("type"),
40
+ "Poetry lock package source type"
41
+ )
42
+ )
43
+ end
44
+
45
+ sig { returns(T::Hash[Symbol, T.nilable(String)]) }
46
+ def to_h
47
+ {
48
+ name: name,
49
+ version: version,
50
+ source_type: source_type
51
+ }
52
+ end
53
+ end
54
+
55
+ sig { returns(T::Array[Package]) }
56
+ attr_reader :packages
57
+
58
+ sig { params(packages: T::Array[Package]).void }
59
+ def initialize(packages)
60
+ @packages = packages
61
+ end
62
+
63
+ sig { params(file: Dependabot::DependencyFile).returns(PoetryLock) }
64
+ def self.from_file(file)
65
+ parsed = T.cast(TomlRB.parse(T.must(file.content)), Object)
66
+ document = PyprojectValueParser.object_hash(parsed, "poetry.lock")
67
+ value = document["package"]
68
+ packages =
69
+ if value.nil?
70
+ []
71
+ else
72
+ PyprojectValueParser.array(value, "poetry.lock package").map do |package|
73
+ Package.from_object(package)
74
+ end
75
+ end
76
+ new(packages)
77
+ rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
78
+ raise Dependabot::DependencyFileNotParseable, file.path
79
+ end
80
+
81
+ sig do
82
+ params(
83
+ name: String,
84
+ _normalizer: T.proc.params(name: String).returns(String)
85
+ )
86
+ .returns(T.nilable(String))
87
+ end
88
+ def version_for(name, &_normalizer)
89
+ normalized_name = yield(name)
90
+ packages.find { |package| yield(package.name) == normalized_name }&.version
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,242 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "toml-rb"
5
+ require "sorbet-runtime"
6
+
7
+ require "dependabot/dependency_file"
8
+ require "dependabot/errors"
9
+ require "dependabot/python/file_parser"
10
+
11
+ module Dependabot
12
+ module Python
13
+ class FileParser < Dependabot::FileParsers::Base
14
+ module PyprojectValueParser
15
+ extend T::Sig
16
+
17
+ ObjectHash = T.type_alias { T::Hash[String, Object] }
18
+ PoetryRequirement = T.type_alias { T.any(String, ObjectHash) }
19
+ PoetryDependencyMap = T.type_alias { T::Hash[String, T::Array[PoetryRequirement]] }
20
+
21
+ sig { params(value: Object, context: String).returns(ObjectHash) }
22
+ def self.object_hash(value, context)
23
+ raise TypeError, "#{context} must be an object" unless value.is_a?(Hash)
24
+
25
+ result = T.let({}, ObjectHash)
26
+ value.each do |raw_key, raw_value|
27
+ key = T.cast(raw_key, Object)
28
+ raise TypeError, "#{context} keys must be strings" unless key.is_a?(String)
29
+
30
+ result[key] = T.cast(raw_value, Object)
31
+ end
32
+ result
33
+ end
34
+
35
+ sig { params(value: Object, context: String).returns(T::Array[Object]) }
36
+ def self.array(value, context)
37
+ raise TypeError, "#{context} must be an array" unless value.is_a?(Array)
38
+
39
+ value.map { |item| T.cast(item, Object) }
40
+ end
41
+
42
+ sig { params(value: Object, context: String).returns(String) }
43
+ def self.string(value, context)
44
+ return value if value.is_a?(String)
45
+
46
+ raise TypeError, "#{context} must be a string"
47
+ end
48
+
49
+ sig { params(value: T.nilable(Object), context: String).returns(T.nilable(String)) }
50
+ def self.optional_string(value, context)
51
+ return if value.nil?
52
+
53
+ string(value, context)
54
+ end
55
+
56
+ sig { params(value: Object, context: String).returns(T::Array[String]) }
57
+ def self.string_array(value, context)
58
+ array(value, context).map do |item|
59
+ raise TypeError, "#{context} must contain only strings" unless item.is_a?(String)
60
+
61
+ item
62
+ end
63
+ end
64
+
65
+ sig { params(value: Object, name: String).returns(T::Array[PoetryRequirement]) }
66
+ def self.poetry_requirements(value, name)
67
+ flattened = value.is_a?(Array) ? value.flatten : [value]
68
+ flattened.compact.map do |item|
69
+ item = T.cast(item, Object)
70
+ case item
71
+ when String
72
+ item
73
+ when Hash
74
+ object_hash(item, "Poetry dependency #{name}")
75
+ else
76
+ raise TypeError, "Poetry dependency #{name} must be a string, object, or array"
77
+ end
78
+ end
79
+ end
80
+
81
+ sig { params(value: Object, context: String).returns(PoetryDependencyMap) }
82
+ def self.poetry_dependency_map(value, context)
83
+ object_hash(value, context).to_h do |name, requirement|
84
+ [name, poetry_requirements(requirement, name)]
85
+ end
86
+ end
87
+ end
88
+ private_constant :PyprojectValueParser
89
+
90
+ class PyprojectDocument
91
+ extend T::Sig
92
+
93
+ PoetryRequirement = T.type_alias { PyprojectValueParser::PoetryRequirement }
94
+ PoetryDependencyMap = T.type_alias { PyprojectValueParser::PoetryDependencyMap }
95
+
96
+ class PoetrySource < T::ImmutableStruct
97
+ const :name, String
98
+ const :url, T.nilable(String), default: nil
99
+ end
100
+
101
+ sig { params(data: PyprojectValueParser::ObjectHash).void }
102
+ def initialize(data)
103
+ @data = data
104
+ end
105
+
106
+ sig { params(file: Dependabot::DependencyFile).returns(PyprojectDocument) }
107
+ 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"))
111
+ rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
112
+ raise Dependabot::DependencyFileNotParseable, file.path
113
+ end
114
+
115
+ sig { returns(T::Boolean) }
116
+ def poetry?
117
+ !poetry_root.nil?
118
+ end
119
+
120
+ sig { returns(T::Boolean) }
121
+ def pep621?
122
+ project = section(@data, "project", "project")
123
+ build_system = section(@data, "build-system", "build-system")
124
+
125
+ !project&.[]("dependencies").nil? ||
126
+ !project&.[]("optional-dependencies").nil? ||
127
+ !build_system&.[]("requires").nil?
128
+ end
129
+
130
+ sig { returns(T::Boolean) }
131
+ def pep735?
132
+ @data.key?("dependency-groups")
133
+ end
134
+
135
+ sig { params(type: String).returns(PoetryDependencyMap) }
136
+ def poetry_dependencies(type)
137
+ root = poetry_root
138
+ return {} unless root
139
+
140
+ value = root[type]
141
+ return {} if value.nil?
142
+
143
+ PyprojectValueParser.poetry_dependency_map(value, "tool.poetry.#{type}")
144
+ end
145
+
146
+ sig { returns(T::Hash[String, PoetryDependencyMap]) }
147
+ def poetry_groups
148
+ root = poetry_root
149
+ return {} unless root
150
+
151
+ raw_groups = root["group"]
152
+ return {} if raw_groups.nil?
153
+
154
+ parsed_groups = PyprojectValueParser.object_hash(raw_groups, "tool.poetry.group")
155
+ parsed_groups.each_with_object({}) do |(name, value), groups|
156
+ group = PyprojectValueParser.object_hash(value, "tool.poetry.group.#{name}")
157
+ dependencies = group["dependencies"]
158
+ next if dependencies.nil?
159
+
160
+ groups[name] = PyprojectValueParser.poetry_dependency_map(
161
+ dependencies,
162
+ "tool.poetry.group.#{name}.dependencies"
163
+ )
164
+ end
165
+ end
166
+
167
+ sig { returns(T::Array[String]) }
168
+ def dynamic_fields
169
+ project = section(@data, "project", "project")
170
+ value = project&.[]("dynamic")
171
+ return [] if value.nil?
172
+
173
+ PyprojectValueParser.string_array(value, "project.dynamic")
174
+ end
175
+
176
+ sig { params(name: String).returns(T::Boolean) }
177
+ def optional_dependency_group?(name)
178
+ project = section(@data, "project", "project")
179
+ value = project&.[]("optional-dependencies")
180
+ return false if value.nil?
181
+
182
+ PyprojectValueParser.object_hash(value, "project.optional-dependencies").key?(name)
183
+ end
184
+
185
+ sig { params(name: String).returns(T.nilable(PoetrySource)) }
186
+ def poetry_source(name)
187
+ poetry_sources.find { |source| source.name == name }
188
+ end
189
+
190
+ sig { params(key: String).returns(T::Array[String]) }
191
+ def workspace_globs(key)
192
+ tool = section(@data, "tool", "tool")
193
+ uv = tool && section(tool, "uv", "tool.uv")
194
+ workspace = uv && section(uv, "workspace", "tool.uv.workspace")
195
+ value = workspace&.[](key)
196
+ return [] if value.nil?
197
+
198
+ PyprojectValueParser.string_array(value, "tool.uv.workspace.#{key}")
199
+ end
200
+
201
+ private
202
+
203
+ sig { returns(T.nilable(PyprojectValueParser::ObjectHash)) }
204
+ def poetry_root
205
+ tool = section(@data, "tool", "tool")
206
+ tool && section(tool, "poetry", "tool.poetry")
207
+ end
208
+
209
+ sig { returns(T::Array[PoetrySource]) }
210
+ def poetry_sources
211
+ root = poetry_root
212
+ return [] unless root
213
+
214
+ value = root["source"]
215
+ return [] if value.nil?
216
+
217
+ PyprojectValueParser.array(value, "tool.poetry.source").map do |source|
218
+ source = PyprojectValueParser.object_hash(source, "tool.poetry.source entry")
219
+ PoetrySource.new(
220
+ name: PyprojectValueParser.string(source["name"], "Poetry source name"),
221
+ url: PyprojectValueParser.optional_string(source["url"], "Poetry source url")
222
+ )
223
+ end
224
+ end
225
+
226
+ sig do
227
+ params(
228
+ hash: PyprojectValueParser::ObjectHash,
229
+ key: String,
230
+ context: String
231
+ ).returns(T.nilable(PyprojectValueParser::ObjectHash))
232
+ end
233
+ def section(hash, key, context)
234
+ value = hash[key]
235
+ return if value.nil?
236
+
237
+ PyprojectValueParser.object_hash(value, context)
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end