dependabot-python 0.385.0 → 0.386.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: edc98d373ce3e27a5db1080f1ddc5f2a7a60c8be26a734ac889abf8e2298ede0
4
- data.tar.gz: fd9d7507adb7b29b972c2f318a785eea5efd4150e12e2a6a3fe5707463d38f8a
3
+ metadata.gz: d6743a3b5b5ae514de1fc09550af84d3285ae7fed69a8c74b8d95291b7cb2c1a
4
+ data.tar.gz: 1bde8ed1421e31bf67c7ca1fab04f7f0c2edd3fbd4afcb2eb38df5c0bba007d1
5
5
  SHA512:
6
- metadata.gz: 9d328bd3c10cb36152a63a2c3975303e111a540367ec6e464ef7ab8208bd2ee642c12561f72928116069160a39cf1b9aa0527b4efb7411966714ff6e41032cdf
7
- data.tar.gz: 2a00eef7b5ac804ee7389022190c84a933e9ae2afa89e37852ffb68729a591ceba986587f823ac1a0df460d6d42e72587979328104af12b2d53a05f99c16b150
6
+ metadata.gz: e484276f80b4690e9f41f471f18adb8f4e0adf1481c73c1912ead78274785d908cea245a50265a3f2e567ae643d5d4759fc1694fbc66ec91903f3c65136f95f7
7
+ data.tar.gz: f0b4c4092d96440d9e98acb64d750b380712cb456da886ecf4c8a6872ef5cb4b2f3d98551fb0e940878de37fe600dcc5981494e6d9eee978351f8ade1e584f99
@@ -0,0 +1,210 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "pathname"
5
+ require "sorbet-runtime"
6
+
7
+ require "dependabot/dependency_file"
8
+ require "dependabot/dependency_graphers/base"
9
+ require "dependabot/python/shared_file_fetcher"
10
+
11
+ module Dependabot
12
+ module Python
13
+ class DependencyGrapher < Dependabot::DependencyGraphers::Base
14
+ # Splits a directory of requirements files (pip / pip-compile) into one manifest group per "layer".
15
+ #
16
+ # Python is the reported case of "layered requirements": a single directory can hold multiple
17
+ # independent requirements files (e.g. base-requirements.txt, test-requirements.txt) cross-linked
18
+ # with `-r`/`-c`.
19
+ #
20
+ # Each 'layer' is its own manifest and must get its own snapshot, rather than the whole
21
+ # directory collapsing onto the first `.txt` alphabetically.
22
+ class RequirementsLayers
23
+ extend T::Sig
24
+
25
+ # Regex patterns for detecting Python requirements / dependencies .txt manifest variants.
26
+ # Used to filter out unrelated .txt files (e.g. README-style notes, tool output, etc.) from being
27
+ # treated as pip manifests.
28
+
29
+ # Matches "requirements" preceded by a hyphen, period, underscore, start-of-string, or slash,
30
+ # followed by non-whitespace chars and ".txt".
31
+ # Examples: requirements.txt, requirements.prod.txt, requirements/production.txt
32
+ REQUIREMENTS_TXT_REGEX = T.let(%r{(?:[-._]|^|/)requirements[^\s]*\.txt$}i, Regexp)
33
+
34
+ # More lenient: matches "require" at the start of the filename or after a hyphen/period/underscore/
35
+ # slash delimiter, with an optional hyphen/underscore/slash suffix. The leading delimiter prevents
36
+ # matching "require" as a substring of another word (e.g. "prequire.txt", "acquire.txt").
37
+ # Examples: require.txt, require-test.txt, py3-require.txt, pyenv_require_e2e.txt
38
+ REQUIRE_TXT_REGEX = T.let(%r{(?:[-._]|^|/)require(?:[-_/][^\s.]*)?\.txt$}i, Regexp)
39
+
40
+ # Matches "dependencies" / "dependency" preceded by a hyphen, period, underscore,
41
+ # start-of-string, or slash, followed by non-whitespace chars and ".txt".
42
+ # Examples: dependencies.txt, my-dependencies.txt, dependencies/python/ansible-lint.txt
43
+ DEPENDENCIES_TXT_REGEX = T.let(%r{(?:[-._]|^|/)dependenc(?:y|ies)[^\s]*\.txt$}i, Regexp)
44
+
45
+ # More lenient: matches "depend" / "depends" at the start of the filename or after a hyphen/period/
46
+ # underscore/slash delimiter, with an optional hyphen/underscore/slash suffix. The leading delimiter
47
+ # prevents matching "depend" as a substring of another word (e.g. "independ.txt").
48
+ # Examples: depend.txt, depends.txt, depend-test.txt, py3-depends.txt
49
+ DEPEND_TXT_REGEX = T.let(%r{(?:[-._]|^|/)depend(?:s)?(?:[-_/][^\s.]*)?\.txt$}i, Regexp)
50
+
51
+ # Whether a .txt filename looks like a real pip manifest rather than an unrelated .txt file.
52
+ sig { params(path: String).returns(T::Boolean) }
53
+ def self.manifest_txt_filename?(path)
54
+ path.match?(REQUIREMENTS_TXT_REGEX) ||
55
+ path.match?(REQUIRE_TXT_REGEX) ||
56
+ path.match?(DEPENDENCIES_TXT_REGEX) ||
57
+ path.match?(DEPEND_TXT_REGEX)
58
+ end
59
+
60
+ # Resolves the sibling paths a requirements file references via `-r`/`-c`, returning repo-relative
61
+ # cleanpaths (matching fetched DependencyFile names). Single home for reference resolution, shared by the
62
+ # grapher's bystander filter and the layering group builder so reference-syntax handling never drifts.
63
+ #
64
+ # Reuses SharedFileFetcher's reference regexes so callers keep exactly the children the file fetcher pulled in.
65
+ sig { params(file: Dependabot::DependencyFile).returns(T::Array[String]) }
66
+ def self.referenced_paths(file)
67
+ content = file.content
68
+ return [] if content.nil?
69
+
70
+ current_dir = File.dirname(file.name)
71
+ referenced =
72
+ content.scan(Dependabot::Python::SharedFileFetcher::CHILD_REQUIREMENT_REGEX).flatten +
73
+ content.scan(Dependabot::Python::SharedFileFetcher::CONSTRAINT_REGEX).flatten
74
+
75
+ referenced.map do |path|
76
+ resolved = current_dir == "." ? path : File.join(current_dir, path)
77
+ Pathname.new(resolved).cleanpath.to_path
78
+ end
79
+ end
80
+
81
+ sig { params(dependency_files: T::Array[Dependabot::DependencyFile]).void }
82
+ def initialize(dependency_files:)
83
+ @dependency_files = dependency_files
84
+ end
85
+
86
+ # Builds one manifest group per requirements layer. A layer's primary is its compiled `.txt` (the
87
+ # most specific file, akin to a lockfile) when present, otherwise its `.in`. Each group also includes
88
+ # the paired `.in`/`.txt`, any constraints files, and any sibling files referenced via `-r`/`-c` so
89
+ # the parser can resolve the layer in isolation.
90
+ sig { returns(T::Array[Dependabot::DependencyGraphers::ManifestGroup]) }
91
+ def groups
92
+ layer_primaries.map do |primary|
93
+ Dependabot::DependencyGraphers::ManifestGroup.new(
94
+ primary: primary,
95
+ files: files_for_layer(primary)
96
+ )
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
103
+ attr_reader :dependency_files
104
+
105
+ # The set of files that act as the owning manifest for a layer: every requirements `.txt` that looks
106
+ # like a real manifest, plus any `.in` file that has no compiled `.txt` counterpart.
107
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
108
+ def layer_primaries
109
+ txt_primaries = requirement_family_files.select do |f|
110
+ f.name.end_with?(".txt") && self.class.manifest_txt_filename?(f.name)
111
+ end
112
+ compiled_stems = txt_primaries.map { |f| requirements_stem(f.name) }
113
+
114
+ in_primaries = requirement_family_files.select do |f|
115
+ f.name.end_with?(".in") && !compiled_stems.include?(requirements_stem(f.name))
116
+ end
117
+
118
+ txt_primaries + in_primaries
119
+ end
120
+
121
+ # All the files a single layer needs to parse: the primary, its paired `.in`/`.txt`, constraints
122
+ # files and any `-r`/`-c` referenced siblings (added as support files so they never win attribution).
123
+ sig { params(primary: Dependabot::DependencyFile).returns(T::Array[Dependabot::DependencyFile]) }
124
+ def files_for_layer(primary)
125
+ stem = requirements_stem(primary.name)
126
+
127
+ paired = requirement_family_files.select do |f|
128
+ f != primary && requirements_stem(f.name) == stem
129
+ end
130
+
131
+ referenced = referenced_requirement_files(primary)
132
+
133
+ support = (paired + referenced + constraints_files).uniq.map do |f|
134
+ as_support_file(f)
135
+ end
136
+
137
+ [primary] + support
138
+ end
139
+
140
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
141
+ def requirement_family_files
142
+ @requirement_family_files ||= T.let(
143
+ dependency_files.select { |f| f.name.end_with?(".txt", ".in") },
144
+ T.nilable(T::Array[Dependabot::DependencyFile])
145
+ )
146
+ end
147
+
148
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
149
+ def constraints_files
150
+ requirement_family_files.select { |f| File.basename(f.name).include?("constraint") }
151
+ end
152
+
153
+ # Finds sibling files referenced by a requirements file via `-r`/`-c`, following the chain transitively.
154
+ #
155
+ # A referenced file can itself reference further files (e.g. `develop.in -r test.in`, `test.in -r base.in`),
156
+ # so we walk the whole closure to gather every sibling the layer needs on disk to parse in isolation.
157
+ sig { params(file: Dependabot::DependencyFile).returns(T::Array[Dependabot::DependencyFile]) }
158
+ def referenced_requirement_files(file)
159
+ by_name = requirement_family_files.to_h { |f| [f.name, f] }
160
+ seen = T.let(Set.new, T::Set[String])
161
+ queue = [file]
162
+ collected = T.let([], T::Array[Dependabot::DependencyFile])
163
+
164
+ until queue.empty?
165
+ current = T.must(queue.shift)
166
+ self.class.referenced_paths(current).each do |path|
167
+ next if seen.include?(path)
168
+
169
+ seen << path
170
+ referenced = by_name[path]
171
+ next if referenced.nil?
172
+
173
+ collected << referenced
174
+ queue << referenced
175
+ end
176
+ end
177
+
178
+ collected
179
+ end
180
+
181
+ # Returns a support-file copy of the given file so it can be parsed for cross-reference resolution
182
+ # without becoming an attribution target. Never mutates the shared DependencyFile.
183
+ sig { params(file: Dependabot::DependencyFile).returns(Dependabot::DependencyFile) }
184
+ def as_support_file(file)
185
+ return file if file.support_file?
186
+
187
+ Dependabot::DependencyFile.new(
188
+ name: file.name,
189
+ content: file.content,
190
+ directory: file.directory,
191
+ type: file.type,
192
+ support_file: true,
193
+ vendored_file: file.vendored_file,
194
+ symlink_target: file.symlink_target,
195
+ content_encoding: file.content_encoding,
196
+ deleted: file.deleted?,
197
+ operation: file.operation,
198
+ mode: file.mode
199
+ )
200
+ end
201
+
202
+ # The stem shared by a pip-compile `.in` and its compiled `.txt` (e.g. "base-requirements").
203
+ sig { params(name: String).returns(String) }
204
+ def requirements_stem(name)
205
+ name.sub(/\.(txt|in)\z/, "")
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
@@ -3,6 +3,7 @@
3
3
 
4
4
  require "json"
5
5
  require "sorbet-runtime"
6
+ require "pathname"
6
7
 
7
8
  require "dependabot/dependency_graphers"
8
9
  require "dependabot/dependency_graphers/base"
@@ -11,36 +12,14 @@ require "dependabot/python/language_version_manager"
11
12
  require "dependabot/python/name_normaliser"
12
13
  require "dependabot/python/pip_compile_file_matcher"
13
14
  require "dependabot/python/pipenv_runner"
15
+ require "dependabot/python/shared_file_fetcher"
14
16
  require "toml-rb"
15
17
 
16
18
  module Dependabot
17
19
  module Python
18
20
  class DependencyGrapher < Dependabot::DependencyGraphers::Base
19
21
  require_relative "dependency_grapher/lockfile_generator"
20
-
21
- # Regex patterns for detecting Python requirements / dependencies .txt manifest variants.
22
- # Used by the dependency grapher to filter out unrelated .txt files (e.g. README-style notes,
23
- # tool output, etc.) from being treated as pip manifests.
24
-
25
- # Matches "requirements" preceded by a hyphen, period, underscore, start-of-string, or slash,
26
- # followed by non-whitespace chars and ".txt".
27
- # Examples: requirements.txt, requirements.prod.txt, requirements/production.txt
28
- REQUIREMENTS_TXT_REGEX = T.let(%r{(?:[-._]|^|/)requirements[^\s]*\.txt$}i, Regexp)
29
-
30
- # More lenient: matches "require" with optional prefix (no dots/whitespace)
31
- # and optional hyphen/underscore/slash suffix. Does not match "require" as a substring.
32
- # Examples: require.txt, require-test.txt, py3-require.txt, pyenv_require_e2e.txt
33
- REQUIRE_TXT_REGEX = T.let(%r{[^\s|.]*require(?:[-_/][^\s|.]*)?\.txt$}i, Regexp)
34
-
35
- # Matches "dependencies" / "dependency" preceded by a hyphen, period, underscore,
36
- # start-of-string, or slash, followed by non-whitespace chars and ".txt".
37
- # Examples: dependencies.txt, my-dependencies.txt, dependencies/python/ansible-lint.txt
38
- DEPENDENCIES_TXT_REGEX = T.let(%r{(?:[-._]|^|/)dependenc(?:y|ies)[^\s]*\.txt$}i, Regexp)
39
-
40
- # More lenient: matches "depend" / "depends" with optional prefix (no dots/whitespace)
41
- # and optional hyphen/underscore/slash suffix. Does not match "depend" as a substring.
42
- # Examples: depend.txt, depends.txt, depend-test.txt, py3-depends.txt
43
- DEPEND_TXT_REGEX = T.let(%r{[^\s|.]*depend(?:s)?(?:[-_/][^\s|.]*)?\.txt$}i, Regexp)
22
+ require_relative "dependency_grapher/requirements_layers"
44
23
 
45
24
  sig { override.returns(Dependabot::DependencyFile) }
46
25
  def relevant_dependency_file
@@ -59,11 +38,22 @@ module Dependabot
59
38
  relevant_file = candidates.compact.first
60
39
  return relevant_file if relevant_file
61
40
 
41
+ # If we do not have any dependencies to report, the absence of a relevant manifest file is tolerable, it
42
+ # means the file fetcher retrieved bystander `txt` files we have filtered out and the correct outcome
43
+ # is a blank snapshot for this path.
44
+ return empty_manifest_file if resolved_dependencies.empty?
45
+
46
+ # If dependencies resolved but no owning manifest could be identified, we are in an inconsistent state
47
+ # that we cannot represent.
62
48
  raise DependabotError, "No supported dependency file present."
63
49
  end
64
50
 
65
51
  sig { override.void }
66
52
  def prepare!
53
+ # Exclude .txt files that don't look like pip manifests before we parse anything to avoid risk of bystander
54
+ # files in non-pip projects.
55
+ filter_non_manifest_txt_files!
56
+
67
57
  if poetry_project_without_lockfile?
68
58
  # Generating an ephemeral lockfile requires executing `poetry lock`. Strictly speaking, that violates our
69
59
  # policy of refusing to run Python tooling when external code execution is disallowed, so fail fast.
@@ -76,8 +66,126 @@ module Dependabot
76
66
  super
77
67
  end
78
68
 
69
+ # Layering is specific to pip / pip-compile for Python.
70
+ sig { override.returns(T::Array[Dependabot::DependencyGraphers::ManifestGroup]) }
71
+ def manifest_groups
72
+ return super unless supports_layering?
73
+
74
+ groups = RequirementsLayers.new(dependency_files: dependency_files).groups
75
+ # If we try to apply grouping, but find there is only one group, we prefer
76
+ # to fallback to the base method (the whole directory parsed as one manifest,
77
+ # which naturally includes any setup.py/setup.cfg/pyproject.toml present).
78
+ return super if groups.length < 2
79
+
80
+ groups + non_requirements_manifest_groups
81
+ end
82
+
79
83
  private
80
84
 
85
+ # When a pip/pip-compile directory is split into requirements layers, any non-requirements manifest
86
+ # sharing that directory (setup.py, setup.cfg, pyproject.toml) would otherwise fall outside every layer
87
+ # group and have its dependencies dropped.
88
+ #
89
+ # We instead emit each as its own self-attributed group so its dependencies are preserved and attributed
90
+ # to the file itself. This matches existing static analysis behaviour.
91
+ #
92
+ # NOTE:
93
+ # This logic is only applied on the pip/pip-compile path, so poetry.lock/Pipfile.lock are absent as they would
94
+ # select the poetry/pipenv path which does not support layering.
95
+ #
96
+ # If a pyproject.toml is present, it is treated as a pip-context manifest.
97
+ sig { returns(T::Array[Dependabot::DependencyGraphers::ManifestGroup]) }
98
+ def non_requirements_manifest_groups
99
+ [setup_file, setup_cfg_file, pyproject_toml].compact.map do |file|
100
+ Dependabot::DependencyGraphers::ManifestGroup.new(primary: file, files: [file])
101
+ end
102
+ end
103
+
104
+ # An empty, nameless dependency file used to represent a directory that has no supported manifest and
105
+ # resolves to no dependencies. The submission layer treats a nameless manifest as "nothing to report",
106
+ # producing a valid, empty snapshot for the directory instead of a failure.
107
+ sig { returns(Dependabot::DependencyFile) }
108
+ def empty_manifest_file
109
+ Dependabot::DependencyFile.new(
110
+ name: "",
111
+ content: "",
112
+ directory: file_parser.source&.directory || "/"
113
+ )
114
+ end
115
+
116
+ # Non-.txt files (pyproject.toml, setup.py, Pipfile, lockfiles, .in files, etc.) are always retained.
117
+ # A .txt file is kept when its name looks like a pip requirements/dependencies manifest, the pip-compile
118
+ # matcher recognises it as a compiled lockfile, or it is referenced (transitively) via `-r`/`-c` from a
119
+ # retained requirements file. The last case preserves constraint/child files (e.g. `constraints.txt`) that
120
+ # the parser needs on disk to resolve a real manifest, while still dropping bystander `.txt` files.
121
+ sig { void }
122
+ def filter_non_manifest_txt_files!
123
+ keep = txt_files_to_keep
124
+
125
+ file_parser.dependency_files.reject! do |file|
126
+ next false unless file.name.end_with?(".txt")
127
+
128
+ !keep.include?(file.name)
129
+ end
130
+ end
131
+
132
+ # Names of the `.txt` files that must be retained: those that look like manifests or pip-compile lockfiles,
133
+ # plus any `.txt` reachable via `-r`/`-c` references from a retained requirements file (`.in` files are
134
+ # never dropped, so references originating from them are followed too).
135
+ sig { returns(T::Set[String]) }
136
+ def txt_files_to_keep
137
+ files_by_name = dependency_files.to_h { |file| [file.name, file] }
138
+
139
+ seeds = dependency_files.select do |file|
140
+ file.name.end_with?(".txt") &&
141
+ (RequirementsLayers.manifest_txt_filename?(file.name) ||
142
+ pip_compile_file_matcher.lockfile_for_pip_compile_file?(file))
143
+ end
144
+
145
+ # `.in` files are always retained, so a `.txt` they reference must be kept too.
146
+ roots = seeds + dependency_files.select { |file| file.name.end_with?(".in") }
147
+
148
+ reachable_txt_names(roots, files_by_name, Set.new(seeds.map(&:name)))
149
+ end
150
+
151
+ # Breadth-first closure over `-r`/`-c` references starting from `roots`, adding every referenced `.txt`
152
+ # file that exists in `files_by_name` to `keep`.
153
+ sig do
154
+ params(
155
+ roots: T::Array[Dependabot::DependencyFile],
156
+ files_by_name: T::Hash[String, Dependabot::DependencyFile],
157
+ keep: T::Set[String]
158
+ ).returns(T::Set[String])
159
+ end
160
+ def reachable_txt_names(roots, files_by_name, keep)
161
+ queue = roots.dup
162
+ until queue.empty?
163
+ file = T.must(queue.shift)
164
+ referenced_txt_names(file).each do |name|
165
+ referenced_file = files_by_name[name]
166
+ next if referenced_file.nil? || keep.include?(name)
167
+
168
+ keep << name
169
+ queue << referenced_file
170
+ end
171
+ end
172
+
173
+ keep
174
+ end
175
+
176
+ # Resolves the `.txt` files referenced from a requirements file via `-r`/`-c`, returning their names
177
+ # relative to the repo (matching the fetched DependencyFile names) so we can retain them. Delegates to
178
+ # RequirementsLayers.referenced_paths so grouping and the bystander filter resolve references identically.
179
+ sig { params(file: Dependabot::DependencyFile).returns(T::Array[String]) }
180
+ def referenced_txt_names(file)
181
+ RequirementsLayers.referenced_paths(file).select { |path| path.end_with?(".txt") }
182
+ end
183
+
184
+ sig { returns(T::Boolean) }
185
+ def supports_layering?
186
+ [PipPackageManager::NAME, PipCompilePackageManager::NAME].include?(python_package_manager)
187
+ end
188
+
81
189
  # Returns the poetry.lock only if it was committed to the repo,
82
190
  # not if it was generated ephemerally. This ensures that
83
191
  # relevant_dependency_file reports the real manifest (pyproject.toml).
@@ -380,19 +488,11 @@ module Dependabot
380
488
 
381
489
  @pip_requirements_file = T.let(
382
490
  dependency_files.find { |f| f.name == "requirements.txt" } ||
383
- dependency_files.find { |f| f.name.end_with?(".txt") && python_manifest_txt_filename?(f.name) },
491
+ dependency_files.find { |f| f.name.end_with?(".txt") && RequirementsLayers.manifest_txt_filename?(f.name) },
384
492
  T.nilable(Dependabot::DependencyFile)
385
493
  )
386
494
  end
387
495
 
388
- sig { params(path: String).returns(T::Boolean) }
389
- def python_manifest_txt_filename?(path)
390
- path.match?(REQUIREMENTS_TXT_REGEX) ||
391
- path.match?(REQUIRE_TXT_REGEX) ||
392
- path.match?(DEPENDENCIES_TXT_REGEX) ||
393
- path.match?(DEPEND_TXT_REGEX)
394
- end
395
-
396
496
  sig { params(filename: String).returns(T.nilable(Dependabot::DependencyFile)) }
397
497
  def dependency_file(filename)
398
498
  dependency_files.find { |file| file.name == filename }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-python
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.385.0
4
+ version: 0.386.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.385.0
18
+ version: 0.386.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.385.0
25
+ version: 0.386.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: debug
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -277,6 +277,7 @@ files:
277
277
  - lib/dependabot/python/authed_url_builder.rb
278
278
  - lib/dependabot/python/dependency_grapher.rb
279
279
  - lib/dependabot/python/dependency_grapher/lockfile_generator.rb
280
+ - lib/dependabot/python/dependency_grapher/requirements_layers.rb
280
281
  - lib/dependabot/python/file_fetcher.rb
281
282
  - lib/dependabot/python/file_parser.rb
282
283
  - lib/dependabot/python/file_parser/pipfile_files_parser.rb
@@ -322,7 +323,7 @@ licenses:
322
323
  - MIT
323
324
  metadata:
324
325
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
325
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.385.0
326
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.386.0
326
327
  rdoc_options: []
327
328
  require_paths:
328
329
  - lib