class Dependabot::Python::FileParser
Constants
- DEPENDENCY_GROUP_KEYS
- POETRY_DEPENDENCY_TYPES
- REQUIREMENT_FILE_EVALUATION_ERRORS
Public Instance Methods
parse()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 38 def parse # TODO: setup.py from external dependencies is evaluated. Provide guards before removing this. raise Dependabot::UnexpectedExternalCode if @reject_external_code dependency_set = DependencySet.new dependency_set += pipenv_dependencies if pipfile dependency_set += poetry_dependencies if using_poetry? dependency_set += requirement_dependencies if requirement_files.any? dependency_set += setup_file_dependencies if setup_file || setup_cfg_file dependency_set.dependencies end
Private Instance Methods
blocking_marker?(dep)
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 125 def blocking_marker?(dep) return false if dep["markers"] == "None" return true if dep["markers"].include?("<") return false if dep["markers"].include?(">") dep["requirement"]&.include?("<") end
check_required_files()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 204 def check_required_files filenames = dependency_files.map(&:name) return if filenames.any? { |name| name.end_with?(".txt", ".in") } return if pipfile return if pyproject return if setup_file return if setup_cfg_file raise "Missing required files!" end
check_requirements(requirements)
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 171 def check_requirements(requirements) requirements.each do |dep| next unless dep["requirement"] Python::Requirement.new(dep["requirement"].split(",")) rescue Gem::Requirement::BadRequirementError => e raise Dependabot::DependencyFileNotEvaluatable, e.message end end
group_from_filename(filename)
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 107 def group_from_filename(filename) if filename.include?("dev") then ["dev-dependencies"] else ["dependencies"] end end
included_in_pipenv_deps?(dep_name)
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 113 def included_in_pipenv_deps?(dep_name) return false unless pipfile pipenv_dependencies.dependencies.map(&:name).include?(dep_name) end
included_in_poetry_deps?(dep_name)
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 119 def included_in_poetry_deps?(dep_name) return false unless using_poetry? poetry_dependencies.dependencies.map(&:name).include?(dep_name) end
lockfile_for_pip_compile_file?(filename)
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 140 def lockfile_for_pip_compile_file?(filename) return false unless pip_compile_files.any? return false unless filename.end_with?(".txt") file = dependency_files.find { |f| f.name == filename } return true if file&.content&.match?(output_file_regex(filename)) basename = filename.gsub(/\.txt$/, "") pip_compile_files.any? { |f| f.name == basename + ".in" } end
normalised_name(name, extras = [])
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 200 def normalised_name(name, extras = []) NameNormaliser.normalise_including_extras(name, extras) end
output_file_regex(filename)
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 232 def output_file_regex(filename) "--output-file[=\s]+#{Regexp.escape(filename)}(?:\s|$)" end
parsed_requirement_files()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 151 def parsed_requirement_files SharedHelpers.in_a_temporary_directory do write_temporary_dependency_files requirements = SharedHelpers.run_helper_subprocess( command: "pyenv exec python #{NativeHelpers.python_helper_path}", function: "parse_requirements", args: [Dir.pwd] ) check_requirements(requirements) requirements end rescue SharedHelpers::HelperSubprocessFailed => e evaluation_errors = REQUIREMENT_FILE_EVALUATION_ERRORS raise unless e.message.start_with?(*evaluation_errors) raise Dependabot::DependencyFileNotEvaluatable, e.message end
pip_compile_files()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 256 def pip_compile_files @pip_compile_files ||= dependency_files.select { |f| f.name.end_with?(".in") } end
pipenv_dependencies()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 58 def pipenv_dependencies @pipenv_dependencies ||= PipfileFilesParser. new(dependency_files: dependency_files). dependency_set end
pipfile()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 215 def pipfile @pipfile ||= get_original_file("Pipfile") end
pipfile_lock()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 219 def pipfile_lock @pipfile_lock ||= get_original_file("Pipfile.lock") end
poetry_dependencies()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 65 def poetry_dependencies @poetry_dependencies ||= PoetryFilesParser. new(dependency_files: dependency_files). dependency_set end
poetry_lock()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 244 def poetry_lock @poetry_lock ||= get_original_file("poetry.lock") end
pyproject()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 236 def pyproject @pyproject ||= get_original_file("pyproject.toml") end
pyproject_lock()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 240 def pyproject_lock @pyproject_lock ||= get_original_file("pyproject.lock") end
remove_imports(file)
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 191 def remove_imports(file) return file.content if file.path.end_with?(".tar.gz", ".whl", ".zip") file.content.lines. reject { |l| l.match?(/^['"]?(?<path>\..*?)(?=\[|#|'|"|$)/) }. reject { |l| l.match?(/^(?:-e)\s+['"]?(?<path>.*?)(?=\[|#|'|"|$)/) }. join end
requirement_dependencies()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 72 def requirement_dependencies dependencies = DependencySet.new parsed_requirement_files.each do |dep| # This isn't ideal, but currently the FileUpdater won't update # deps that appear in a requirements.txt and Pipenv / Poetry # and *aren't* a straight lockfile for Pipenv / Poetry next if included_in_pipenv_deps?(normalised_name(dep["name"])) next if included_in_poetry_deps?(normalised_name(dep["name"])) # If a requirement has a `<`, `<=` or '==' marker then updating it is # probably blocked. Ignore it. next if blocking_marker?(dep) requirements = if lockfile_for_pip_compile_file?(dep["file"]) then [] else [{ requirement: dep["requirement"], file: Pathname.new(dep["file"]).cleanpath.to_path, source: nil, groups: group_from_filename(dep["file"]) }] end dependencies << Dependency.new( name: normalised_name(dep["name"], dep["extras"]), version: dep["version"]&.include?("*") ? nil : dep["version"], requirements: requirements, package_manager: "pip" ) end dependencies end
requirement_files()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 54 def requirement_files dependency_files.select { |f| f.name.end_with?(".txt", ".in") } end
setup_cfg_file()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 252 def setup_cfg_file @setup_cfg_file ||= get_original_file("setup.cfg") end
setup_file()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 248 def setup_file @setup_file ||= get_original_file("setup.py") end
setup_file_dependencies()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 133 def setup_file_dependencies @setup_file_dependencies ||= SetupFileParser. new(dependency_files: dependency_files). dependency_set end
using_poetry?()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 223 def using_poetry? return false unless pyproject return true if poetry_lock || pyproject_lock !TomlRB.parse(pyproject.content).dig("tool", "poetry").nil? rescue TomlRB::ParseError, TomlRB::ValueOverwriteError raise Dependabot::DependencyFileNotParseable, pyproject.path end
write_temporary_dependency_files()
click to toggle source
# File lib/dependabot/python/file_parser.rb, line 181 def write_temporary_dependency_files dependency_files. reject { |f| f.name == ".python-version" }. each do |file| path = file.name FileUtils.mkdir_p(Pathname.new(path).dirname) File.write(path, remove_imports(file)) end end