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 +4 -4
- data/helpers/requirements.txt +3 -3
- data/lib/dependabot/python/file_parser/pep_dependency.rb +53 -0
- data/lib/dependabot/python/file_parser/poetry_lock.rb +95 -0
- data/lib/dependabot/python/file_parser/pyproject_document.rb +242 -0
- data/lib/dependabot/python/file_parser/pyproject_files_parser.rb +134 -128
- data/lib/dependabot/python/file_parser/setup_file_parser.rb +26 -8
- data/lib/dependabot/python/file_parser.rb +3 -0
- data/lib/dependabot/python/file_updater/pipfile_file_updater.rb +13 -4
- data/lib/dependabot/python/file_updater/poetry_file_updater.rb +13 -2
- data/lib/dependabot/python/package/package_details_fetcher.rb +81 -35
- data/lib/dependabot/python/package/package_registry_finder.rb +11 -4
- data/lib/dependabot/python/package/simple_api_parser.rb +91 -0
- data/lib/dependabot/python/shared_file_fetcher.rb +1 -2
- data/lib/dependabot/python/update_checker/latest_version_finder.rb +16 -0
- data/lib/dependabot/python/update_checker.rb +4 -4
- metadata +10 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0d1b1deb24d07d3b15f0c1cb2a434bc4014459ff9e52173a6b1aa1608bed9bef
|
|
4
|
+
data.tar.gz: c2a82f5e34e3f06ecfd16ad0516aa925d7cea4d564b9436befaf50ea5eaeb05f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c6f557bb606f5cdd0105035e2569dfb80410716ae0b6eb514c46d92837f4677107e2f22c309dd90fcd983c0b4a0d23a8ef596036f242560670de569b90a01b43
|
|
7
|
+
data.tar.gz: 7426a6a6748ef14ed637e82fa5757e5a11fc2c56fadb9e549b559a35e0bd831985978be092f6908068ae5cdd787d330ff95bc7e9b54dc0add588523a4a194d6d
|
data/helpers/requirements.txt
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
pip==26.1
|
|
2
|
-
pip-tools==7.6.
|
|
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.
|
|
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
|