Class: Dependabot::Python::DependencyGrapher

Inherits:
DependencyGraphers::Base
  • Object
show all
Defined in:
lib/dependabot/python/dependency_grapher.rb,
lib/dependabot/python/dependency_grapher/lockfile_generator.rb,
lib/dependabot/python/dependency_grapher/requirements_layers.rb

Defined Under Namespace

Classes: LockfileGenerator, RequirementsLayers

Instance Method Summary collapse

Instance Method Details

#manifest_groupsObject



71
72
73
74
75
76
77
78
79
80
81
# File 'lib/dependabot/python/dependency_grapher.rb', line 71

def manifest_groups
  return super unless supports_layering?

  groups = RequirementsLayers.new(dependency_files: dependency_files).groups
  # If we try to apply grouping, but find there is only one group, we prefer
  # to fallback to the base method (the whole directory parsed as one manifest,
  # which naturally includes any setup.py/setup.cfg/pyproject.toml present).
  return super if groups.length < 2

  groups + non_requirements_manifest_groups
end

#prepare!Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/dependabot/python/dependency_grapher.rb', line 52

def prepare!
  # Exclude .txt files that don't look like pip manifests before we parse anything to avoid risk of bystander
  # files in non-pip projects.
  filter_non_manifest_txt_files!

  if poetry_project_without_lockfile?
    # Generating an ephemeral lockfile requires executing `poetry lock`. Strictly speaking, that violates our
    # policy of refusing to run Python tooling when external code execution is disallowed, so fail fast.
    raise Dependabot::UnexpectedExternalCode if file_parser.reject_external_code?

    Dependabot.logger.info("No poetry.lock found, generating ephemeral lockfile for dependency graphing")
    generate_ephemeral_lockfile!
    emit_missing_lockfile_warning! if @ephemeral_lockfile_generated
  end
  super
end

#relevant_dependency_fileObject

Raises:

  • (DependabotError)


25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/dependabot/python/dependency_grapher.rb', line 25

def relevant_dependency_file
  dependency_files_by_package_manager = T.let(
    {
      PipenvPackageManager::NAME => [pipfile_lock, pipfile],
      PoetryPackageManager::NAME => [committed_poetry_lock, pyproject_toml],
      PipCompilePackageManager::NAME => [pip_compile_lockfile, pip_compile_manifest, pyproject_toml],
      PipPackageManager::NAME => [pip_requirements_file, pyproject_toml, pipfile_lock, pipfile, setup_file,
                                  setup_cfg_file]
    },
    T::Hash[String, T::Array[T.nilable(Dependabot::DependencyFile)]]
  )

  candidates = dependency_files_by_package_manager.fetch(python_package_manager, [])
  relevant_file = candidates.compact.first
  return relevant_file if relevant_file

  # If we do not have any dependencies to report, the absence of a relevant manifest file is tolerable, it
  # means the file fetcher retrieved bystander `txt` files we have filtered out and the correct outcome
  # is a blank snapshot for this path.
  return empty_manifest_file if resolved_dependencies.empty?

  # If dependencies resolved but no owning manifest could be identified, we are in an inconsistent state
  # that we cannot represent.
  raise DependabotError, "No supported dependency file present."
end