class Dependabot::Uv::FileParser::PythonRequirementParser

def imputed_requirements

(e.g., Django 2.x implies Python 3)
TODO: Add better Python version detection using dependency versions
def imputed_requirements
  requirement_files.flat_map do |file|
    file.content.lines
        .select { |l| l.include?(";") && l.include?("python") }
        .filter_map { |l| l.match(/python_version(?<req>.*?["'].*?['"])/) }
        .map { |re| re.named_captures.fetch("req").gsub(/['"]/, "") }
        .select { |r| valid_requirement?(r) }
  end
end

def initialize(dependency_files:)

def initialize(dependency_files:)
  @dependency_files = dependency_files
end

def pip_compile_files

def pip_compile_files
  dependency_files.select { |f| f.name.end_with?(".in") }
end

def pip_compile_python_requirement

def pip_compile_python_requirement
  requirement_files.each do |file|
    next unless requirements_in_file_matcher.compiled_file?(file)
    marker = /^# This file is autogenerated by pip-compile with [pP]ython (?<version>\d+.\d+)$/m
    match = marker.match(file.content)
    next unless match
    return match[:version]
  end
  nil
end

def pyenv_versions

def pyenv_versions
  @pyenv_versions ||= run_command("pyenv install --list")
end

def pyproject

def pyproject
  dependency_files.find { |f| f.name == "pyproject.toml" }
end

def pyproject_python_requirement

def pyproject_python_requirement
  return unless pyproject
  pyproject_object = TomlRB.parse(pyproject.content)
  # Check for PEP621 requires-python
  pep621_python = pyproject_object.dig("project", "requires-python")
  return pep621_python if pep621_python
  # Fallback to Poetry configuration
  poetry_object = pyproject_object.dig("tool", "poetry")
  poetry_object&.dig("dependencies", "python") ||
    poetry_object&.dig("dev-dependencies", "python")
end

def python_version_file

def python_version_file
  dependency_files.find { |f| f.name == ".python-version" }
end

def python_version_file_version

def python_version_file_version
  return unless python_version_file
  # read the content, split into lines and remove any lines with '#'
  content_lines = python_version_file.content.each_line.map do |line|
    line.sub(/#.*$/, " ").strip
  end.reject(&:empty?)
  file_version = content_lines.first
  return if file_version&.empty?
  return unless pyenv_versions.include?("#{file_version}\n")
  file_version
end

def requirement_class

def requirement_class
  Dependabot::Uv::Requirement
end

def requirement_files

def requirement_files
  dependency_files.select { |f| f.name.end_with?(".txt") }
end

def requirements_in_file_matcher

def requirements_in_file_matcher
  @requirements_in_file_matcher ||= RequiremenstFileMatcher.new(pip_compile_files)
end

def run_command(command, env: {})

def run_command(command, env: {})
  SharedHelpers.run_shell_command(command, env: env, stderr_to_stdout: true)
end

def runtime_file

def runtime_file
  dependency_files.find { |f| f.name.end_with?("runtime.txt") }
end

def runtime_file_python_version

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

def user_specified_requirements

def user_specified_requirements
  [
    pyproject_python_requirement,
    pip_compile_python_requirement,
    python_version_file_version,
    runtime_file_python_version
  ].compact
end

def valid_requirement?(req_string)

def valid_requirement?(req_string)
  requirement_class.new(req_string)
  true
rescue Gem::Requirement::BadRequirementError
  false
end