dependabot-python 0.212.0 → 0.214.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/build +5 -6
- data/helpers/lib/parser.py +59 -0
- data/helpers/requirements.txt +3 -3
- data/helpers/run.py +2 -0
- data/lib/dependabot/python/file_fetcher.rb +21 -12
- data/lib/dependabot/python/file_parser/{poetry_files_parser.rb → pyproject_files_parser.rb} +84 -2
- data/lib/dependabot/python/file_parser/setup_file_parser.rb +4 -4
- data/lib/dependabot/python/file_parser.rb +5 -29
- data/lib/dependabot/python/file_updater/pip_compile_file_updater.rb +7 -22
- data/lib/dependabot/python/file_updater/pipfile_file_updater.rb +11 -8
- data/lib/dependabot/python/file_updater/pipfile_preparer.rb +6 -4
- data/lib/dependabot/python/file_updater/poetry_file_updater.rb +15 -7
- data/lib/dependabot/python/file_updater/pyproject_preparer.rb +16 -1
- data/lib/dependabot/python/file_updater.rb +14 -1
- data/lib/dependabot/python/helpers.rb +37 -0
- data/lib/dependabot/python/metadata_finder.rb +2 -0
- data/lib/dependabot/python/python_versions.rb +11 -7
- data/lib/dependabot/python/requirement.rb +7 -4
- data/lib/dependabot/python/requirement_parser.rb +20 -23
- data/lib/dependabot/python/update_checker/index_finder.rb +1 -1
- data/lib/dependabot/python/update_checker/latest_version_finder.rb +2 -2
- data/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb +19 -21
- data/lib/dependabot/python/update_checker/pipenv_version_resolver.rb +16 -18
- data/lib/dependabot/python/update_checker/poetry_version_resolver.rb +14 -12
- data/lib/dependabot/python/update_checker/requirements_updater.rb +17 -4
- data/lib/dependabot/python/update_checker.rb +82 -25
- data/lib/dependabot/python/version.rb +2 -2
- metadata +15 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 940ed0c4abf7f4d3a496321e4898ba9c123091d6539f86ef54d7ee74dadf3344
|
4
|
+
data.tar.gz: 802abe558f75bc2e98f1b88e93be85fc48f8b71774a1ff37b8ea16311381f587
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 523ff39717afd9636f3d2f3115d6953817ab01585e2e218233eb0439a7cc9e5ac620c4b28d429b35256530a32bff6e71a73ffdfd72587ba53c8b10b6a3070175
|
7
|
+
data.tar.gz: a3d05a60ad4d1b08dfe8fed7cdac12384aa49fbb3ad130008bf4748ab710df9b20a8297c99f66e33bd672717b52be32c17434b2ed253fe4bb6556cfc87941b05
|
data/helpers/build
CHANGED
@@ -18,9 +18,8 @@ cp -r \
|
|
18
18
|
"$install_dir"
|
19
19
|
|
20
20
|
cd "$install_dir"
|
21
|
-
PYENV_VERSION=3.
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
PYENV_VERSION=3.10.5 pyenv exec poetry config experimental.new-installer false
|
21
|
+
PYENV_VERSION=3.11.0 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
|
22
|
+
PYENV_VERSION=3.10.8 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
|
23
|
+
PYENV_VERSION=3.9.15 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
|
24
|
+
PYENV_VERSION=3.8.15 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
|
25
|
+
PYENV_VERSION=3.7.15 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
|
data/helpers/lib/parser.py
CHANGED
@@ -11,11 +11,70 @@ from pip._internal.req.constructors import (
|
|
11
11
|
install_req_from_line,
|
12
12
|
install_req_from_parsed_requirement,
|
13
13
|
)
|
14
|
+
|
15
|
+
from packaging.requirements import InvalidRequirement, Requirement
|
16
|
+
import toml
|
17
|
+
|
14
18
|
# Inspired by pips internal check:
|
15
19
|
# https://github.com/pypa/pip/blob/0bb3ac87f5bb149bd75cceac000844128b574385/src/pip/_internal/req/req_file.py#L35
|
16
20
|
COMMENT_RE = re.compile(r'(^|\s+)#.*$')
|
17
21
|
|
18
22
|
|
23
|
+
def parse_pep621_dependencies(pyproject_path):
|
24
|
+
project_toml = toml.load(pyproject_path)['project']
|
25
|
+
|
26
|
+
def parse_toml_section_pep621_dependencies(pyproject_path, dependencies):
|
27
|
+
requirement_packages = []
|
28
|
+
|
29
|
+
def version_from_req(specifier_set):
|
30
|
+
if (len(specifier_set) == 1 and
|
31
|
+
next(iter(specifier_set)).operator in {"==", "==="}):
|
32
|
+
return next(iter(specifier_set)).version
|
33
|
+
|
34
|
+
for dependency in dependencies:
|
35
|
+
try:
|
36
|
+
req = Requirement(dependency)
|
37
|
+
except InvalidRequirement as e:
|
38
|
+
print(json.dumps({"error": repr(e)}))
|
39
|
+
exit(1)
|
40
|
+
else:
|
41
|
+
requirement_packages.append({
|
42
|
+
"name": req.name,
|
43
|
+
"version": version_from_req(req.specifier),
|
44
|
+
"markers": str(req.marker) or None,
|
45
|
+
"file": pyproject_path,
|
46
|
+
"requirement": str(req.specifier),
|
47
|
+
"extras": sorted(list(req.extras))
|
48
|
+
})
|
49
|
+
|
50
|
+
return requirement_packages
|
51
|
+
|
52
|
+
dependencies = []
|
53
|
+
|
54
|
+
if 'dependencies' in project_toml:
|
55
|
+
dependencies_toml = project_toml['dependencies']
|
56
|
+
|
57
|
+
runtime_dependencies = parse_toml_section_pep621_dependencies(
|
58
|
+
pyproject_path,
|
59
|
+
dependencies_toml
|
60
|
+
)
|
61
|
+
|
62
|
+
dependencies.extend(runtime_dependencies)
|
63
|
+
|
64
|
+
if 'optional-dependencies' in project_toml:
|
65
|
+
optional_dependencies_toml = project_toml['optional-dependencies']
|
66
|
+
|
67
|
+
for group in optional_dependencies_toml:
|
68
|
+
group_dependencies = parse_toml_section_pep621_dependencies(
|
69
|
+
pyproject_path,
|
70
|
+
optional_dependencies_toml[group]
|
71
|
+
)
|
72
|
+
|
73
|
+
dependencies.extend(group_dependencies)
|
74
|
+
|
75
|
+
return json.dumps({"result": dependencies})
|
76
|
+
|
77
|
+
|
19
78
|
def parse_requirements(directory):
|
20
79
|
# Parse the requirements.txt
|
21
80
|
requirement_packages = []
|
data/helpers/requirements.txt
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
pip>=21.3.1,<22.
|
2
|
-
pip-tools>=6.4.0,<6.
|
1
|
+
pip>=21.3.1,<22.4.0 # Range maintains py36 support TODO: Review python 3.6 support in April 2023 (eol ubuntu 18.04)
|
2
|
+
pip-tools>=6.4.0,<6.10.1 # Range maintains py36 support TODO: Review python 3.6 support in April 2023 (eol ubuntu 18.04)
|
3
3
|
flake8==5.0.4
|
4
4
|
hashin==0.17.0
|
5
5
|
pipenv==2022.4.8
|
6
6
|
pipfile==0.0.2
|
7
|
-
poetry>=1.1.15
|
7
|
+
poetry>=1.1.15,<1.3.0
|
8
8
|
wheel==0.37.1
|
9
9
|
|
10
10
|
# Some dependencies will only install if Cython is present
|
data/helpers/run.py
CHANGED
@@ -10,6 +10,8 @@ if __name__ == "__main__":
|
|
10
10
|
print(parser.parse_requirements(args["args"][0]))
|
11
11
|
elif args["function"] == "parse_setup":
|
12
12
|
print(parser.parse_setup(args["args"][0]))
|
13
|
+
elif args["function"] == "parse_pep621_dependencies":
|
14
|
+
print(parser.parse_pep621_dependencies(args["args"][0]))
|
13
15
|
elif args["function"] == "get_dependency_hash":
|
14
16
|
print(hasher.get_dependency_hash(*args["args"]))
|
15
17
|
elif args["function"] == "get_pipfile_hash":
|
@@ -5,15 +5,15 @@ require "toml-rb"
|
|
5
5
|
require "dependabot/file_fetchers"
|
6
6
|
require "dependabot/file_fetchers/base"
|
7
7
|
require "dependabot/python/requirement_parser"
|
8
|
-
require "dependabot/python/file_parser/
|
8
|
+
require "dependabot/python/file_parser/pyproject_files_parser"
|
9
9
|
require "dependabot/errors"
|
10
10
|
|
11
11
|
module Dependabot
|
12
12
|
module Python
|
13
13
|
class FileFetcher < Dependabot::FileFetchers::Base
|
14
|
-
CHILD_REQUIREMENT_REGEX = /^-r\s?(?<path>.*\.(?:txt|in))
|
15
|
-
CONSTRAINT_REGEX = /^-c\s?(?<path>.*\.(?:txt|in))
|
16
|
-
DEPENDENCY_TYPES = %w(packages dev-packages)
|
14
|
+
CHILD_REQUIREMENT_REGEX = /^-r\s?(?<path>.*\.(?:txt|in))/
|
15
|
+
CONSTRAINT_REGEX = /^-c\s?(?<path>.*\.(?:txt|in))/
|
16
|
+
DEPENDENCY_TYPES = %w(packages dev-packages)
|
17
17
|
|
18
18
|
def self.required_files_in?(filenames)
|
19
19
|
return true if filenames.any? { |name| name.end_with?(".txt", ".in") }
|
@@ -24,7 +24,7 @@ module Dependabot
|
|
24
24
|
# If this repo is using a Pipfile return true
|
25
25
|
return true if filenames.include?("Pipfile")
|
26
26
|
|
27
|
-
# If this repo is using
|
27
|
+
# If this repo is using pyproject.toml return true
|
28
28
|
return true if filenames.include?("pyproject.toml")
|
29
29
|
|
30
30
|
return true if filenames.include?("setup.py")
|
@@ -69,7 +69,7 @@ module Dependabot
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def pyproject_files
|
72
|
-
[pyproject, pyproject_lock, poetry_lock].compact
|
72
|
+
[pyproject, pyproject_lock, poetry_lock, pdm_lock].compact
|
73
73
|
end
|
74
74
|
|
75
75
|
def requirement_files
|
@@ -81,7 +81,12 @@ module Dependabot
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def check_required_files_present
|
84
|
-
return if requirements_txt_files.any? ||
|
84
|
+
return if requirements_txt_files.any? ||
|
85
|
+
requirements_in_files.any? ||
|
86
|
+
setup_file ||
|
87
|
+
setup_cfg_file ||
|
88
|
+
pipfile ||
|
89
|
+
pyproject
|
85
90
|
|
86
91
|
path = Pathname.new(File.join(directory, "requirements.txt")).
|
87
92
|
cleanpath.to_path
|
@@ -136,6 +141,10 @@ module Dependabot
|
|
136
141
|
@poetry_lock ||= fetch_file_if_present("poetry.lock")
|
137
142
|
end
|
138
143
|
|
144
|
+
def pdm_lock
|
145
|
+
@pdm_lock ||= fetch_file_if_present("pdm.lock")
|
146
|
+
end
|
147
|
+
|
139
148
|
def requirements_txt_files
|
140
149
|
req_txt_and_in_files.select { |f| f.name.end_with?(".txt") }
|
141
150
|
end
|
@@ -169,7 +178,7 @@ module Dependabot
|
|
169
178
|
repo_contents.
|
170
179
|
select { |f| f.type == "file" }.
|
171
180
|
select { |f| f.name.end_with?(".txt", ".in") }.
|
172
|
-
reject { |f| f.size >
|
181
|
+
reject { |f| f.size > 500_000 }.
|
173
182
|
map { |f| fetch_file_from_host(f.name) }.
|
174
183
|
select { |f| requirements_file?(f) }.
|
175
184
|
each { |f| @req_txt_and_in_files << f }
|
@@ -189,7 +198,7 @@ module Dependabot
|
|
189
198
|
repo_contents(dir: relative_reqs_dir).
|
190
199
|
select { |f| f.type == "file" }.
|
191
200
|
select { |f| f.name.end_with?(".txt", ".in") }.
|
192
|
-
reject { |f| f.size >
|
201
|
+
reject { |f| f.size > 500_000 }.
|
193
202
|
map { |f| fetch_file_from_host("#{relative_reqs_dir}/#{f.name}") }.
|
194
203
|
select { |f| requirements_file?(f) }
|
195
204
|
end
|
@@ -291,8 +300,8 @@ module Dependabot
|
|
291
300
|
fetch_submodules: true
|
292
301
|
).tap { |f| f.support_file = true }
|
293
302
|
rescue Dependabot::DependencyFileNotFound
|
294
|
-
# For
|
295
|
-
# given path instead of a setup.py. We do not require a
|
303
|
+
# For projects with pyproject.toml attempt to fetch a pyproject.toml
|
304
|
+
# at the given path instead of a setup.py. We do not require a
|
296
305
|
# setup.py to be present, so if none can be found, simply return
|
297
306
|
return [] unless allow_pyproject
|
298
307
|
|
@@ -390,7 +399,7 @@ module Dependabot
|
|
390
399
|
return [] unless pyproject
|
391
400
|
|
392
401
|
paths = []
|
393
|
-
Dependabot::Python::FileParser::
|
402
|
+
Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |dep_type|
|
394
403
|
next unless parsed_pyproject.dig("tool", "poetry", dep_type)
|
395
404
|
|
396
405
|
parsed_pyproject.dig("tool", "poetry", dep_type).each do |_, req|
|
@@ -12,7 +12,7 @@ require "dependabot/python/name_normaliser"
|
|
12
12
|
module Dependabot
|
13
13
|
module Python
|
14
14
|
class FileParser
|
15
|
-
class
|
15
|
+
class PyprojectFilesParser
|
16
16
|
POETRY_DEPENDENCY_TYPES = %w(dependencies dev-dependencies).freeze
|
17
17
|
|
18
18
|
# https://python-poetry.org/docs/dependency-specification/
|
@@ -25,7 +25,7 @@ module Dependabot
|
|
25
25
|
def dependency_set
|
26
26
|
dependency_set = Dependabot::FileParsers::Base::DependencySet.new
|
27
27
|
|
28
|
-
dependency_set += pyproject_dependencies
|
28
|
+
dependency_set += pyproject_dependencies if using_poetry? || using_pep621?
|
29
29
|
dependency_set += lockfile_dependencies if lockfile
|
30
30
|
|
31
31
|
dependency_set
|
@@ -36,6 +36,14 @@ module Dependabot
|
|
36
36
|
attr_reader :dependency_files
|
37
37
|
|
38
38
|
def pyproject_dependencies
|
39
|
+
if using_poetry?
|
40
|
+
poetry_dependencies
|
41
|
+
else
|
42
|
+
pep621_dependencies
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def poetry_dependencies
|
39
47
|
dependencies = Dependabot::FileParsers::Base::DependencySet.new
|
40
48
|
|
41
49
|
POETRY_DEPENDENCY_TYPES.each do |type|
|
@@ -59,6 +67,44 @@ module Dependabot
|
|
59
67
|
dependencies
|
60
68
|
end
|
61
69
|
|
70
|
+
def pep621_dependencies
|
71
|
+
dependencies = Dependabot::FileParsers::Base::DependencySet.new
|
72
|
+
|
73
|
+
# PDM is not yet supported, so we want to ignore it for now because in
|
74
|
+
# the current state of things, going on would result in updating
|
75
|
+
# pyproject.toml but leaving pdm.lock out of sync, which is
|
76
|
+
# undesirable. Leave PDM alone until properly supported
|
77
|
+
return dependencies if using_pdm?
|
78
|
+
|
79
|
+
parsed_pep621_dependencies.each do |dep|
|
80
|
+
# If a requirement has a `<` or `<=` marker then updating it is
|
81
|
+
# probably blocked. Ignore it.
|
82
|
+
next if dep["markers"].include?("<")
|
83
|
+
|
84
|
+
# If no requirement, don't add it
|
85
|
+
next if dep["requirement"].empty?
|
86
|
+
|
87
|
+
dependencies <<
|
88
|
+
Dependency.new(
|
89
|
+
name: normalised_name(dep["name"], dep["extras"]),
|
90
|
+
version: dep["version"]&.include?("*") ? nil : dep["version"],
|
91
|
+
requirements: [{
|
92
|
+
requirement: dep["requirement"],
|
93
|
+
file: Pathname.new(dep["file"]).cleanpath.to_path,
|
94
|
+
source: nil,
|
95
|
+
groups: [dep["requirement_type"]]
|
96
|
+
}],
|
97
|
+
package_manager: "pip"
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
dependencies
|
102
|
+
end
|
103
|
+
|
104
|
+
def normalised_name(name, extras)
|
105
|
+
NameNormaliser.normalise_including_extras(name, extras)
|
106
|
+
end
|
107
|
+
|
62
108
|
# @param req can be an Array, Hash or String that represents the constraints for a dependency
|
63
109
|
def parse_requirements_from(req, type)
|
64
110
|
[req].flatten.compact.filter_map do |requirement|
|
@@ -75,6 +121,19 @@ module Dependabot
|
|
75
121
|
end
|
76
122
|
end
|
77
123
|
|
124
|
+
def using_poetry?
|
125
|
+
!parsed_pyproject.dig("tool", "poetry").nil?
|
126
|
+
end
|
127
|
+
|
128
|
+
def using_pep621?
|
129
|
+
!parsed_pyproject.dig("project", "dependencies").nil? ||
|
130
|
+
!parsed_pyproject.dig("project", "optional-dependencies").nil?
|
131
|
+
end
|
132
|
+
|
133
|
+
def using_pdm?
|
134
|
+
using_pep621? && pdm_lock
|
135
|
+
end
|
136
|
+
|
78
137
|
# Create a DependencySet where each element has no requirement. Any
|
79
138
|
# requirements will be added when combining the DependencySet with
|
80
139
|
# other DependencySets.
|
@@ -146,6 +205,24 @@ module Dependabot
|
|
146
205
|
poetry_lock || pyproject_lock
|
147
206
|
end
|
148
207
|
|
208
|
+
def parsed_pep621_dependencies
|
209
|
+
SharedHelpers.in_a_temporary_directory do
|
210
|
+
write_temporary_pyproject
|
211
|
+
|
212
|
+
SharedHelpers.run_helper_subprocess(
|
213
|
+
command: "pyenv exec python #{NativeHelpers.python_helper_path}",
|
214
|
+
function: "parse_pep621_dependencies",
|
215
|
+
args: [pyproject.name]
|
216
|
+
)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def write_temporary_pyproject
|
221
|
+
path = pyproject.name
|
222
|
+
FileUtils.mkdir_p(Pathname.new(path).dirname)
|
223
|
+
File.write(path, pyproject.content)
|
224
|
+
end
|
225
|
+
|
149
226
|
def parsed_lockfile
|
150
227
|
return parsed_poetry_lock if poetry_lock
|
151
228
|
return parsed_pyproject_lock if pyproject_lock
|
@@ -160,6 +237,11 @@ module Dependabot
|
|
160
237
|
@poetry_lock ||=
|
161
238
|
dependency_files.find { |f| f.name == "poetry.lock" }
|
162
239
|
end
|
240
|
+
|
241
|
+
def pdm_lock
|
242
|
+
@pdm_lock ||=
|
243
|
+
dependency_files.find { |f| f.name == "pdm.lock" }
|
244
|
+
end
|
163
245
|
end
|
164
246
|
end
|
165
247
|
end
|
@@ -12,10 +12,10 @@ module Dependabot
|
|
12
12
|
module Python
|
13
13
|
class FileParser
|
14
14
|
class SetupFileParser
|
15
|
-
INSTALL_REQUIRES_REGEX = /install_requires\s*=\s*\[/m
|
16
|
-
SETUP_REQUIRES_REGEX = /setup_requires\s*=\s*\[/m
|
17
|
-
TESTS_REQUIRE_REGEX = /tests_require\s*=\s*\[/m
|
18
|
-
EXTRAS_REQUIRE_REGEX = /extras_require\s*=\s*\{/m
|
15
|
+
INSTALL_REQUIRES_REGEX = /install_requires\s*=\s*\[/m
|
16
|
+
SETUP_REQUIRES_REGEX = /setup_requires\s*=\s*\[/m
|
17
|
+
TESTS_REQUIRE_REGEX = /tests_require\s*=\s*\[/m
|
18
|
+
EXTRAS_REQUIRE_REGEX = /extras_require\s*=\s*\{/m
|
19
19
|
|
20
20
|
CLOSING_BRACKET = { "[" => "]", "{" => "}" }.freeze
|
21
21
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "toml-rb"
|
4
3
|
require "dependabot/dependency"
|
5
4
|
require "dependabot/file_parsers"
|
6
5
|
require "dependabot/file_parsers/base"
|
@@ -15,11 +14,9 @@ module Dependabot
|
|
15
14
|
module Python
|
16
15
|
class FileParser < Dependabot::FileParsers::Base
|
17
16
|
require_relative "file_parser/pipfile_files_parser"
|
18
|
-
require_relative "file_parser/
|
17
|
+
require_relative "file_parser/pyproject_files_parser"
|
19
18
|
require_relative "file_parser/setup_file_parser"
|
20
19
|
|
21
|
-
POETRY_DEPENDENCY_TYPES =
|
22
|
-
%w(tool.poetry.dependencies tool.poetry.dev-dependencies).freeze
|
23
20
|
DEPENDENCY_GROUP_KEYS = [
|
24
21
|
{
|
25
22
|
pipfile: "packages",
|
@@ -42,7 +39,7 @@ module Dependabot
|
|
42
39
|
dependency_set = DependencySet.new
|
43
40
|
|
44
41
|
dependency_set += pipenv_dependencies if pipfile
|
45
|
-
dependency_set +=
|
42
|
+
dependency_set += pyproject_file_dependencies if pyproject
|
46
43
|
dependency_set += requirement_dependencies if requirement_files.any?
|
47
44
|
dependency_set += setup_file_dependencies if setup_file || setup_cfg_file
|
48
45
|
|
@@ -62,9 +59,9 @@ module Dependabot
|
|
62
59
|
dependency_set
|
63
60
|
end
|
64
61
|
|
65
|
-
def
|
66
|
-
@
|
67
|
-
|
62
|
+
def pyproject_file_dependencies
|
63
|
+
@pyproject_file_dependencies ||=
|
64
|
+
PyprojectFilesParser.
|
68
65
|
new(dependency_files: dependency_files).
|
69
66
|
dependency_set
|
70
67
|
end
|
@@ -105,18 +102,6 @@ module Dependabot
|
|
105
102
|
end
|
106
103
|
end
|
107
104
|
|
108
|
-
def included_in_pipenv_deps?(dep_name)
|
109
|
-
return false unless pipfile
|
110
|
-
|
111
|
-
pipenv_dependencies.dependencies.map(&:name).include?(dep_name)
|
112
|
-
end
|
113
|
-
|
114
|
-
def included_in_poetry_deps?(dep_name)
|
115
|
-
return false unless using_poetry?
|
116
|
-
|
117
|
-
poetry_dependencies.dependencies.map(&:name).include?(dep_name)
|
118
|
-
end
|
119
|
-
|
120
105
|
def blocking_marker?(dep)
|
121
106
|
return false if dep["markers"] == "None"
|
122
107
|
return true if dep["markers"].include?("<")
|
@@ -215,15 +200,6 @@ module Dependabot
|
|
215
200
|
@pipfile_lock ||= get_original_file("Pipfile.lock")
|
216
201
|
end
|
217
202
|
|
218
|
-
def using_poetry?
|
219
|
-
return false unless pyproject
|
220
|
-
return true if poetry_lock || pyproject_lock
|
221
|
-
|
222
|
-
!TomlRB.parse(pyproject.content).dig("tool", "poetry").nil?
|
223
|
-
rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
|
224
|
-
raise Dependabot::DependencyFileNotParseable, pyproject.path
|
225
|
-
end
|
226
|
-
|
227
203
|
def output_file_regex(filename)
|
228
204
|
"--output-file[=\s]+#{Regexp.escape(filename)}(?:\s|$)"
|
229
205
|
end
|
@@ -7,6 +7,7 @@ require "dependabot/python/file_fetcher"
|
|
7
7
|
require "dependabot/python/file_parser/python_requirement_parser"
|
8
8
|
require "dependabot/python/file_updater"
|
9
9
|
require "dependabot/shared_helpers"
|
10
|
+
require "dependabot/python/helpers"
|
10
11
|
require "dependabot/python/native_helpers"
|
11
12
|
require "dependabot/python/python_versions"
|
12
13
|
require "dependabot/python/name_normaliser"
|
@@ -22,10 +23,9 @@ module Dependabot
|
|
22
23
|
require_relative "setup_file_sanitizer"
|
23
24
|
|
24
25
|
UNSAFE_PACKAGES = %w(setuptools distribute pip).freeze
|
25
|
-
INCOMPATIBLE_VERSIONS_REGEX = /There are incompatible versions in the resolved dependencies:.*\z/m
|
26
|
-
WARNINGS = /\s*# WARNING:.*\Z/m
|
27
|
-
UNSAFE_NOTE =
|
28
|
-
/\s*# The following packages are considered to be unsafe.*\Z/m.freeze
|
26
|
+
INCOMPATIBLE_VERSIONS_REGEX = /There are incompatible versions in the resolved dependencies:.*\z/m
|
27
|
+
WARNINGS = /\s*# WARNING:.*\Z/m
|
28
|
+
UNSAFE_NOTE = /\s*# The following packages are considered to be unsafe.*\Z/m
|
29
29
|
|
30
30
|
attr_reader :dependencies, :dependency_files, :credentials
|
31
31
|
|
@@ -66,7 +66,7 @@ module Dependabot
|
|
66
66
|
def compile_new_requirement_files
|
67
67
|
SharedHelpers.in_a_temporary_directory do
|
68
68
|
write_updated_dependency_files
|
69
|
-
install_required_python
|
69
|
+
Helpers.install_required_python(python_version)
|
70
70
|
|
71
71
|
filenames_to_compile.each do |filename|
|
72
72
|
# Shell out to pip-compile, generate a new set of requirements.
|
@@ -81,12 +81,6 @@ module Dependabot
|
|
81
81
|
"#{SharedHelpers.escape_command(version_part)}",
|
82
82
|
allow_unsafe_shell_command: true
|
83
83
|
)
|
84
|
-
# Run pip-compile a second time, without an update argument, to
|
85
|
-
# ensure it resets the right comments.
|
86
|
-
run_pip_compile_command(
|
87
|
-
"pyenv exec pip-compile #{pip_compile_options(filename)} " \
|
88
|
-
"#{filename}"
|
89
|
-
)
|
90
84
|
end
|
91
85
|
|
92
86
|
# Remove any .python-version file before parsing the reqs
|
@@ -174,7 +168,7 @@ module Dependabot
|
|
174
168
|
end
|
175
169
|
|
176
170
|
def run_pip_compile_command(command, allow_unsafe_shell_command: false)
|
177
|
-
run_command("pyenv local #{python_version}")
|
171
|
+
run_command("pyenv local #{Helpers.python_major_minor(python_version)}")
|
178
172
|
run_command(
|
179
173
|
command,
|
180
174
|
allow_unsafe_shell_command: allow_unsafe_shell_command
|
@@ -204,7 +198,7 @@ module Dependabot
|
|
204
198
|
end
|
205
199
|
|
206
200
|
# Overwrite the .python-version with updated content
|
207
|
-
File.write(".python-version", python_version)
|
201
|
+
File.write(".python-version", Helpers.python_major_minor(python_version))
|
208
202
|
|
209
203
|
setup_files.each do |file|
|
210
204
|
path = file.name
|
@@ -219,15 +213,6 @@ module Dependabot
|
|
219
213
|
end
|
220
214
|
end
|
221
215
|
|
222
|
-
def install_required_python
|
223
|
-
return if run_command("pyenv versions").include?("#{python_version}\n")
|
224
|
-
|
225
|
-
run_command("pyenv install -s #{python_version}")
|
226
|
-
run_command("pyenv exec pip install --upgrade pip")
|
227
|
-
run_command("pyenv exec pip install -r " \
|
228
|
-
"#{NativeHelpers.python_requirements_path}")
|
229
|
-
end
|
230
|
-
|
231
216
|
def sanitized_setup_file_content(file)
|
232
217
|
@sanitized_setup_file_content ||= {}
|
233
218
|
return @sanitized_setup_file_content[file.name] if @sanitized_setup_file_content[file.name]
|
@@ -133,6 +133,7 @@ module Dependabot
|
|
133
133
|
content = freeze_other_dependencies(content)
|
134
134
|
content = freeze_dependencies_being_updated(content)
|
135
135
|
content = add_private_sources(content)
|
136
|
+
content = update_python_requirement(content)
|
136
137
|
content
|
137
138
|
end
|
138
139
|
|
@@ -142,6 +143,12 @@ module Dependabot
|
|
142
143
|
freeze_top_level_dependencies_except(dependencies)
|
143
144
|
end
|
144
145
|
|
146
|
+
def update_python_requirement(pipfile_content)
|
147
|
+
PipfilePreparer.
|
148
|
+
new(pipfile_content: pipfile_content).
|
149
|
+
update_python_requirement(Helpers.python_major_minor(python_version))
|
150
|
+
end
|
151
|
+
|
145
152
|
# rubocop:disable Metrics/PerceivedComplexity
|
146
153
|
def freeze_dependencies_being_updated(pipfile_content)
|
147
154
|
pipfile_object = TomlRB.parse(pipfile_content)
|
@@ -246,7 +253,7 @@ module Dependabot
|
|
246
253
|
def run_command(command, env: {})
|
247
254
|
start = Time.now
|
248
255
|
command = SharedHelpers.escape_command(command)
|
249
|
-
stdout, process = Open3.
|
256
|
+
stdout, _, process = Open3.capture3(env, command)
|
250
257
|
time_taken = Time.now - start
|
251
258
|
|
252
259
|
# Raise an error with the output from the shell session if Pipenv
|
@@ -264,7 +271,7 @@ module Dependabot
|
|
264
271
|
end
|
265
272
|
|
266
273
|
def run_pipenv_command(command, env: pipenv_env_variables)
|
267
|
-
run_command("pyenv local #{python_version}")
|
274
|
+
run_command("pyenv local #{Helpers.python_major_minor(python_version)}")
|
268
275
|
run_command(command, env: env)
|
269
276
|
end
|
270
277
|
|
@@ -276,7 +283,7 @@ module Dependabot
|
|
276
283
|
end
|
277
284
|
|
278
285
|
# Overwrite the .python-version with updated content
|
279
|
-
File.write(".python-version", python_version)
|
286
|
+
File.write(".python-version", Helpers.python_major_minor(python_version))
|
280
287
|
|
281
288
|
setup_files.each do |file|
|
282
289
|
path = file.name
|
@@ -302,11 +309,7 @@ module Dependabot
|
|
302
309
|
nil
|
303
310
|
end
|
304
311
|
|
305
|
-
|
306
|
-
|
307
|
-
requirements_path = NativeHelpers.python_requirements_path
|
308
|
-
run_command("pyenv install -s #{python_version}")
|
309
|
-
run_command("pyenv exec pip install -r #{requirements_path}")
|
312
|
+
Helpers.install_required_python(python_version)
|
310
313
|
end
|
311
314
|
|
312
315
|
def sanitized_setup_file_content(file)
|
@@ -70,10 +70,12 @@ module Dependabot
|
|
70
70
|
pipfile_object = TomlRB.parse(pipfile_content)
|
71
71
|
|
72
72
|
pipfile_object["requires"] ||= {}
|
73
|
-
pipfile_object
|
74
|
-
|
75
|
-
pipfile_object
|
76
|
-
|
73
|
+
if pipfile_object.dig("requires", "python_full_version") && pipfile_object.dig("requires", "python_version")
|
74
|
+
pipfile_object["requires"].delete("python_full_version")
|
75
|
+
elsif pipfile_object.dig("requires", "python_full_version")
|
76
|
+
pipfile_object["requires"].delete("python_full_version")
|
77
|
+
pipfile_object["requires"]["python_version"] = requirement
|
78
|
+
end
|
77
79
|
TomlRB.dump(pipfile_object)
|
78
80
|
end
|
79
81
|
|
@@ -4,6 +4,7 @@ require "toml-rb"
|
|
4
4
|
require "open3"
|
5
5
|
require "dependabot/dependency"
|
6
6
|
require "dependabot/shared_helpers"
|
7
|
+
require "dependabot/python/helpers"
|
7
8
|
require "dependabot/python/version"
|
8
9
|
require "dependabot/python/requirement"
|
9
10
|
require "dependabot/python/python_versions"
|
@@ -105,6 +106,7 @@ module Dependabot
|
|
105
106
|
content = sanitize(content)
|
106
107
|
content = freeze_other_dependencies(content)
|
107
108
|
content = freeze_dependencies_being_updated(content)
|
109
|
+
content = update_python_requirement(content)
|
108
110
|
content
|
109
111
|
end
|
110
112
|
end
|
@@ -130,8 +132,14 @@ module Dependabot
|
|
130
132
|
TomlRB.dump(pyproject_object)
|
131
133
|
end
|
132
134
|
|
135
|
+
def update_python_requirement(pyproject_content)
|
136
|
+
PyprojectPreparer.
|
137
|
+
new(pyproject_content: pyproject_content).
|
138
|
+
update_python_requirement(Helpers.python_major_minor(python_version))
|
139
|
+
end
|
140
|
+
|
133
141
|
def lock_declaration_to_new_version!(poetry_object, dep)
|
134
|
-
Dependabot::Python::FileParser::
|
142
|
+
Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |type|
|
135
143
|
names = poetry_object[type]&.keys || []
|
136
144
|
pkg_name = names.find { |nm| normalise(nm) == dep.name }
|
137
145
|
next unless pkg_name
|
@@ -170,11 +178,11 @@ module Dependabot
|
|
170
178
|
write_temporary_dependency_files(pyproject_content)
|
171
179
|
add_auth_env_vars
|
172
180
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
181
|
+
Helpers.install_required_python(python_version)
|
182
|
+
|
183
|
+
# use system git instead of the pure Python dulwich
|
184
|
+
unless python_version&.start_with?("3.6")
|
185
|
+
run_poetry_command("pyenv exec poetry config experimental.system-git-client true")
|
178
186
|
end
|
179
187
|
|
180
188
|
run_poetry_command(poetry_update_command)
|
@@ -220,7 +228,7 @@ module Dependabot
|
|
220
228
|
end
|
221
229
|
|
222
230
|
# Overwrite the .python-version with updated content
|
223
|
-
File.write(".python-version", python_version) if python_version
|
231
|
+
File.write(".python-version", Helpers.python_major_minor(python_version)) if python_version
|
224
232
|
|
225
233
|
# Overwrite the pyproject with updated content
|
226
234
|
File.write("pyproject.toml", pyproject_content)
|