class Dependabot::Python::FileUpdater::PoetryFileUpdater
Attributes
credentials[R]
dependencies[R]
dependency_files[R]
Public Class Methods
new(dependencies:, dependency_files:, credentials:)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 23 def initialize(dependencies:, dependency_files:, credentials:) @dependencies = dependencies @dependency_files = dependency_files @credentials = credentials end
Public Instance Methods
updated_dependency_files()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 29 def updated_dependency_files return @updated_dependency_files if @update_already_attempted @update_already_attempted = true @updated_dependency_files ||= fetch_updated_dependency_files end
Private Instance Methods
add_private_sources(pyproject_content)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 153 def add_private_sources(pyproject_content) PyprojectPreparer. new(pyproject_content: pyproject_content). replace_sources(credentials) end
create_declaration_at_new_version!(poetry_object, dep)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 148 def create_declaration_at_new_version!(poetry_object, dep) poetry_object[subdep_type] ||= {} poetry_object[subdep_type][dependency.name] = dep.version end
declaration_regex(dep)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 271 def declaration_regex(dep) escaped_name = Regexp.escape(dep.name).gsub("\\-", "[-_.]") /(?:^\s*|["'])#{escaped_name}["']?\s*=.*$/i end
dependency()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 38 def dependency # For now, we'll only ever be updating a single dependency dependencies.first end
fetch_updated_dependency_files()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 43 def fetch_updated_dependency_files updated_files = [] if file_changed?(pyproject) updated_files << updated_file( file: pyproject, content: updated_pyproject_content ) end raise "Expected lockfile to change!" if lockfile && lockfile.content == updated_lockfile_content if lockfile updated_files << updated_file(file: lockfile, content: updated_lockfile_content) end updated_files end
file_changed?(file)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 276 def file_changed?(file) dependencies.any? { |dep| requirement_changed?(file, dep) } end
freeze_dependencies_being_updated(pyproject_content)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 119 def freeze_dependencies_being_updated(pyproject_content) pyproject_object = TomlRB.parse(pyproject_content) poetry_object = pyproject_object.fetch("tool").fetch("poetry") dependencies.each do |dep| if dep.requirements.find { |r| r[:file] == pyproject.name } lock_declaration_to_new_version!(poetry_object, dep) else create_declaration_at_new_version!(poetry_object, dep) end end TomlRB.dump(pyproject_object) end
freeze_other_dependencies(pyproject_content)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 113 def freeze_other_dependencies(pyproject_content) PyprojectPreparer. new(pyproject_content: pyproject_content, lockfile: lockfile). freeze_top_level_dependencies_except(dependencies) end
lock_declaration_to_new_version!(poetry_object, dep)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 134 def lock_declaration_to_new_version!(poetry_object, dep) Dependabot::Python::FileParser::PoetryFilesParser::POETRY_DEPENDENCY_TYPES.each do |type| names = poetry_object[type]&.keys || [] pkg_name = names.find { |nm| normalise(nm) == dep.name } next unless pkg_name if poetry_object[type][pkg_name].is_a?(Hash) poetry_object[type][pkg_name]["version"] = dep.version else poetry_object[type][pkg_name] = dep.version end end end
lockfile()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 302 def lockfile @lockfile ||= pyproject_lock || poetry_lock end
normalise(name)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 293 def normalise(name) NameNormaliser.normalise(name) end
poetry_lock()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 314 def poetry_lock dependency_files.find { |f| f.name == "poetry.lock" } end
poetry_update_command()
click to toggle source
Using `–lock` avoids doing an install. Using `–no-interaction` avoids asking for passwords.
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 196 def poetry_update_command "pyenv exec poetry update #{dependency.name} --lock --no-interaction" end
pre_installed_python?(version)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 253 def pre_installed_python?(version) PythonVersions::PRE_INSTALLED_PYTHON_VERSIONS.include?(version) end
prepared_pyproject()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 101 def prepared_pyproject @prepared_pyproject ||= begin content = updated_pyproject_content content = sanitize(content) content = freeze_other_dependencies(content) content = freeze_dependencies_being_updated(content) content = add_private_sources(content) content end end
pyproject()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 297 def pyproject @pyproject ||= dependency_files.find { |f| f.name == "pyproject.toml" } end
pyproject_hash_for(pyproject_content)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 257 def pyproject_hash_for(pyproject_content) SharedHelpers.in_a_temporary_directory do |dir| SharedHelpers.with_git_configured(credentials: credentials) do write_temporary_dependency_files(pyproject_content) SharedHelpers.run_helper_subprocess( command: "pyenv exec python #{python_helper_path}", function: "get_pyproject_hash", args: [dir] ) end end end
pyproject_lock()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 310 def pyproject_lock dependency_files.find { |f| f.name == "pyproject.lock" } end
python_helper_path()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 306 def python_helper_path NativeHelpers.python_helper_path end
python_requirement_parser()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 246 def python_requirement_parser @python_requirement_parser ||= FileParser::PythonRequirementParser.new( dependency_files: dependency_files ) end
python_version()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 234 def python_version requirements = python_requirement_parser.user_specified_requirements requirements = requirements. map { |r| Python::Requirement.requirements_array(r) } PythonVersions::SUPPORTED_VERSIONS_TO_ITERATE.find do |version| requirements.all? do |reqs| reqs.any? { |r| r.satisfied_by?(Python::Version.new(version)) } end end end
requirement_changed?(file, dependency)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 280 def requirement_changed?(file, dependency) changed_requirements = dependency.requirements - dependency.previous_requirements changed_requirements.any? { |f| f[:file] == file.name } end
run_poetry_command(command)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 200 def run_poetry_command(command) start = Time.now command = SharedHelpers.escape_command(command) stdout, process = Open3.capture2e(command) time_taken = Time.now - start # Raise an error with the output from the shell session if Pipenv # returns a non-zero status return if process.success? raise SharedHelpers::HelperSubprocessFailed.new( message: stdout, error_context: { command: command, time_taken: time_taken, process_exit_value: process.to_s } ) end
sanitize(pyproject_content)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 168 def sanitize(pyproject_content) PyprojectPreparer. new(pyproject_content: pyproject_content). sanitize end
subdep_type()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 159 def subdep_type category = TomlRB.parse(lockfile.content).fetch("package", []). find { |dets| normalise(dets.fetch("name")) == dependency.name }. fetch("category") category == "dev" ? "dev-dependencies" : "dependencies" end
updated_file(file:, content:)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 287 def updated_file(file:, content:) updated_file = file.dup updated_file.content = content updated_file end
updated_lockfile_content()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 88 def updated_lockfile_content @updated_lockfile_content ||= begin new_lockfile = updated_lockfile_content_for(prepared_pyproject) tmp_hash = TomlRB.parse(new_lockfile)["metadata"]["content-hash"] correct_hash = pyproject_hash_for(updated_pyproject_content) new_lockfile.gsub(tmp_hash, correct_hash) end end
updated_lockfile_content_for(pyproject_content)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 174 def updated_lockfile_content_for(pyproject_content) SharedHelpers.in_a_temporary_directory do SharedHelpers.with_git_configured(credentials: credentials) do write_temporary_dependency_files(pyproject_content) if python_version && !pre_installed_python?(python_version) run_poetry_command("pyenv install -s #{python_version}") run_poetry_command("pyenv exec pip install -r"\ "#{NativeHelpers.python_requirements_path}") end run_poetry_command(poetry_update_command) return File.read("poetry.lock") if File.exist?("poetry.lock") File.read("pyproject.lock") end end end
updated_pyproject_content()
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 64 def updated_pyproject_content dependencies. select { |dep| requirement_changed?(pyproject, dep) }. reduce(pyproject.content.dup) do |content, dep| updated_requirement = dep.requirements.find { |r| r[:file] == pyproject.name }. fetch(:requirement) old_req = dep.previous_requirements. find { |r| r[:file] == pyproject.name }. fetch(:requirement) updated_content = content.gsub(declaration_regex(dep)) do |line| line.gsub(old_req, updated_requirement) end raise "Content did not change!" if content == updated_content updated_content end end
write_temporary_dependency_files(pyproject_content)
click to toggle source
# File lib/dependabot/python/file_updater/poetry_file_updater.rb, line 220 def write_temporary_dependency_files(pyproject_content) dependency_files.each do |file| path = file.name FileUtils.mkdir_p(Pathname.new(path).dirname) File.write(path, file.content) end # Overwrite the .python-version with updated content File.write(".python-version", python_version) if python_version # Overwrite the pyproject with updated content File.write("pyproject.toml", pyproject_content) end