dependabot-python 0.212.0 → 0.213.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.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/build +1 -6
  3. data/helpers/lib/parser.py +52 -0
  4. data/helpers/requirements.txt +3 -3
  5. data/helpers/run.py +2 -0
  6. data/lib/dependabot/python/file_fetcher.rb +21 -12
  7. data/lib/dependabot/python/file_parser/{poetry_files_parser.rb → pyproject_files_parser.rb} +83 -2
  8. data/lib/dependabot/python/file_parser/setup_file_parser.rb +4 -4
  9. data/lib/dependabot/python/file_parser.rb +5 -29
  10. data/lib/dependabot/python/file_updater/pip_compile_file_updater.rb +5 -20
  11. data/lib/dependabot/python/file_updater/pipfile_file_updater.rb +1 -5
  12. data/lib/dependabot/python/file_updater/poetry_file_updater.rb +7 -6
  13. data/lib/dependabot/python/file_updater/pyproject_preparer.rb +1 -1
  14. data/lib/dependabot/python/file_updater.rb +14 -1
  15. data/lib/dependabot/python/helpers.rb +20 -0
  16. data/lib/dependabot/python/metadata_finder.rb +2 -0
  17. data/lib/dependabot/python/python_versions.rb +5 -5
  18. data/lib/dependabot/python/requirement.rb +7 -4
  19. data/lib/dependabot/python/requirement_parser.rb +20 -23
  20. data/lib/dependabot/python/update_checker/index_finder.rb +1 -1
  21. data/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb +17 -19
  22. data/lib/dependabot/python/update_checker/pipenv_version_resolver.rb +7 -16
  23. data/lib/dependabot/python/update_checker/poetry_version_resolver.rb +13 -11
  24. data/lib/dependabot/python/update_checker/requirements_updater.rb +17 -4
  25. data/lib/dependabot/python/update_checker.rb +82 -25
  26. data/lib/dependabot/python/version.rb +2 -2
  27. metadata +15 -56
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4862b59513a97a33d51ff8ad57b6d9815842769c87f612426ffbcd8e75a9655c
4
- data.tar.gz: 9543935b3ab8027344757b41aa8ff265d823beac956a06d20d4917e75962698d
3
+ metadata.gz: 6cb23890c79504e40e7e4962485003c76c07179574ac89b210b6529d15d2c216
4
+ data.tar.gz: b96523f9cf991cbffc38fc2831221c43450c74ef560e8e80ff2d2bbf73c889c4
5
5
  SHA512:
6
- metadata.gz: fd9e547d19f01addfdc2fcc4eeb7095bf8e886eded086772f81b9bfbd6acfb2fc6076c354c970ae5fad0691a25679d99db8eb0884121e986c1bcf39b17c041ba
7
- data.tar.gz: be587fe031f132dd20d4fc023668e31d81c37608d915a9f586ca159b675302993a208b6e93209cfaedad83d08eaae1ca06f6af7eec8933bcc2c44e8a61293ccf
6
+ metadata.gz: 5beeac4ec63193ce095e6a5d7223c11e4e9c2ace55b3ef5c94f0011d8cb0c70fc7b364108b68cd68f656a5ed79d712b64eecbb75998714325a0f3b101169592c
7
+ data.tar.gz: 17ec5483c750fe4bc35490feebf418a8bd7eaf0eb2d14e2bcfc811e06f94c02f69eca71bafb589cb2d51b8a73155d3d358b97fcc6125a8fb7229b54f06a42fbe
data/helpers/build CHANGED
@@ -18,9 +18,4 @@ cp -r \
18
18
  "$install_dir"
19
19
 
20
20
  cd "$install_dir"
21
- PYENV_VERSION=3.10.5 pyenv exec pip --disable-pip-version-check install -r "requirements.txt"
22
-
23
- # Workaround of https://github.com/python-poetry/poetry/issues/3010
24
- # By default poetry config file is stored under ~/.config/pypoetry
25
- # and is not bound to any specific Python version
26
- PYENV_VERSION=3.10.5 pyenv exec poetry config experimental.new-installer false
21
+ PYENV_VERSION=3.10.7 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
@@ -11,11 +11,63 @@ 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 = parse_toml_section_pep621_dependencies(
53
+ pyproject_path,
54
+ project_toml['dependencies']
55
+ )
56
+
57
+ if 'optional-dependencies' in project_toml:
58
+ optional_dependencies_toml = project_toml['optional-dependencies']
59
+
60
+ for group in optional_dependencies_toml:
61
+ group_dependencies = parse_toml_section_pep621_dependencies(
62
+ pyproject_path,
63
+ optional_dependencies_toml[group]
64
+ )
65
+
66
+ dependencies.extend(group_dependencies)
67
+
68
+ return json.dumps({"result": dependencies})
69
+
70
+
19
71
  def parse_requirements(directory):
20
72
  # Parse the requirements.txt
21
73
  requirement_packages = []
@@ -1,10 +1,10 @@
1
- pip>=21.3.1,<22.2.3 # Range maintains py36 support TODO: Review python 3.6 support in April 2023 (eol ubuntu 18.04)
2
- pip-tools>=6.4.0,<6.8.1 # Range maintains py36 support TODO: Review python 3.6 support in April 2023 (eol ubuntu 18.04)
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.9.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,<=1.2.0
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/poetry_files_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))/.freeze
15
- CONSTRAINT_REGEX = /^-c\s?(?<path>.*\.(?:txt|in))/.freeze
16
- DEPENDENCY_TYPES = %w(packages dev-packages).freeze
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 Poetry return true
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? || setup_file || setup_cfg_file || pipfile || pyproject
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 > 200_000 }.
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 > 200_000 }.
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 Poetry projects attempt to fetch a pyproject.toml at the
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::PoetryFilesParser::POETRY_DEPENDENCY_TYPES.each do |dep_type|
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 PoetryFilesParser
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,18 @@ 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
+ end
131
+
132
+ def using_pdm?
133
+ using_pep621? && pdm_lock
134
+ end
135
+
78
136
  # Create a DependencySet where each element has no requirement. Any
79
137
  # requirements will be added when combining the DependencySet with
80
138
  # other DependencySets.
@@ -146,6 +204,24 @@ module Dependabot
146
204
  poetry_lock || pyproject_lock
147
205
  end
148
206
 
207
+ def parsed_pep621_dependencies
208
+ SharedHelpers.in_a_temporary_directory do
209
+ write_temporary_pyproject
210
+
211
+ SharedHelpers.run_helper_subprocess(
212
+ command: "pyenv exec python #{NativeHelpers.python_helper_path}",
213
+ function: "parse_pep621_dependencies",
214
+ args: [pyproject.name]
215
+ )
216
+ end
217
+ end
218
+
219
+ def write_temporary_pyproject
220
+ path = pyproject.name
221
+ FileUtils.mkdir_p(Pathname.new(path).dirname)
222
+ File.write(path, pyproject.content)
223
+ end
224
+
149
225
  def parsed_lockfile
150
226
  return parsed_poetry_lock if poetry_lock
151
227
  return parsed_pyproject_lock if pyproject_lock
@@ -160,6 +236,11 @@ module Dependabot
160
236
  @poetry_lock ||=
161
237
  dependency_files.find { |f| f.name == "poetry.lock" }
162
238
  end
239
+
240
+ def pdm_lock
241
+ @pdm_lock ||=
242
+ dependency_files.find { |f| f.name == "pdm.lock" }
243
+ end
163
244
  end
164
245
  end
165
246
  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.freeze
16
- SETUP_REQUIRES_REGEX = /setup_requires\s*=\s*\[/m.freeze
17
- TESTS_REQUIRE_REGEX = /tests_require\s*=\s*\[/m.freeze
18
- EXTRAS_REQUIRE_REGEX = /extras_require\s*=\s*\{/m.freeze
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/poetry_files_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 += poetry_dependencies if using_poetry?
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 poetry_dependencies
66
- @poetry_dependencies ||=
67
- PoetryFilesParser.
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.freeze
26
- WARNINGS = /\s*# WARNING:.*\Z/m.freeze
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
@@ -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]
@@ -302,11 +302,7 @@ module Dependabot
302
302
  nil
303
303
  end
304
304
 
305
- return if run_command("pyenv versions").include?("#{python_version}\n")
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}")
305
+ Helpers.install_required_python(python_version)
310
306
  end
311
307
 
312
308
  def sanitized_setup_file_content(file)
@@ -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"
@@ -131,7 +132,7 @@ module Dependabot
131
132
  end
132
133
 
133
134
  def lock_declaration_to_new_version!(poetry_object, dep)
134
- Dependabot::Python::FileParser::PoetryFilesParser::POETRY_DEPENDENCY_TYPES.each do |type|
135
+ Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |type|
135
136
  names = poetry_object[type]&.keys || []
136
137
  pkg_name = names.find { |nm| normalise(nm) == dep.name }
137
138
  next unless pkg_name
@@ -170,11 +171,11 @@ module Dependabot
170
171
  write_temporary_dependency_files(pyproject_content)
171
172
  add_auth_env_vars
172
173
 
173
- if python_version && !pre_installed_python?(python_version)
174
- run_poetry_command("pyenv install -s #{python_version}")
175
- run_poetry_command("pyenv exec pip install --upgrade pip")
176
- run_poetry_command("pyenv exec pip install -r" \
177
- "#{NativeHelpers.python_requirements_path}")
174
+ Helpers.install_required_python(python_version)
175
+
176
+ # use system git instead of the pure Python dulwich
177
+ unless python_version&.start_with?("3.6")
178
+ run_poetry_command("pyenv exec poetry config experimental.system-git-client true")
178
179
  end
179
180
 
180
181
  run_poetry_command(poetry_update_command)
@@ -52,7 +52,7 @@ module Dependabot
52
52
  poetry_object = pyproject_object["tool"]["poetry"]
53
53
  excluded_names = dependencies.map(&:name) + ["python"]
54
54
 
55
- Dependabot::Python::FileParser::PoetryFilesParser::POETRY_DEPENDENCY_TYPES.each do |key|
55
+ Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |key|
56
56
  next unless poetry_object[key]
57
57
 
58
58
  source_types = %w(directory file url)
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "toml-rb"
3
4
  require "dependabot/file_updaters"
4
5
  require "dependabot/file_updaters/base"
5
6
  require "dependabot/shared_helpers"
@@ -61,7 +62,13 @@ module Dependabot
61
62
  # Otherwise, this is a top-level dependency, and we can figure out
62
63
  # which resolver to use based on the filename of its requirements
63
64
  return :pipfile if changed_req_files.any?("Pipfile")
64
- return :poetry if changed_req_files.any?("pyproject.toml")
65
+
66
+ if changed_req_files.any?("pyproject.toml")
67
+ return :poetry if poetry_based?
68
+
69
+ return :requirements
70
+ end
71
+
65
72
  return :pip_compile if changed_req_files.any? { |f| f.end_with?(".in") }
66
73
 
67
74
  :requirements
@@ -119,6 +126,12 @@ module Dependabot
119
126
  raise "Missing required files!"
120
127
  end
121
128
 
129
+ def poetry_based?
130
+ return false unless pyproject
131
+
132
+ !TomlRB.parse(pyproject.content).dig("tool", "poetry").nil?
133
+ end
134
+
122
135
  def pipfile
123
136
  @pipfile ||= get_original_file("Pipfile")
124
137
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/logger"
4
+
5
+ module Dependabot
6
+ module Python
7
+ module Helpers
8
+ def self.install_required_python(python_version)
9
+ # The leading space is important in the version check
10
+ return if SharedHelpers.run_shell_command("pyenv versions").include?(" #{python_version}")
11
+
12
+ Dependabot.logger.info("Installing required Python #{python_version}.")
13
+ SharedHelpers.run_shell_command("pyenv install -s #{python_version}")
14
+ SharedHelpers.run_shell_command("pyenv exec pip install --upgrade pip")
15
+ SharedHelpers.run_shell_command("pyenv exec pip install -r" \
16
+ "#{NativeHelpers.python_requirements_path}")
17
+ end
18
+ end
19
+ end
20
+ end
@@ -131,6 +131,8 @@ module Dependabot
131
131
  return @pypi_listing
132
132
  rescue JSON::ParserError
133
133
  next
134
+ rescue Excon::Error::Timeout
135
+ next
134
136
  end
135
137
 
136
138
  @pypi_listing = {} # No listing found
@@ -4,16 +4,16 @@ module Dependabot
4
4
  module Python
5
5
  module PythonVersions
6
6
  PRE_INSTALLED_PYTHON_VERSIONS = %w(
7
- 3.10.5
7
+ 3.10.7
8
8
  ).freeze
9
9
 
10
10
  # Due to an OpenSSL issue we can only install the following versions in
11
11
  # the Dependabot container.
12
12
  SUPPORTED_VERSIONS = %w(
13
- 3.10.5 3.10.4 3.10.3 3.10.2 3.10.1 3.10.0
14
- 3.9.13 3.9.12 3.9.11 3.9.10 3.9.9 3.9.8 3.9.7 3.9.6 3.9.5 3.9.4 3.9.2 3.9.1 3.9.0
15
- 3.8.13 3.8.12 3.8.11 3.8.10 3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 3.8.2 3.8.1 3.8.0
16
- 3.7.13 3.7.12 3.7.11 3.7.10 3.7.9 3.7.8 3.7.7 3.7.6 3.7.5 3.7.4 3.7.3 3.7.2 3.7.1 3.7.0
13
+ 3.10.7 3.10.6 3.10.5 3.10.4 3.10.3 3.10.2 3.10.1 3.10.0
14
+ 3.9.14 3.9.13 3.9.12 3.9.11 3.9.10 3.9.9 3.9.8 3.9.7 3.9.6 3.9.5 3.9.4 3.9.2 3.9.1 3.9.0
15
+ 3.8.14 3.8.13 3.8.12 3.8.11 3.8.10 3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 3.8.2 3.8.1 3.8.0
16
+ 3.7.14 3.7.13 3.7.12 3.7.11 3.7.10 3.7.9 3.7.8 3.7.7 3.7.6 3.7.5 3.7.4 3.7.3 3.7.2 3.7.1 3.7.0
17
17
  3.6.15 3.6.14 3.6.13 3.6.12 3.6.11 3.6.10 3.6.9 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3
18
18
  3.6.2 3.6.1 3.6.0 3.5.10 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3
19
19
  ).freeze