class Dependabot::Python::FileParser::PythonRequirementParser

Attributes

dependency_files[R]

Public Class Methods

new(dependency_files:) click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 16
def initialize(dependency_files:)
  @dependency_files = dependency_files
end

Public Instance Methods

imputed_requirements() click to toggle source

TODO: Add better Python version detection using dependency versions (e.g., Django 2.x implies Python 3)

# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 32
def imputed_requirements
  requirement_files.flat_map do |file|
    file.content.lines.
      select { |l| l.include?(";") && l.include?("python") }.
      map { |l| l.match(/python_version(?<req>.*?["'].*?['"])/) }.
      compact.
      map { |re| re.named_captures.fetch("req").gsub(/['"]/, "") }.
      select { |r| valid_requirement?(r) }
  end
end
user_specified_requirements() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 20
def user_specified_requirements
  [
    pipfile_python_requirement,
    pyproject_python_requirement,
    python_version_file_version,
    runtime_file_python_version,
    setup_file_requirement
  ].compact
end

Private Instance Methods

pipfile() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 134
def pipfile
  dependency_files.find { |f| f.name == "Pipfile" }
end
pipfile_lock() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 138
def pipfile_lock
  dependency_files.find { |f| f.name == "Pipfile.lock" }
end
pipfile_python_requirement() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 45
def pipfile_python_requirement
  return unless pipfile

  parsed_pipfile = TomlRB.parse(pipfile.content)
  requirement =
    parsed_pipfile.dig("requires", "python_full_version") ||
    parsed_pipfile.dig("requires", "python_version")
  return unless requirement&.match?(/^\d/)

  requirement
end
pyenv_versions() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 101
def pyenv_versions
  @pyenv_versions ||= run_command("pyenv install --list")
end
pyproject() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 142
def pyproject
  dependency_files.find { |f| f.name == "pyproject.toml" }
end
pyproject_python_requirement() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 57
def pyproject_python_requirement
  return unless pyproject

  pyproject_object = TomlRB.parse(pyproject.content)
  poetry_object = pyproject_object.dig("tool", "poetry")

  poetry_object&.dig("dependencies", "python") ||
    poetry_object&.dig("dev-dependencies", "python")
end
python_version_file() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 150
def python_version_file
  dependency_files.find { |f| f.name == ".python-version" }
end
python_version_file_version() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 67
def python_version_file_version
  return unless python_version_file

  file_version = python_version_file.content.strip
  return if file_version&.empty?
  return unless pyenv_versions.include?("#{file_version}\n")

  file_version
end
requirement_class() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 123
def requirement_class
  Dependabot::Python::Requirement
end
requirement_files() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 158
def requirement_files
  dependency_files.select { |f| f.name.end_with?(".txt") }
end
run_command(command, env: {}) click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 105
def run_command(command, env: {})
  start = Time.now
  command = SharedHelpers.escape_command(command)
  stdout, process = Open3.capture2e(env, command)
  time_taken = Time.now - start

  return stdout if process.success?

  raise SharedHelpers::HelperSubprocessFailed.new(
    message: stdout,
    error_context: {
      command: command,
      time_taken: time_taken,
      process_exit_value: process.to_s
    }
  )
end
runtime_file() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 154
def runtime_file
  dependency_files.find { |f| f.name.end_with?("runtime.txt") }
end
runtime_file_python_version() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 77
def runtime_file_python_version
  return unless runtime_file

  file_version = runtime_file.content.
                 match(/(?<=python-).*/)&.to_s&.strip
  return if file_version&.empty?
  return unless pyenv_versions.include?("#{file_version}\n")

  file_version
end
setup_file() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 146
def setup_file
  dependency_files.find { |f| f.name == "setup.py" }
end
setup_file_requirement() click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 88
def setup_file_requirement
  return unless setup_file

  req = setup_file.content.
        match(/python_requires\s*=\s*['"](?<req>[^'"]+)['"]/)&.
        named_captures&.fetch("req")&.strip

  requirement_class.new(req)
  req
rescue Gem::Requirement::BadRequirementError
  nil
end
valid_requirement?(req_string) click to toggle source
# File lib/dependabot/python/file_parser/python_requirement_parser.rb, line 127
def valid_requirement?(req_string)
  requirement_class.new(req_string)
  true
rescue Gem::Requirement::BadRequirementError
  false
end