dependabot-core 0.78.0 → 0.79.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/helpers/npm/lib/updater.js +11 -5
  4. data/helpers/npm/package.json +2 -2
  5. data/helpers/npm/yarn.lock +26 -28
  6. data/helpers/yarn/lib/replace-lockfile-declaration.js +15 -3
  7. data/helpers/yarn/lib/updater.js +17 -5
  8. data/helpers/yarn/package.json +2 -2
  9. data/helpers/yarn/yarn.lock +24 -31
  10. data/lib/dependabot/file_fetchers.rb +0 -2
  11. data/lib/dependabot/file_parsers.rb +0 -2
  12. data/lib/dependabot/file_updaters.rb +0 -2
  13. data/lib/dependabot/metadata_finders.rb +0 -2
  14. data/lib/dependabot/update_checkers.rb +0 -2
  15. data/lib/dependabot/utils.rb +0 -4
  16. data/lib/dependabot/version.rb +1 -1
  17. metadata +3 -34
  18. data/helpers/python/lib/__init__.py +0 -0
  19. data/helpers/python/lib/hasher.py +0 -23
  20. data/helpers/python/lib/parser.py +0 -130
  21. data/helpers/python/requirements.txt +0 -9
  22. data/helpers/python/run.py +0 -18
  23. data/lib/dependabot/file_fetchers/python/pip.rb +0 -305
  24. data/lib/dependabot/file_parsers/python/pip.rb +0 -223
  25. data/lib/dependabot/file_parsers/python/pip/pipfile_files_parser.rb +0 -154
  26. data/lib/dependabot/file_parsers/python/pip/poetry_files_parser.rb +0 -141
  27. data/lib/dependabot/file_parsers/python/pip/setup_file_parser.rb +0 -164
  28. data/lib/dependabot/file_updaters/python/pip.rb +0 -147
  29. data/lib/dependabot/file_updaters/python/pip/pip_compile_file_updater.rb +0 -363
  30. data/lib/dependabot/file_updaters/python/pip/pipfile_file_updater.rb +0 -397
  31. data/lib/dependabot/file_updaters/python/pip/pipfile_preparer.rb +0 -125
  32. data/lib/dependabot/file_updaters/python/pip/poetry_file_updater.rb +0 -289
  33. data/lib/dependabot/file_updaters/python/pip/pyproject_preparer.rb +0 -105
  34. data/lib/dependabot/file_updaters/python/pip/requirement_file_updater.rb +0 -166
  35. data/lib/dependabot/file_updaters/python/pip/requirement_replacer.rb +0 -95
  36. data/lib/dependabot/file_updaters/python/pip/setup_file_sanitizer.rb +0 -91
  37. data/lib/dependabot/file_updaters/ruby/.DS_Store +0 -0
  38. data/lib/dependabot/metadata_finders/python/pip.rb +0 -120
  39. data/lib/dependabot/update_checkers/python/pip.rb +0 -227
  40. data/lib/dependabot/update_checkers/python/pip/latest_version_finder.rb +0 -252
  41. data/lib/dependabot/update_checkers/python/pip/pip_compile_version_resolver.rb +0 -380
  42. data/lib/dependabot/update_checkers/python/pip/pipfile_version_resolver.rb +0 -559
  43. data/lib/dependabot/update_checkers/python/pip/poetry_version_resolver.rb +0 -300
  44. data/lib/dependabot/update_checkers/python/pip/requirements_updater.rb +0 -367
  45. data/lib/dependabot/utils/python/requirement.rb +0 -130
  46. data/lib/dependabot/utils/python/version.rb +0 -88
  47. data/lib/python_requirement_parser.rb +0 -33
  48. data/lib/python_versions.rb +0 -21
@@ -1,164 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dependabot/dependency"
4
- require "dependabot/errors"
5
- require "dependabot/file_parsers/base/dependency_set"
6
- require "dependabot/file_parsers/python/pip"
7
- require "dependabot/shared_helpers"
8
-
9
- module Dependabot
10
- module FileParsers
11
- module Python
12
- class Pip
13
- class SetupFileParser
14
- INSTALL_REQUIRES_REGEX =
15
- /install_requires\s*=\s*(\[.*?\])[,)\s]/m.freeze
16
- SETUP_REQUIRES_REGEX = /setup_requires\s*=\s*(\[.*?\])[,)\s]/m.freeze
17
- TESTS_REQUIRE_REGEX = /tests_require\s*=\s*(\[.*?\])[,)\s]/m.freeze
18
- EXTRAS_REQUIRE_REGEX = /extras_require\s*=\s*(\{.*?\})[,)\s]/m.freeze
19
-
20
- def initialize(dependency_files:)
21
- @dependency_files = dependency_files
22
- end
23
-
24
- def dependency_set
25
- dependencies = Dependabot::FileParsers::Base::DependencySet.new
26
-
27
- parsed_setup_file.each do |dep|
28
- # If a requirement has a `<` or `<=` marker then updating it is
29
- # probably blocked. Ignore it.
30
- next if dep["markers"].include?("<")
31
-
32
- # If the requirement is our inserted version, ignore it
33
- # (we wouldn't be able to update it)
34
- next if dep["version"] == "0.0.1+dependabot"
35
-
36
- dependencies <<
37
- Dependency.new(
38
- name: normalised_name(dep["name"]),
39
- version: dep["version"]&.include?("*") ? nil : dep["version"],
40
- requirements: [{
41
- requirement: dep["requirement"],
42
- file: Pathname.new(dep["file"]).cleanpath.to_path,
43
- source: nil,
44
- groups: [dep["requirement_type"]]
45
- }],
46
- package_manager: "pip"
47
- )
48
- end
49
- dependencies
50
- end
51
-
52
- private
53
-
54
- attr_reader :dependency_files
55
-
56
- def parsed_setup_file
57
- SharedHelpers.in_a_temporary_directory do
58
- write_temporary_dependency_files
59
-
60
- requirements = SharedHelpers.run_helper_subprocess(
61
- command: "pyenv exec python #{python_helper_path}",
62
- function: "parse_setup",
63
- args: [Dir.pwd]
64
- )
65
-
66
- check_requirements(requirements)
67
- requirements
68
- end
69
- rescue SharedHelpers::HelperSubprocessFailed => error
70
- if error.message.start_with?("InstallationError")
71
- raise Dependabot::DependencyFileNotEvaluatable, error.message
72
- end
73
-
74
- parsed_sanitized_setup_file
75
- end
76
-
77
- def parsed_sanitized_setup_file
78
- SharedHelpers.in_a_temporary_directory do
79
- write_sanitized_setup_file
80
-
81
- requirements = SharedHelpers.run_helper_subprocess(
82
- command: "pyenv exec python #{python_helper_path}",
83
- function: "parse_setup",
84
- args: [Dir.pwd]
85
- )
86
-
87
- check_requirements(requirements)
88
- requirements
89
- end
90
- rescue SharedHelpers::HelperSubprocessFailed
91
- # Assume there are no dependencies in setup.py files that fail to
92
- # parse. This isn't ideal, and we should continue to improve
93
- # parsing, but there are a *lot* of things that can go wrong at
94
- # the moment!
95
- []
96
- end
97
-
98
- def check_requirements(requirements)
99
- requirements.each do |dep|
100
- next unless dep["requirement"]
101
-
102
- Utils::Python::Requirement.new(dep["requirement"].split(","))
103
- rescue Gem::Requirement::BadRequirementError => error
104
- raise Dependabot::DependencyFileNotEvaluatable, error.message
105
- end
106
- end
107
-
108
- def write_temporary_dependency_files
109
- dependency_files.
110
- reject { |f| f.name == ".python-version" }.
111
- each do |file|
112
- path = file.name
113
- FileUtils.mkdir_p(Pathname.new(path).dirname)
114
- File.write(path, file.content)
115
- end
116
- end
117
-
118
- # Write a setup.py with only entries for the requires fields.
119
- #
120
- # This sanitization is far from perfect (it will fail if any of the
121
- # entries are dynamic), but it is an alternative approach to the one
122
- # used in parser.py which sometimes succeeds when that has failed.
123
- def write_sanitized_setup_file
124
- original_content = setup_file.content
125
-
126
- install_requires =
127
- original_content.match(INSTALL_REQUIRES_REGEX)&.captures&.first
128
- setup_requires =
129
- original_content.match(SETUP_REQUIRES_REGEX)&.captures&.first
130
- tests_require =
131
- original_content.match(TESTS_REQUIRE_REGEX)&.captures&.first
132
- extras_require =
133
- original_content.match(EXTRAS_REQUIRE_REGEX)&.captures&.first
134
-
135
- tmp = "from setuptools import setup\n\n"\
136
- "setup(name=\"sanitized-package\",version=\"0.0.1\","
137
-
138
- tmp += "install_requires=#{install_requires}," if install_requires
139
- tmp += "setup_requires=#{setup_requires}," if setup_requires
140
- tmp += "tests_require=#{tests_require}," if tests_require
141
- tmp += "extras_require=#{extras_require}," if extras_require
142
- tmp += ")"
143
-
144
- File.write("setup.py", tmp)
145
- end
146
-
147
- def python_helper_path
148
- project_root = File.join(File.dirname(__FILE__), "../../../../..")
149
- File.join(project_root, "helpers/python/run.py")
150
- end
151
-
152
- # See https://www.python.org/dev/peps/pep-0503/#normalized-names
153
- def normalised_name(name)
154
- name.downcase.gsub(/[-_.]+/, "-")
155
- end
156
-
157
- def setup_file
158
- dependency_files.find { |f| f.name == "setup.py" }
159
- end
160
- end
161
- end
162
- end
163
- end
164
- end
@@ -1,147 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dependabot/file_updaters/base"
4
- require "dependabot/shared_helpers"
5
-
6
- module Dependabot
7
- module FileUpdaters
8
- module Python
9
- class Pip < Dependabot::FileUpdaters::Base
10
- require_relative "pip/pipfile_file_updater"
11
- require_relative "pip/pip_compile_file_updater"
12
- require_relative "pip/poetry_file_updater"
13
- require_relative "pip/requirement_file_updater"
14
-
15
- def self.updated_files_regex
16
- [
17
- /^Pipfile$/,
18
- /^Pipfile\.lock$/,
19
- /.*\.txt$/,
20
- /.*\.in$/,
21
- /^setup\.py$/,
22
- /^pyproject\.toml$/,
23
- /^pyproject\.lock$/
24
- ]
25
- end
26
-
27
- def updated_dependency_files
28
- updated_files =
29
- case resolver_type
30
- when :pipfile then updated_pipfile_based_files
31
- when :poetry then updated_poetry_based_files
32
- when :pip_compile then updated_pip_compile_based_files
33
- when :requirements then updated_requirement_based_files
34
- else raise "Unexpected resolver type: #{resolver_type}"
35
- end
36
-
37
- if updated_files.none? ||
38
- updated_files.sort_by(&:name) == dependency_files.sort_by(&:name)
39
- raise "No files have changed!"
40
- end
41
-
42
- updated_files
43
- end
44
-
45
- private
46
-
47
- def resolver_type
48
- reqs = dependencies.flat_map(&:requirements)
49
- req_files = reqs.map { |r| r.fetch(:file) }
50
-
51
- # If there are no requirements then this is a sub-dependency. It
52
- # must come from one of Pipenv, Poetry or pip-tools, and can't come
53
- # from the first two unless they have a lockfile.
54
- return subdependency_resolver if reqs.none?
55
-
56
- # Otherwise, this is a top-level dependency, and we can figure out
57
- # which resolver to use based on the filename of its requirements
58
- return :pipfile if req_files.any? { |f| f == "Pipfile" }
59
- return :poetry if req_files.any? { |f| f == "pyproject.toml" }
60
- return :pip_compile if req_files.any? { |f| f.end_with?(".in") }
61
-
62
- # Finally, we should only ever be updating a requirements.txt file if
63
- # some requirements have changed. Otherwise, this must be a case where
64
- # we have a requirements.txt *and* some other resolver of which the
65
- # dependency is a sub-dependency.
66
- changed_reqs = reqs - dependencies.flat_map(&:previous_requirements)
67
- changed_reqs.none? ? subdependency_resolver : :requirements
68
- end
69
-
70
- def subdependency_resolver
71
- return :pipfile if pipfile_lock
72
- return :poetry if poetry_lock || pyproject_lock
73
- return :pip_compile if pip_compile_files.any?
74
-
75
- raise "Claimed to be a sub-dependency, but no lockfile exists!"
76
- end
77
-
78
- def updated_pipfile_based_files
79
- PipfileFileUpdater.new(
80
- dependencies: dependencies,
81
- dependency_files: dependency_files,
82
- credentials: credentials
83
- ).updated_dependency_files
84
- end
85
-
86
- def updated_poetry_based_files
87
- PoetryFileUpdater.new(
88
- dependencies: dependencies,
89
- dependency_files: dependency_files,
90
- credentials: credentials
91
- ).updated_dependency_files
92
- end
93
-
94
- def updated_pip_compile_based_files
95
- PipCompileFileUpdater.new(
96
- dependencies: dependencies,
97
- dependency_files: dependency_files,
98
- credentials: credentials
99
- ).updated_dependency_files
100
- end
101
-
102
- def updated_requirement_based_files
103
- RequirementFileUpdater.new(
104
- dependencies: dependencies,
105
- dependency_files: dependency_files,
106
- credentials: credentials
107
- ).updated_dependency_files
108
- end
109
-
110
- def check_required_files
111
- filenames = dependency_files.map(&:name)
112
- return if filenames.any? { |name| name.end_with?(".txt", ".in") }
113
- return if pipfile
114
- return if pyproject
115
- return if get_original_file("setup.py")
116
-
117
- raise "No requirements.txt or setup.py!"
118
- end
119
-
120
- def pipfile
121
- @pipfile ||= get_original_file("Pipfile")
122
- end
123
-
124
- def pipfile_lock
125
- @pipfile_lock ||= get_original_file("Pipfile.lock")
126
- end
127
-
128
- def pyproject
129
- @pyproject ||= get_original_file("pyproject.toml")
130
- end
131
-
132
- def pyproject_lock
133
- @pyproject_lock ||= get_original_file("pyproject.lock")
134
- end
135
-
136
- def poetry_lock
137
- @poetry_lock ||= get_original_file("poetry.lock")
138
- end
139
-
140
- def pip_compile_files
141
- @pip_compile_files ||=
142
- dependency_files.select { |f| f.name.end_with?(".in") }
143
- end
144
- end
145
- end
146
- end
147
- end
@@ -1,363 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "python_requirement_parser"
4
- require "dependabot/file_fetchers/python/pip"
5
- require "dependabot/file_updaters/python/pip"
6
- require "dependabot/shared_helpers"
7
-
8
- # rubocop:disable Metrics/ClassLength
9
- module Dependabot
10
- module FileUpdaters
11
- module Python
12
- class Pip
13
- class PipCompileFileUpdater
14
- require_relative "requirement_replacer"
15
- require_relative "requirement_file_updater"
16
- require_relative "setup_file_sanitizer"
17
-
18
- UNSAFE_PACKAGES = %w(setuptools distribute pip).freeze
19
-
20
- attr_reader :dependencies, :dependency_files, :credentials
21
-
22
- def initialize(dependencies:, dependency_files:, credentials:)
23
- @dependencies = dependencies
24
- @dependency_files = dependency_files
25
- @credentials = credentials
26
- end
27
-
28
- def updated_dependency_files
29
- return @updated_dependency_files if @update_already_attempted
30
-
31
- @update_already_attempted = true
32
- @updated_dependency_files ||= fetch_updated_dependency_files
33
- end
34
-
35
- private
36
-
37
- def dependency
38
- # For now, we'll only ever be updating a single dependency
39
- dependencies.first
40
- end
41
-
42
- def fetch_updated_dependency_files
43
- updated_compiled_files = compile_new_requirement_files
44
- updated_manifest_files = update_manifest_files
45
-
46
- updated_files = updated_compiled_files + updated_manifest_files
47
- updated_uncompiled_files = update_uncompiled_files(updated_files)
48
-
49
- [
50
- *updated_manifest_files,
51
- *updated_compiled_files,
52
- *updated_uncompiled_files
53
- ]
54
- end
55
-
56
- def compile_new_requirement_files
57
- SharedHelpers.in_a_temporary_directory do
58
- write_updated_dependency_files
59
-
60
- filenames_to_compile.each do |filename|
61
- # Shell out to pip-compile, generate a new set of requirements.
62
- # This is slow, as pip-compile needs to do installs.
63
- run_command(
64
- "pyenv exec pip-compile #{pip_compile_options(filename)} "\
65
- "-P #{dependency.name} #{filename}"
66
- )
67
- end
68
-
69
- dependency_files.map do |file|
70
- next unless file.name.end_with?(".txt")
71
-
72
- updated_content = File.read(file.name)
73
-
74
- updated_content =
75
- replace_header_with_original(updated_content, file.content)
76
- next if updated_content == file.content
77
-
78
- file.dup.tap { |f| f.content = updated_content }
79
- end.compact
80
- end
81
- end
82
-
83
- def update_manifest_files
84
- dependency_files.map do |file|
85
- next unless file.name.end_with?(".in")
86
-
87
- file = file.dup
88
- updated_content = update_dependency_requirement(file)
89
- next if updated_content == file.content
90
-
91
- file.content = updated_content
92
- file
93
- end.compact
94
- end
95
-
96
- def update_uncompiled_files(updated_files)
97
- updated_filenames = updated_files.map(&:name)
98
- old_reqs = dependency.previous_requirements.
99
- reject { |r| updated_filenames.include?(r[:file]) }
100
- new_reqs = dependency.requirements.
101
- reject { |r| updated_filenames.include?(r[:file]) }
102
-
103
- return [] if new_reqs.none?
104
-
105
- files = dependency_files.
106
- reject { |file| updated_filenames.include?(file.name) }
107
-
108
- args = dependency.to_h
109
- args = Hash[args.keys.map { |k| [k.to_sym, args[k]] }]
110
- args[:requirements] = new_reqs
111
- args[:previous_requirements] = old_reqs
112
-
113
- RequirementFileUpdater.new(
114
- dependencies: [Dependency.new(**args)],
115
- dependency_files: files,
116
- credentials: credentials
117
- ).updated_dependency_files
118
- end
119
-
120
- def run_command(command)
121
- command = command.dup
122
- raw_response = nil
123
- IO.popen(command, err: %i(child out)) do |process|
124
- raw_response = process.read
125
- end
126
-
127
- # Raise an error with the output from the shell session if
128
- # pip-compile returns a non-zero status
129
- return if $CHILD_STATUS.success?
130
-
131
- raise SharedHelpers::HelperSubprocessFailed.new(
132
- raw_response,
133
- command
134
- )
135
- rescue SharedHelpers::HelperSubprocessFailed => error
136
- original_error ||= error
137
- msg = error.message
138
-
139
- relevant_error =
140
- if error_suggests_bad_python_version?(msg) then original_error
141
- else error
142
- end
143
-
144
- raise relevant_error unless error_suggests_bad_python_version?(msg)
145
- raise relevant_error if File.exist?(".python-version")
146
-
147
- command = "pyenv local 2.7.15 && " + command
148
- retry
149
- ensure
150
- FileUtils.remove_entry(".python-version", true)
151
- end
152
-
153
- def error_suggests_bad_python_version?(message)
154
- return true if message.include?("not find a version that satisfies")
155
-
156
- message.include?('Command "python setup.py egg_info" failed')
157
- end
158
-
159
- def write_updated_dependency_files
160
- dependency_files.each do |file|
161
- next if file.name == ".python-version"
162
-
163
- path = file.name
164
- FileUtils.mkdir_p(Pathname.new(path).dirname)
165
- File.write(path, freeze_dependency_requirement(file))
166
- end
167
-
168
- setup_files.each do |file|
169
- path = file.name
170
- FileUtils.mkdir_p(Pathname.new(path).dirname)
171
- File.write(path, sanitized_setup_file_content(file))
172
- end
173
-
174
- setup_cfg_files.each do |file|
175
- path = file.name
176
- FileUtils.mkdir_p(Pathname.new(path).dirname)
177
- File.write(path, "[metadata]\nname = sanitized-package\n")
178
- end
179
- end
180
-
181
- def sanitized_setup_file_content(file)
182
- @sanitized_setup_file_content ||= {}
183
- if @sanitized_setup_file_content[file.name]
184
- return @sanitized_setup_file_content[file.name]
185
- end
186
-
187
- @sanitized_setup_file_content[file.name] =
188
- SetupFileSanitizer.
189
- new(setup_file: file, setup_cfg: setup_cfg(file)).
190
- sanitized_content
191
- end
192
-
193
- def setup_cfg(file)
194
- dependency_files.find do |f|
195
- f.name == file.name.sub(/\.py$/, ".cfg")
196
- end
197
- end
198
-
199
- def freeze_dependency_requirement(file)
200
- return file.content unless file.name.end_with?(".in")
201
-
202
- old_req = dependency.previous_requirements.
203
- find { |r| r[:file] == file.name }
204
-
205
- return file.content unless old_req
206
- return file.content if old_req == "==#{dependency.version}"
207
-
208
- RequirementReplacer.new(
209
- content: file.content,
210
- dependency_name: dependency.name,
211
- old_requirement: old_req[:requirement],
212
- new_requirement: "==#{dependency.version}"
213
- ).updated_content
214
- end
215
-
216
- def update_dependency_requirement(file)
217
- return file.content unless file.name.end_with?(".in")
218
-
219
- old_req = dependency.previous_requirements.
220
- find { |r| r[:file] == file.name }
221
- new_req = dependency.requirements.
222
- find { |r| r[:file] == file.name }
223
- return file.content unless old_req&.fetch(:requirement)
224
- return file.content if old_req == new_req
225
-
226
- RequirementReplacer.new(
227
- content: file.content,
228
- dependency_name: dependency.name,
229
- old_requirement: old_req[:requirement],
230
- new_requirement: new_req[:requirement]
231
- ).updated_content
232
- end
233
-
234
- def replace_header_with_original(updated_content, original_content)
235
- original_header_lines =
236
- original_content.lines.take_while { |l| l.start_with?("#") }
237
-
238
- updated_content_lines =
239
- updated_content.lines.drop_while { |l| l.start_with?("#") }
240
-
241
- [*original_header_lines, *updated_content_lines].join
242
- end
243
-
244
- def pip_compile_options(filename)
245
- current_requirements_file_name = filename.sub(/\.in$/, ".txt")
246
-
247
- requirements_file =
248
- dependency_files.
249
- find { |f| f.name == current_requirements_file_name }
250
-
251
- return unless requirements_file
252
-
253
- options = ""
254
-
255
- if requirements_file.content.include?("--hash=sha")
256
- options += " --generate-hashes"
257
- end
258
-
259
- if includes_unsafe_packages?(requirements_file.content)
260
- options += " --allow-unsafe"
261
- end
262
-
263
- unless requirements_file.content.include?("# via ")
264
- options += " --no-annotate"
265
- end
266
-
267
- unless requirements_file.content.include?("autogenerated by pip-c")
268
- options += " --no-header"
269
- end
270
-
271
- options.strip
272
- end
273
-
274
- def includes_unsafe_packages?(content)
275
- UNSAFE_PACKAGES.any? { |n| content.match?(/^#{Regexp.quote(n)}==/) }
276
- end
277
-
278
- def filenames_to_compile
279
- files_from_reqs =
280
- dependency.requirements.
281
- map { |r| r[:file] }.
282
- select { |fn| fn.end_with?(".in") }
283
-
284
- files_from_compiled_files =
285
- pip_compile_files.map(&:name).select do |fn|
286
- compiled_file = dependency_files.
287
- find { |f| f.name == fn.gsub(/\.in$/, ".txt") }
288
- compiled_file_includes_dependency?(compiled_file)
289
- end
290
-
291
- filenames = [*files_from_reqs, *files_from_compiled_files].uniq
292
-
293
- order_filenames_for_compilation(filenames)
294
- end
295
-
296
- def compiled_file_includes_dependency?(compiled_file)
297
- return false unless compiled_file
298
-
299
- regex = PythonRequirementParser::INSTALL_REQ_WITH_REQUIREMENT
300
-
301
- matches = []
302
- compiled_file.content.scan(regex) { matches << Regexp.last_match }
303
- matches.any? { |m| normalise(m[:name]) == dependency.name }
304
- end
305
-
306
- # See https://www.python.org/dev/peps/pep-0503/#normalized-names
307
- def normalise(name)
308
- name.downcase.gsub(/[-_.]+/, "-")
309
- end
310
-
311
- # If the files we need to update require one another then we need to
312
- # update them in the right order
313
- def order_filenames_for_compilation(filenames)
314
- ordered_filenames = []
315
-
316
- while (remaining_filenames = filenames - ordered_filenames).any?
317
- ordered_filenames +=
318
- remaining_filenames.
319
- select do |fn|
320
- unupdated_reqs = requirement_map[fn] - ordered_filenames
321
- (unupdated_reqs & filenames).empty?
322
- end
323
- end
324
-
325
- ordered_filenames
326
- end
327
-
328
- def requirement_map
329
- child_req_regex = FileFetchers::Python::Pip::CHILD_REQUIREMENT_REGEX
330
- @requirement_map ||=
331
- pip_compile_files.each_with_object({}) do |file, req_map|
332
- paths = file.content.scan(child_req_regex).flatten
333
- current_dir = File.dirname(file.name)
334
-
335
- req_map[file.name] =
336
- paths.map do |path|
337
- path = File.join(current_dir, path) if current_dir != "."
338
- path = Pathname.new(path).cleanpath.to_path
339
- path = path.gsub(/\.txt$/, ".in")
340
- next if path == file.name
341
-
342
- path
343
- end.uniq.compact
344
- end
345
- end
346
-
347
- def setup_files
348
- dependency_files.select { |f| f.name.end_with?("setup.py") }
349
- end
350
-
351
- def pip_compile_files
352
- dependency_files.select { |f| f.name.end_with?(".in") }
353
- end
354
-
355
- def setup_cfg_files
356
- dependency_files.select { |f| f.name.end_with?("setup.cfg") }
357
- end
358
- end
359
- end
360
- end
361
- end
362
- end
363
- # rubocop:enable Metrics/ClassLength