diff --git a/python/helpers/requirements.txt b/python/helpers/requirements.txt index 4aad2ff2eb0f..8beac1fcb85f 100644 --- a/python/helpers/requirements.txt +++ b/python/helpers/requirements.txt @@ -7,6 +7,7 @@ plette==2.1.0 poetry==1.8.3 # TODO: Replace 3p package `toml` with 3.11's new stdlib `tomllib` once we drop support for Python 3.10. toml==0.10.2 +uv==0.2.24 # Some dependencies will only install if Cython is present Cython==3.0.10 diff --git a/python/lib/dependabot/python/file_updater/pip_compile_file_updater.rb b/python/lib/dependabot/python/file_updater/pip_compile_file_updater.rb index 67f2121475dd..9c0215c701ae 100644 --- a/python/lib/dependabot/python/file_updater/pip_compile_file_updater.rb +++ b/python/lib/dependabot/python/file_updater/pip_compile_file_updater.rb @@ -96,13 +96,14 @@ def compile_new_requirement_files def compile_file(filename) # Shell out to pip-compile, generate a new set of requirements. # This is slow, as pip-compile needs to do installs. - options = pip_compile_options(filename) + + options, command = pip_compile_options(filename) options_fingerprint = pip_compile_options_fingerprint(options) - name_part = "pyenv exec pip-compile " \ + name_part = "#{command} " \ "#{options} -P " \ "#{dependency.name}" - fingerprint_name_part = "pyenv exec pip-compile " \ + fingerprint_name_part = "#{command} " \ "#{options_fingerprint} -P " \ "" @@ -456,7 +457,13 @@ def pip_compile_options(filename) options += pip_compile_options_from_compiled_file(requirements_file) end - options.join(" ") + command = "pyenv exec pip-compile" + if (requirements_file = compiled_file_for_filename(filename)) && + requirements_file.content.include?("autogenerated by uv") + command = "pyenv exec uv pip compile" + end + + [options.join(" "), command] end def pip_compile_options_from_compiled_file(requirements_file) diff --git a/python/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb b/python/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb index 1b8accb8af18..67a67c41d1ac 100644 --- a/python/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb +++ b/python/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb @@ -91,12 +91,12 @@ def fetch_latest_resolvable_version_string(requirement:) def compile_file(filename) # Shell out to pip-compile. # This is slow, as pip-compile needs to do installs. - options = pip_compile_options(filename) + options, command = pip_compile_options(filename) options_fingerprint = pip_compile_options_fingerprint(options) run_pip_compile_command( - "pyenv exec pip-compile -v #{options} -P #{dependency.name} #{filename}", - fingerprint: "pyenv exec pip-compile -v #{options_fingerprint} -P " + "#{command} -v #{options} -P #{dependency.name} #{filename}", + fingerprint: "#{command} -v #{options_fingerprint} -P " ) return true if dependency.top_level? @@ -110,8 +110,8 @@ def compile_file(filename) # update_not_possible. write_original_manifest_files run_pip_compile_command( - "pyenv exec pip-compile #{options} #{filename}", - fingerprint: "pyenv exec pip-compile #{options_fingerprint} " + "#{command} #{options} #{filename}", + fingerprint: "#{command} #{options_fingerprint} " ) true @@ -201,12 +201,12 @@ def check_original_requirements_resolvable write_temporary_dependency_files(update_requirement: false) filenames_to_compile.each do |filename| - options = pip_compile_options(filename) + options, command = pip_compile_options(filename) options_fingerprint = pip_compile_options_fingerprint(options) run_pip_compile_command( - "pyenv exec pip-compile #{options} #{filename}", - fingerprint: "pyenv exec pip-compile #{options_fingerprint} " + "#{command} #{options} #{filename}", + fingerprint: "#{command} #{options_fingerprint} " ) end @@ -251,7 +251,13 @@ def pip_compile_options(filename) options << "--output-file=#{requirements_file.name}" end - options.join(" ") + command = "pyenv exec pip-compile" + if (requirements_file = compiled_file_for_filename(filename)) && + requirements_file.content.include?("autogenerated by uv") + command = "pyenv exec uv pip compile" + end + + [options.join(" "), command] end def pip_compile_index_options diff --git a/python/spec/dependabot/python/file_updater/pip_compile_file_updater_spec.rb b/python/spec/dependabot/python/file_updater/pip_compile_file_updater_spec.rb index 894fe3db18dd..29134a702685 100644 --- a/python/spec/dependabot/python/file_updater/pip_compile_file_updater_spec.rb +++ b/python/spec/dependabot/python/file_updater/pip_compile_file_updater_spec.rb @@ -112,6 +112,17 @@ end end + context "with a uv header" do + let(:manifest_fixture_name) { "unpinned_uv.in" } + let(:generated_fixture_name) { "pip_compile_uv_header.txt" } + + it "upgrades attrs to latest" do + expect(updated_files.count).to eq(1) + expect(updated_files.first.content).to include("attrs==18.1.0") + expect(updated_files.first.content).to include("This file was autogenerated by uv") + end + end + context "with a no-binary flag" do let(:manifest_fixture_name) { "no_binary.in" } let(:generated_fixture_name) { "pip_compile_no_binary.txt" } diff --git a/python/spec/fixtures/pip_compile_files/unpinned_uv.in b/python/spec/fixtures/pip_compile_files/unpinned_uv.in new file mode 100644 index 000000000000..9e636b9fc7b0 --- /dev/null +++ b/python/spec/fixtures/pip_compile_files/unpinned_uv.in @@ -0,0 +1,5 @@ +flaky +pytest +pytest-xdist +mock +Attrs diff --git a/python/spec/fixtures/requirements/pip_compile_uv_header.txt b/python/spec/fixtures/requirements/pip_compile_uv_header.txt new file mode 100644 index 000000000000..e78afee14e4d --- /dev/null +++ b/python/spec/fixtures/requirements/pip_compile_uv_header.txt @@ -0,0 +1,22 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --output-file pip_compile_uv_header.txt unpinned_uv.in -P attrs==18.1.0 +attrs==18.1.0 + # via -r unpinned_uv.in +execnet==2.1.1 + # via pytest-xdist +flaky==3.8.1 + # via -r unpinned_uv.in +iniconfig==2.0.0 + # via pytest +mock==5.1.0 + # via -r unpinned_uv.in +packaging==24.1 + # via pytest +pluggy==1.5.0 + # via pytest +pytest==8.2.2 + # via + # -r unpinned_uv.in + # pytest-xdist +pytest-xdist==3.6.1 + # via -r unpinned_uv.in