From 3a82e1d101ff845c128a4f6a4236b8a6447ccc45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Wed, 19 Jun 2024 08:12:46 -0300 Subject: [PATCH 01/17] Support python uv as pip-compile compatible replacement --- python/helpers/requirements.txt | 1 + .../file_updater/pip_compile_file_updater.rb | 15 ++++++++---- .../pip_compile_version_resolver.rb | 24 ++++++++++++------- .../pip_compile_file_updater_spec.rb | 11 +++++++++ .../fixtures/pip_compile_files/unpinned_uv.in | 5 ++++ .../requirements/pip_compile_uv_header.txt | 22 +++++++++++++++++ 6 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 python/spec/fixtures/pip_compile_files/unpinned_uv.in create mode 100644 python/spec/fixtures/requirements/pip_compile_uv_header.txt diff --git a/python/helpers/requirements.txt b/python/helpers/requirements.txt index 4aad2ff2eb..8beac1fcb8 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 67f2121475..9c0215c701 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 1b8accb8af..67a67c41d1 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 894fe3db18..29134a7026 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 0000000000..9e636b9fc7 --- /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 0000000000..e78afee14e --- /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 From 5a9eb3fa938f13a1de3a9e2094aca860040f3510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Tue, 16 Jul 2024 09:02:09 -0300 Subject: [PATCH 02/17] Update requirements.txt --- python/helpers/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/helpers/requirements.txt b/python/helpers/requirements.txt index 8beac1fcb8..816dbb3f28 100644 --- a/python/helpers/requirements.txt +++ b/python/helpers/requirements.txt @@ -7,7 +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 +uv==0.2.25 # Some dependencies will only install if Cython is present Cython==3.0.10 From 4d67cb2d368a1334c6eee404aaf045f8f9451d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Wed, 17 Jul 2024 19:42:32 -0300 Subject: [PATCH 03/17] Update python/helpers/requirements.txt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Edgar Ramírez Mondragón <16805946+edgarrmondragon@users.noreply.github.com> --- python/helpers/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/helpers/requirements.txt b/python/helpers/requirements.txt index 816dbb3f28..1078f056ab 100644 --- a/python/helpers/requirements.txt +++ b/python/helpers/requirements.txt @@ -7,7 +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.25 +uv==0.2.26 # Some dependencies will only install if Cython is present Cython==3.0.10 From c4fba47c91070cd97777c686a11579ca6a8fe0fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Wed, 17 Jul 2024 22:59:19 -0300 Subject: [PATCH 04/17] Update python/spec/fixtures/requirements/pip_compile_uv_header.txt Co-authored-by: skshetry <18718008+skshetry@users.noreply.github.com> --- python/spec/fixtures/requirements/pip_compile_uv_header.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/spec/fixtures/requirements/pip_compile_uv_header.txt b/python/spec/fixtures/requirements/pip_compile_uv_header.txt index e78afee14e..c5850a242d 100644 --- a/python/spec/fixtures/requirements/pip_compile_uv_header.txt +++ b/python/spec/fixtures/requirements/pip_compile_uv_header.txt @@ -1,5 +1,5 @@ # 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 +# uv pip compile --output-file pip_compile_uv_header.txt unpinned_uv.in attrs==18.1.0 # via -r unpinned_uv.in execnet==2.1.1 From cd7beaf4057d377ea236768777c16df9ba29b688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Ferr=C3=A0s?= Date: Sun, 18 Aug 2024 23:17:15 +0200 Subject: [PATCH 05/17] Bump uv. New uv_pip_compile_options_from_compiled_file method and bigger uv pip compile test coverage --- python/helpers/requirements.txt | 2 +- .../file_updater/pip_compile_file_updater.rb | 38 +- .../pip_compile_file_updater_spec.rb | 11 - .../uv_pip_compile_file_updater_spec.rb | 568 ++++++++++++++++++ .../pip_compile_files/no_binary_uv.in | 2 + .../fixtures/pip_compile_files/unpinned_uv.in | 5 - .../fixtures/pip_compile_files/vcs_url.in | 2 + ...mpile_uv_header.txt => uv_pip_compile.txt} | 2 +- .../requirements/uv_pip_compile_bounded.txt | 15 + .../uv_pip_compile_extra_hashes.txt | 43 ++ .../requirements/uv_pip_compile_hashes.txt | 51 ++ .../uv_pip_compile_imports_setup.txt | 19 + .../requirements/uv_pip_compile_no_binary.txt | 5 + .../uv_pip_compile_no_strip_extras.txt | 18 + .../requirements/uv_pip_compile_safe.txt | 6 + .../uv_pip_compile_strip_extras.txt | 18 + .../uv_pip_compile_unmet_marker.txt | 15 + .../requirements/uv_pip_compile_unpinned.txt | 15 + .../uv_pip_compile_unpinned_renamed.txt | 15 + .../requirements/uv_pip_compile_unsafe.txt | 10 + .../requirements/uv_pip_compile_vcs_url.txt | 6 + 21 files changed, 841 insertions(+), 25 deletions(-) create mode 100644 python/spec/dependabot/python/file_updater/uv_pip_compile_file_updater_spec.rb create mode 100644 python/spec/fixtures/pip_compile_files/no_binary_uv.in delete mode 100644 python/spec/fixtures/pip_compile_files/unpinned_uv.in create mode 100644 python/spec/fixtures/pip_compile_files/vcs_url.in rename python/spec/fixtures/requirements/{pip_compile_uv_header.txt => uv_pip_compile.txt} (85%) create mode 100644 python/spec/fixtures/requirements/uv_pip_compile_bounded.txt create mode 100644 python/spec/fixtures/requirements/uv_pip_compile_extra_hashes.txt create mode 100644 python/spec/fixtures/requirements/uv_pip_compile_hashes.txt create mode 100644 python/spec/fixtures/requirements/uv_pip_compile_imports_setup.txt create mode 100644 python/spec/fixtures/requirements/uv_pip_compile_no_binary.txt create mode 100644 python/spec/fixtures/requirements/uv_pip_compile_no_strip_extras.txt create mode 100644 python/spec/fixtures/requirements/uv_pip_compile_safe.txt create mode 100644 python/spec/fixtures/requirements/uv_pip_compile_strip_extras.txt create mode 100644 python/spec/fixtures/requirements/uv_pip_compile_unmet_marker.txt create mode 100644 python/spec/fixtures/requirements/uv_pip_compile_unpinned.txt create mode 100644 python/spec/fixtures/requirements/uv_pip_compile_unpinned_renamed.txt create mode 100644 python/spec/fixtures/requirements/uv_pip_compile_unsafe.txt create mode 100644 python/spec/fixtures/requirements/uv_pip_compile_vcs_url.txt diff --git a/python/helpers/requirements.txt b/python/helpers/requirements.txt index 1078f056ab..79f7b56da8 100644 --- a/python/helpers/requirements.txt +++ b/python/helpers/requirements.txt @@ -7,7 +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.26 +uv==0.2.37 # 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 9c0215c701..f1c4b32701 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 @@ -454,13 +454,13 @@ def pip_compile_options(filename) options += pip_compile_index_options if (requirements_file = compiled_file_for_filename(filename)) - options += pip_compile_options_from_compiled_file(requirements_file) - end - - 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" + if requirements_file.content.include?("uv pip compile") + options += uv_pip_compile_options_from_compiled_file(requirements_file) + command = "pyenv exec uv pip compile" + else + options += pip_compile_options_from_compiled_file(requirements_file) + command = "pyenv exec pip-compile" + end end [options.join(" "), command] @@ -490,6 +490,30 @@ def pip_compile_options_from_compiled_file(requirements_file) options end + def uv_pip_compile_options_from_compiled_file(requirements_file) + options = ["--output-file=#{requirements_file.name}"] + + options << "--no-emit-index-url" unless requirements_file.content.include?("index-url http") + + options << "--generate-hashes" if requirements_file.content.include?("--hash=sha") + + options << "--no-annotate" unless requirements_file.content.include?("# via ") + + options << "--no-header" + + options << "--pre" if requirements_file.content.include?("--pre") + + options << "--no-strip-extras" if requirements_file.content.include?("--no-strip-extras") + + options << "--emit-build-options" if (requirements_file.content.include?("--no-binary") || requirements_file.content.include?("--only-binary")) + + if (resolver = RESOLVER_REGEX.match(requirements_file.content)) + options << "--resolver=#{resolver}" + end + + options + end + def pip_compile_index_options credentials .select { |cred| cred["type"] == "python_index" } 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 29134a7026..894fe3db18 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,17 +112,6 @@ 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/dependabot/python/file_updater/uv_pip_compile_file_updater_spec.rb b/python/spec/dependabot/python/file_updater/uv_pip_compile_file_updater_spec.rb new file mode 100644 index 0000000000..4ad1f17dce --- /dev/null +++ b/python/spec/dependabot/python/file_updater/uv_pip_compile_file_updater_spec.rb @@ -0,0 +1,568 @@ +# typed: false +# frozen_string_literal: true + +require "spec_helper" +require "dependabot/dependency" +require "dependabot/dependency_file" +require "dependabot/python/file_updater/pip_compile_file_updater" +require "dependabot/shared_helpers" + +RSpec.describe Dependabot::Python::FileUpdater::PipCompileFileUpdater do + let(:updater) do + described_class.new( + dependency_files: dependency_files, + dependencies: [dependency], + credentials: credentials + ) + end + let(:dependency_files) { [manifest_file, generated_file] } + let(:manifest_file) do + Dependabot::DependencyFile.new( + name: "requirements/test.in", + content: fixture("pip_compile_files", manifest_fixture_name) + ) + end + let(:generated_file) do + Dependabot::DependencyFile.new( + name: "requirements/test.txt", + content: fixture("requirements", generated_fixture_name) + ) + end + let(:manifest_fixture_name) { "unpinned.in" } + let(:generated_fixture_name) { "uv_pip_compile_unpinned.txt" } + let(:dependency) do + Dependabot::Dependency.new( + name: dependency_name, + version: dependency_version, + previous_version: dependency_previous_version, + requirements: dependency_requirements, + previous_requirements: dependency_previous_requirements, + package_manager: "pip" + ) + end + let(:dependency_name) { "attrs" } + let(:dependency_version) { "18.1.0" } + let(:dependency_previous_version) { "17.3.0" } + let(:dependency_requirements) do + [{ + file: "requirements/test.in", + requirement: nil, + groups: [], + source: nil + }] + end + let(:dependency_previous_requirements) do + [{ + file: "requirements/test.in", + requirement: nil, + groups: [], + source: nil + }] + end + let(:credentials) do + [Dependabot::Credential.new({ + "type" => "git_source", + "host" => "github.com", + "username" => "x-access-token", + "password" => "token" + })] + end + let(:tmp_path) { Dependabot::Utils::BUMP_TMP_DIR_PATH } + + before { FileUtils.mkdir_p(tmp_path) } + + describe "#updated_dependency_files" do + subject(:updated_files) { updater.updated_dependency_files } + + it "updates the requirements.txt" 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("pbr==4.0.2\n # via mock") + expect(updated_files.first.content).to include("# This file was autogenerated by uv") + expect(updated_files.first.content).not_to include("--hash=sha") + end + + context "with a mismatch in filename" do + let(:generated_fixture_name) { "uv_pip_compile_unpinned_renamed.txt" } + let(:generated_file) do + Dependabot::DependencyFile.new( + name: "requirements/test-funky.txt", + content: fixture("requirements", generated_fixture_name) + ) + end + + it "updates the requirements.txt" 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("pbr==4.0.2\n # via mock") + expect(updated_files.first.content).to include("# This file was autogenerated by uv") + expect(updated_files.first.content).not_to include("--hash=sha") + end + end + + context "with uv header" do + let(:manifest_fixture_name) { "unpinned.in" } + let(:generated_fixture_name) { "uv_pip_compile.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") + expect(updated_files.first.content).to include("uv pip compile") + end + end + + context "with a no-binary flag" do + let(:manifest_fixture_name) { "no_binary_uv.in" } + let(:generated_fixture_name) { "uv_pip_compile_no_binary.txt" } + let(:dependency_name) { "psycopg2" } + let(:dependency_version) { "2.7.6" } + let(:dependency_previous_version) { "2.7.4" } + + it "updates the requirements.txt correctly" do + expect(updated_files.count).to eq(1) + expect(updated_files.first.content).to include("psycopg2==2.7.6") + expect(updated_files.first.content).to include("--no-binary psycopg2") + expect(updated_files.first.content) + .not_to include("--no-binary psycopg2==") + end + end + + context "with hashes" do + let(:generated_fixture_name) { "uv_pip_compile_hashes.txt" } + + it "updates the requirements.txt, keeping the hashes" 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("4b90b09eeeb9b88c35bc64") + expect(updated_files.first.content).to include("# This file was autogenerated by uv") + end + + context "when needing an augmented hashin" do + let(:manifest_fixture_name) { "extra_hashes.in" } + let(:generated_fixture_name) { "uv_pip_compile_extra_hashes.txt" } + let(:dependency_name) { "pyasn1-modules" } + let(:dependency_version) { "0.1.5" } + let(:dependency_previous_version) { "0.1.4" } + + it "updates the requirements.txt, keeping all the hashes" do + expect(updated_files.count).to eq(1) + expect(updated_files.first.content) + .to include("# This file was autogenerated by uv") + expect(updated_files.first.content) + .to include("pyasn1-modules==0.1.5 \\\n --hash=sha256:01") + expect(updated_files.first.content) + .to include("--hash=sha256:b437be576bdf440fc0e930") + expect(updated_files.first.content) + .to include("pyasn1==0.3.7 \\\n --hash=sha256:16") + expect(updated_files.first.content) + .to include("--hash=sha256:bb6f5d5507621e0298794b") + expect(updated_files.first.content) + .to include("# via pyasn1-modules") + expect(updated_files.first.content).not_to include("WARNING") + end + end + end + + context "with another dependency with an unmet marker" do + let(:manifest_fixture_name) { "unmet_marker.in" } + let(:generated_fixture_name) { "uv_pip_compile_unmet_marker.txt" } + + it "updates the requirements.txt, keeping the unmet dep out of it" do + expect(updated_files.count).to eq(1) + expect(updated_files.first.content).to include("attrs==18.1.0") + expect(updated_files.first.content).not_to include("flaky") + end + end + + context "with an unsafe dependency" do + let(:manifest_fixture_name) { "unsafe.in" } + let(:dependency_name) { "flake8" } + let(:dependency_version) { "3.6.0" } + let(:dependency_previous_version) { "3.5.0" } + + context "when not including in the lockfile" do + let(:generated_fixture_name) { "uv_pip_compile_safe.txt" } + + it "does not include the unsafe dependency" do + expect(updated_files.count).to eq(1) + expect(updated_files.first.content).to include("flake8==3.6.0") + # Distinct from pip-compile, which will not include setuptools as it is considered unsafe dependency + # UV's default will include it by default. Future major pip-tools version will do the same. + expect(updated_files.first.content).to include("setuptools") + expect(updated_files.first.content).to end_with("via flake8\n") + end + end + + context "when including in the lockfile" do + let(:generated_fixture_name) { "uv_pip_compile_unsafe.txt" } + + it "includes the unsafe dependency" do + expect(updated_files.count).to eq(1) + expect(updated_files.first.content).to include("flake8==3.6.0") + expect(updated_files.first.content).to include("setuptools") + end + end + end + + context "with an import of the setup.py" do + let(:dependency_files) do + [manifest_file, generated_file, setup_file, pyproject] + end + let(:setup_file) do + Dependabot::DependencyFile.new( + name: "setup.py", + content: fixture("setup_files", setup_fixture_name) + ) + end + let(:pyproject) do + Dependabot::DependencyFile.new( + name: "pyproject.toml", + content: fixture("pyproject_files", "black_configuration.toml") + ) + end + let(:manifest_fixture_name) { "imports_setup.in" } + let(:generated_fixture_name) { "uv_pip_compile_imports_setup.txt" } + let(:setup_fixture_name) { "small.py" } + + it "updates the requirements.txt", :slow 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("-e file:///Users/greysteil/code/python-test") + expect(updated_files.first.content).not_to include("tmp/dependabot") + expect(updated_files.first.content) + .to include("pbr==4.0.2\n # via mock") + expect(updated_files.first.content).to include("# This file was autogenerated by uv") + expect(updated_files.first.content).not_to include("--hash=sha") + end + + context "when needing sanitization", :slow do + let(:setup_fixture_name) { "small_needs_sanitizing.py" } + + it "updates the requirements.txt" do + expect(updated_files.count).to eq(1) + expect(updated_files.first.content).to include("attrs==18.1.0") + end + end + end + + context "with vcs url dependencies" do + let(:manifest_fixture_name) { "vcs_url.in" } + let(:generated_fixture_name) { "uv_pip_compile_vcs_url.txt" } + + it "updates the requirements.txt" 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("mock @ git+https://github.com/testing-cabal/mock.git@286792b2") + end + end + + context "with a subdependency" do + let(:dependency_name) { "pbr" } + let(:dependency_version) { "4.2.0" } + let(:dependency_previous_version) { "4.0.2" } + let(:dependency_requirements) { [] } + let(:dependency_previous_requirements) { [] } + + it "updates the requirements.txt" do + expect(updated_files.count).to eq(1) + expect(updated_files.first.content) + .to include("pbr==4.2.0\n # via mock") + end + end + + context "when targeting a non-latest version" do + let(:dependency_version) { "17.4.0" } + + it "updates the requirements.txt" do + expect(updated_files.count).to eq(1) + expect(updated_files.first.content).to include("attrs==17.4.0") + expect(updated_files.first.content) + .to include("pbr==4.0.2\n # via mock") + expect(updated_files.first.content).to include("# This file was autogenerated by uv") + expect(updated_files.first.content).not_to include("--hash=sha") + end + end + + context "when the requirement.in file needs to be updated" do + let(:manifest_fixture_name) { "bounded.in" } + let(:generated_fixture_name) { "uv_pip_compile_bounded.txt" } + + let(:dependency_requirements) do + [{ + file: "requirements/test.in", + requirement: "<=18.1.0", + groups: [], + source: nil + }] + end + let(:dependency_previous_requirements) do + [{ + file: "requirements/test.in", + requirement: "<=17.4.0", + groups: [], + source: nil + }] + end + + it "updates the requirements.txt and the requirements.in" do + expect(updated_files.count).to eq(2) + expect(updated_files.first.content).to include("Attrs<=18.1.0") + expect(updated_files.last.content).to include("attrs==18.1.0") + expect(updated_files.last.content).not_to include("# via mock") + end + + context "with an additional requirements.txt" do + let(:dependency_files) { [manifest_file, generated_file, other_txt] } + let(:other_txt) do + Dependabot::DependencyFile.new( + name: "requirements.txt", + content: + fixture("requirements", "uv_pip_compile_unpinned.txt") + ) + end + + let(:dependency_requirements) do + [{ + file: "requirements/test.in", + requirement: "<=18.1.0", + groups: [], + source: nil + }, { + file: "requirements.txt", + requirement: "==18.1.0", + groups: [], + source: nil + }] + end + let(:dependency_previous_requirements) do + [{ + file: "requirements/test.in", + requirement: "<=17.4.0", + groups: [], + source: nil + }, { + file: "requirements.txt", + requirement: "==17.3.0", + groups: [], + source: nil + }] + end + + it "updates the other requirements.txt, too" do + expect(updated_files.count).to eq(3) + expect(updated_files.first.content).to include("Attrs<=18.1.0") + expect(updated_files[1].content).to include("attrs==18.1.0") + expect(updated_files.last.content).to include("attrs==18.1.0") + end + end + + context "with multiple requirement.in files" do + let(:dependency_files) do + [ + manifest_file, manifest_file2, manifest_file3, manifest_file4, + generated_file, generated_file2, generated_file3, generated_file4 + ] + end + + let(:manifest_file2) do + Dependabot::DependencyFile.new( + name: "requirements/dev.in", + content: + fixture("pip_compile_files", manifest_fixture_name) + ) + end + let(:generated_file2) do + Dependabot::DependencyFile.new( + name: "requirements/dev.txt", + content: fixture("requirements", generated_fixture_name) + ) + end + + let(:manifest_file3) do + Dependabot::DependencyFile.new( + name: "requirements/mirror2.in", + content: + fixture("pip_compile_files", "imports_mirror.in") + ) + end + let(:generated_file3) do + Dependabot::DependencyFile.new( + name: "requirements/mirror2.txt", + content: fixture("requirements", generated_fixture_name) + ) + end + + let(:manifest_file4) do + Dependabot::DependencyFile.new( + name: "requirements/mirror.in", + content: + fixture("pip_compile_files", "imports_dev.in") + ) + end + let(:generated_file4) do + Dependabot::DependencyFile.new( + name: "requirements/mirror.txt", + content: fixture("requirements", generated_fixture_name) + ) + end + + let(:dependency_requirements) do + [{ + file: "requirements/test.in", + requirement: "<=18.1.0", + groups: [], + source: nil + }, { + file: "requirements/dev.in", + requirement: "<=18.1.0", + groups: [], + source: nil + }] + end + let(:dependency_previous_requirements) do + [{ + file: "requirements/test.in", + requirement: "<=17.4.0", + groups: [], + source: nil + }, { + file: "requirements/dev.in", + requirement: "<=17.4.0", + groups: [], + source: nil + }] + end + + it "updates the other manifest file, too" do + expect(updated_files.count).to eq(6) + expect(updated_files[0].name).to eq("requirements/test.in") + expect(updated_files[1].name).to eq("requirements/dev.in") + expect(updated_files[2].name).to eq("requirements/test.txt") + expect(updated_files[3].name).to eq("requirements/dev.txt") + expect(updated_files[4].name).to eq("requirements/mirror2.txt") + expect(updated_files[5].name).to eq("requirements/mirror.txt") + expect(updated_files[0].content).to include("Attrs<=18.1.0") + expect(updated_files[1].content).to include("Attrs<=18.1.0") + expect(updated_files[2].content).to include("attrs==18.1.0") + expect(updated_files[3].content).to include("attrs==18.1.0") + expect(updated_files[4].content).to include("attrs==18.1.0") + expect(updated_files[5].content).to include("attrs==18.1.0") + end + end + end + + context "with stripped extras" do + let(:manifest_fixture_name) { "strip_extras.in" } + let(:generated_fixture_name) { "uv_pip_compile_strip_extras.txt" } + let(:dependency_name) { "cachecontrol" } + let(:dependency_version) { "0.12.10" } + let(:dependency_previous_version) { "0.12.9" } + + it "doesn't add an extras annotation on cachecontrol" do + expect(updated_files.count).to eq(1) + expect(updated_files.first.content).to include("cachecontrol==0.12.10") + expect(updated_files.first.content) + .not_to include("cachecontrol[filecache]==") + end + end + + context "with no stripped extras" do + let(:manifest_fixture_name) { "strip_extras.in" } + let(:generated_fixture_name) { "uv_pip_compile_no_strip_extras.txt" } + let(:dependency_name) { "cachecontrol" } + let(:dependency_version) { "0.12.10" } + let(:dependency_previous_version) { "0.12.9" } + + it "adds an extras annotation on cachecontrol" do + expect(updated_files.count).to eq(1) + expect(updated_files.first.content).to include("cachecontrol[filecache]==0.12.10") + end + end + end + + describe "#package_hashes_for" do + let(:name) { "package_name" } + let(:version) { "1.0.0" } + let(:algorithm) { "sha256" } + + context "when index_urls is not set" do + let(:updater) do + described_class.new( + dependencies: [], + dependency_files: [], + credentials: [] + ) + end + + before do + allow(Dependabot::SharedHelpers).to receive(:run_helper_subprocess).and_return([{ "hash" => "123abc" }]) + end + + it "returns hash" do + result = updater.send(:package_hashes_for, name: name, version: version, algorithm: algorithm) + expect(result).to eq(["--hash=sha256:123abc"]) + end + end + + context "when multiple index_urls are set" do + let(:updater) do + described_class.new( + dependencies: [], + dependency_files: [], + credentials: [], + index_urls: [nil, "http://example.com"] + ) + end + + before do + allow(Dependabot::SharedHelpers).to receive(:run_helper_subprocess) + .and_return([{ "hash" => "123abc" }], [{ "hash" => "312cba" }]) + end + + it "returns returns two hashes" do + result = updater.send(:package_hashes_for, name: name, version: version, algorithm: algorithm) + expect(result).to eq(%w(--hash=sha256:123abc --hash=sha256:312cba)) + end + end + + context "when multiple index_urls are set but package does not exist in PyPI" do + let(:updater) do + described_class.new( + dependencies: [], + dependency_files: [], + credentials: [], + index_urls: [nil, "http://example.com"] + ) + end + + before do + allow(Dependabot::SharedHelpers).to receive(:run_helper_subprocess).with({ + args: %w(package_name 1.0.0 sha256), + command: "pyenv exec python3 /opt/python/run.py", + function: "get_dependency_hash" + }).and_raise( + Dependabot::SharedHelpers::HelperSubprocessFailed.new( + message: "Error message", error_context: {}, error_class: "PackageNotFoundError" + ) + ) + + allow(Dependabot::SharedHelpers).to receive(:run_helper_subprocess) + .with({ + args: %w(package_name 1.0.0 sha256 http://example.com), + command: "pyenv exec python3 /opt/python/run.py", + function: "get_dependency_hash" + }).and_return([{ "hash" => "123abc" }]) + end + + it "returns returns two hashes" do + result = updater.send(:package_hashes_for, name: name, version: version, algorithm: algorithm) + expect(result).to eq(["--hash=sha256:123abc"]) + end + end + end +end diff --git a/python/spec/fixtures/pip_compile_files/no_binary_uv.in b/python/spec/fixtures/pip_compile_files/no_binary_uv.in new file mode 100644 index 0000000000..08018c1cdc --- /dev/null +++ b/python/spec/fixtures/pip_compile_files/no_binary_uv.in @@ -0,0 +1,2 @@ +psycopg2 +--no-binary psycopg2 diff --git a/python/spec/fixtures/pip_compile_files/unpinned_uv.in b/python/spec/fixtures/pip_compile_files/unpinned_uv.in deleted file mode 100644 index 9e636b9fc7..0000000000 --- a/python/spec/fixtures/pip_compile_files/unpinned_uv.in +++ /dev/null @@ -1,5 +0,0 @@ -flaky -pytest -pytest-xdist -mock -Attrs diff --git a/python/spec/fixtures/pip_compile_files/vcs_url.in b/python/spec/fixtures/pip_compile_files/vcs_url.in new file mode 100644 index 0000000000..cf1ae99c61 --- /dev/null +++ b/python/spec/fixtures/pip_compile_files/vcs_url.in @@ -0,0 +1,2 @@ +git+https://github.com/testing-cabal/mock.git@2.0.0 +Attrs diff --git a/python/spec/fixtures/requirements/pip_compile_uv_header.txt b/python/spec/fixtures/requirements/uv_pip_compile.txt similarity index 85% rename from python/spec/fixtures/requirements/pip_compile_uv_header.txt rename to python/spec/fixtures/requirements/uv_pip_compile.txt index c5850a242d..e47dcb2a67 100644 --- a/python/spec/fixtures/requirements/pip_compile_uv_header.txt +++ b/python/spec/fixtures/requirements/uv_pip_compile.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv pip compile --output-file pip_compile_uv_header.txt unpinned_uv.in +# uv pip compile --output-file uv_pip_compile.txt unpinned_uv.in attrs==18.1.0 # via -r unpinned_uv.in execnet==2.1.1 diff --git a/python/spec/fixtures/requirements/uv_pip_compile_bounded.txt b/python/spec/fixtures/requirements/uv_pip_compile_bounded.txt new file mode 100644 index 0000000000..a35bb7f8e3 --- /dev/null +++ b/python/spec/fixtures/requirements/uv_pip_compile_bounded.txt @@ -0,0 +1,15 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --no-annotate --output-file requirements/text.txt requirements/test.in +apipkg==1.4 +attrs==17.3.0 +execnet==1.5.0 +flaky==3.4.0 +mock==2.0.0 +more-itertools==4.1.0 +pbr==4.0.3 +pluggy==0.6.0 +py==1.5.3 +pytest-forked==0.2 +pytest-xdist==1.22.2 +pytest==3.5.1 +six==1.11.0 diff --git a/python/spec/fixtures/requirements/uv_pip_compile_extra_hashes.txt b/python/spec/fixtures/requirements/uv_pip_compile_extra_hashes.txt new file mode 100644 index 0000000000..e0b36be5e2 --- /dev/null +++ b/python/spec/fixtures/requirements/uv_pip_compile_extra_hashes.txt @@ -0,0 +1,43 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --generate-hashes --output-file req.txt req.in +atomicwrites==1.2.1 \ + --hash=sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0 \ + --hash=sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee \ + # via pytest +attrs==18.2.0 \ + --hash=sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69 \ + --hash=sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb \ + # via pytest +more-itertools==5.0.0 \ + --hash=sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4 \ + --hash=sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc \ + --hash=sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9 \ + # via pytest +pluggy==0.8.0 \ + --hash=sha256:447ba94990e8014ee25ec853339faf7b0fc8050cdc3289d4d71f7f410fb90095 \ + --hash=sha256:bde19360a8ec4dfd8a20dcb811780a30998101f078fc7ded6162f0076f50508f \ + # via pytest +py==1.7.0 \ + --hash=sha256:bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694 \ + --hash=sha256:e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6 \ + # via pytest +pyasn1-modules==0.1.4 \ + --hash=sha256:2e39aead1602e4e5aa8b796260848f9383e51bea278edf7d2b4fb686944425fa \ + --hash=sha256:34e1d014608ca4f8a0cc3164a5add93f4b8a04a3871b96d31a028ff36a6fe924 \ + --hash=sha256:529d5d509562c22877a53771c5b394d8102c98cd62f33bb5af23b475879ea6d5 \ + --hash=sha256:641cc18cf56d4a60679804aa8ae8cbc7359b3fa2d5559df064ecfaca27d15b10 \ + --hash=sha256:6ad0e6772af4b74bd63c78c0102ece7b6a775f764e395137968af575c20bbfc9 \ + --hash=sha256:7411e837c83ea4cb6088aa3bf63691d348454fcd9bb3d9cc342859282419bfcf \ + --hash=sha256:7930d0f6109a47f78e5fb88a4f7ed2bfa1073ec9ddb2657deffa92f0805568fb \ + --hash=sha256:a24f5118f41af33f13fa0c7e8419fc7380dfe2d2f2dc0f554d928141c842f924 \ + --hash=sha256:ab7e23e45f32f0515e5a084eecaff22653bb6277d0d4229cb7369117abf33baa \ + --hash=sha256:b07c17bdb34d6f64aafea6269f2e8fb306a57473f0f38d9a6ca389d6ab30ac4a \ + --hash=sha256:beb3d344fee1fa68ddf36471c5d9120665ba049900d7fccbffa50c77036581de \ + --hash=sha256:ca3cdc5d8ecc97f78a202009512254bd7aeef77069405e9e834b45c48a26a4db +pytest==4.0.2 \ + --hash=sha256:f689bf2fc18c4585403348dd56f47d87780bf217c53ed9ae7a3e2d7faa45f8e9 \ + --hash=sha256:f812ea39a0153566be53d88f8de94839db1e8a05352ed8a49525d7d7f37861e9 +six==1.12.0 \ + --hash=sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c \ + --hash=sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73 \ + # via more-itertools, pytest diff --git a/python/spec/fixtures/requirements/uv_pip_compile_hashes.txt b/python/spec/fixtures/requirements/uv_pip_compile_hashes.txt new file mode 100644 index 0000000000..147d25f15a --- /dev/null +++ b/python/spec/fixtures/requirements/uv_pip_compile_hashes.txt @@ -0,0 +1,51 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --generate-hashes --output-file req.txt req.in +apipkg==1.4 \ + --hash=sha256:2e38399dbe842891fe85392601aab8f40a8f4cc5a9053c326de35a1cc0297ac6 \ + --hash=sha256:65d2aa68b28e7d31233bb2ba8eb31cda40e4671f8ac2d6b241e358c9652a74b9 \ + # via execnet +attrs==17.3.0 \ + --hash=sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9 \ + --hash=sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450 +execnet==1.5.0 \ + --hash=sha256:a7a84d5fa07a089186a329528f127c9d73b9de57f1a1131b82bb5320ee651f6a \ + --hash=sha256:fc155a6b553c66c838d1a22dba1dc9f5f505c43285a878c6f74a79c024750b83 \ + # via pytest-xdist +flaky==3.4.0 \ + --hash=sha256:4ad7880aef8c35a34ddb394d4fa33047765bca1e3d67d182bf6eba9c8eabf3a2 \ + --hash=sha256:d0533f473a46b916e6db6e84e20b06d8a70656600a0c14e819b0760b63f70226 +mock==2.0.0 \ + --hash=sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1 \ + --hash=sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba +more-itertools==4.1.0 \ + --hash=sha256:0dd8f72eeab0d2c3bd489025bb2f6a1b8342f9b198f6fc37b52d15cfa4531fea \ + --hash=sha256:11a625025954c20145b37ff6309cd54e39ca94f72f6bb9576d1195db6fa2442e \ + --hash=sha256:c9ce7eccdcb901a2c75d326ea134e0886abfbea5f93e91cc95de9507c0816c44 \ + # via pytest +pbr==4.0.3 \ + --hash=sha256:680bf5ba9b28dd56e08eb7c267991a37c7a5f90a92c2e07108829931a50ff80a \ + --hash=sha256:6874feb22334a1e9a515193cba797664e940b763440c88115009ec323a7f2df5 \ + # via mock +pluggy==0.6.0 \ + --hash=sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff \ + --hash=sha256:d345c8fe681115900d6da8d048ba67c25df42973bda370783cd58826442dcd7c \ + --hash=sha256:e160a7fcf25762bb60efc7e171d4497ff1d8d2d75a3d0df7a21b76821ecbf5c5 \ + # via pytest +py==1.5.3 \ + --hash=sha256:29c9fab495d7528e80ba1e343b958684f4ace687327e6f789a94bf3d1915f881 \ + --hash=sha256:983f77f3331356039fdd792e9220b7b8ee1aa6bd2b25f567a963ff1de5a64f6a \ + # via pytest +pytest-forked==0.2 \ + --hash=sha256:e4500cd0509ec4a26535f7d4112a8cc0f17d3a41c29ffd4eab479d2a55b30805 \ + --hash=sha256:f275cb48a73fc61a6710726348e1da6d68a978f0ec0c54ece5a5fae5977e5a08 \ + # via pytest-xdist +pytest-xdist==1.22.2 \ + --hash=sha256:be2662264b035920ba740ed6efb1c816a83c8a22253df7766d129f6a7bfdbd35 \ + --hash=sha256:e8f5744acc270b3e7d915bdb4d5f471670f049b6fbd163d4cbd52203b075d30f +pytest==3.5.1 \ + --hash=sha256:54713b26c97538db6ff0703a12b19aeaeb60b5e599de542e7fca0ec83b9038e8 \ + --hash=sha256:829230122facf05a5f81a6d4dfe6454a04978ea3746853b2b84567ecf8e5c526 +six==1.11.0 \ + --hash=sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9 \ + --hash=sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb \ + # via mock, more-itertools, pytest diff --git a/python/spec/fixtures/requirements/uv_pip_compile_imports_setup.txt b/python/spec/fixtures/requirements/uv_pip_compile_imports_setup.txt new file mode 100644 index 0000000000..48b89a0277 --- /dev/null +++ b/python/spec/fixtures/requirements/uv_pip_compile_imports_setup.txt @@ -0,0 +1,19 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --output-file something.txt something.in +-e file:///Users/greysteil/code/python-test +apipkg==1.5 # via execnet +atomicwrites==1.2.1 # via pytest +attrs==18.2.0 +contextlib2==0.5.5 # via raven +execnet==1.5.0 # via pytest-xdist +flaky==3.4.0 +mock==2.0.0 +more-itertools==4.3.0 # via pytest +pbr==4.0.2 # via mock +pluggy==0.7.1 # via pytest +py==1.6.0 # via pytest +pytest-forked==0.2 # via pytest-xdist +pytest-xdist==1.23.0 +pytest==3.7.4 +raven==5.32.0 +six==1.11.0 # via mock, more-itertools, pytest, pytest-xdist diff --git a/python/spec/fixtures/requirements/uv_pip_compile_no_binary.txt b/python/spec/fixtures/requirements/uv_pip_compile_no_binary.txt new file mode 100644 index 0000000000..258ce1fdfa --- /dev/null +++ b/python/spec/fixtures/requirements/uv_pip_compile_no_binary.txt @@ -0,0 +1,5 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --output-file requirements.txt requirements.in +--no-binary psycopg2 + +psycopg2==2.7.4 diff --git a/python/spec/fixtures/requirements/uv_pip_compile_no_strip_extras.txt b/python/spec/fixtures/requirements/uv_pip_compile_no_strip_extras.txt new file mode 100644 index 0000000000..e26439ba89 --- /dev/null +++ b/python/spec/fixtures/requirements/uv_pip_compile_no_strip_extras.txt @@ -0,0 +1,18 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --no-strip-extras --output-file=constraints.txt constraints.in +cachecontrol==0.12.10 + # via -r constraints.in +certifi==2021.10.8 + # via requests +charset-normalizer==2.0.7 + # via requests +idna==3.3 + # via requests +lockfile==0.12.2 + # via cachecontrol +msgpack==1.0.2 + # via cachecontrol +requests==2.26.0 + # via cachecontrol +urllib3==1.26.7 + # via requests diff --git a/python/spec/fixtures/requirements/uv_pip_compile_safe.txt b/python/spec/fixtures/requirements/uv_pip_compile_safe.txt new file mode 100644 index 0000000000..5151446181 --- /dev/null +++ b/python/spec/fixtures/requirements/uv_pip_compile_safe.txt @@ -0,0 +1,6 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --output-file dev-requirements.txt dev-requirements.in +flake8==3.5.0 +mccabe==0.6.1 # via flake8 +pycodestyle==2.4.0 # via flake8 +pyflakes==2.0.0 # via flake8 diff --git a/python/spec/fixtures/requirements/uv_pip_compile_strip_extras.txt b/python/spec/fixtures/requirements/uv_pip_compile_strip_extras.txt new file mode 100644 index 0000000000..7c21fe5e45 --- /dev/null +++ b/python/spec/fixtures/requirements/uv_pip_compile_strip_extras.txt @@ -0,0 +1,18 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --output-file=constraints.txt constraints.in +cachecontrol==0.12.10 + # via -r constraints.in +certifi==2021.10.8 + # via requests +charset-normalizer==2.0.7 + # via requests +idna==3.3 + # via requests +lockfile==0.12.2 + # via cachecontrol +msgpack==1.0.2 + # via cachecontrol +requests==2.26.0 + # via cachecontrol +urllib3==1.26.7 + # via requests diff --git a/python/spec/fixtures/requirements/uv_pip_compile_unmet_marker.txt b/python/spec/fixtures/requirements/uv_pip_compile_unmet_marker.txt new file mode 100644 index 0000000000..cd2d87df5f --- /dev/null +++ b/python/spec/fixtures/requirements/uv_pip_compile_unmet_marker.txt @@ -0,0 +1,15 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --output-file=requirements.txt requirements.in +apipkg==1.5 # via execnet +atomicwrites==1.1.5 # via pytest +attrs==17.3.0 +execnet==1.5.0 # via pytest-xdist +mock==2.0.0 +more-itertools==4.3.0 # via pytest +pbr==4.2.0 # via mock +pluggy==0.7.1 # via pytest +py==1.5.4 # via pytest +pytest-forked==0.2 # via pytest-xdist +pytest-xdist==1.22.5 +pytest==3.7.2 +six==1.11.0 # via mock, more-itertools, pytest, pytest-xdist diff --git a/python/spec/fixtures/requirements/uv_pip_compile_unpinned.txt b/python/spec/fixtures/requirements/uv_pip_compile_unpinned.txt new file mode 100644 index 0000000000..a776c74089 --- /dev/null +++ b/python/spec/fixtures/requirements/uv_pip_compile_unpinned.txt @@ -0,0 +1,15 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --output-file=requirements/test.txt requirements/test.in +apipkg==1.4 # via execnet +attrs==17.3.0 +execnet==1.5.0 # via pytest-xdist +flaky==3.4.0 +mock==2.0.0 +more-itertools==4.1.0 # via pytest +pbr==4.0.2 # via mock +pluggy==0.6.0 # via pytest +py==1.5.3 # via pytest +pytest-forked==0.2 # via pytest-xdist +pytest-xdist==1.22.2 +pytest==3.5.1 +six==1.11.0 # via mock, more-itertools, pytest diff --git a/python/spec/fixtures/requirements/uv_pip_compile_unpinned_renamed.txt b/python/spec/fixtures/requirements/uv_pip_compile_unpinned_renamed.txt new file mode 100644 index 0000000000..5c455cda2c --- /dev/null +++ b/python/spec/fixtures/requirements/uv_pip_compile_unpinned_renamed.txt @@ -0,0 +1,15 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --output-file=requirements/test-funky.txt requirements/test.in +apipkg==1.4 # via execnet +attrs==17.3.0 +execnet==1.5.0 # via pytest-xdist +flaky==3.4.0 +mock==2.0.0 +more-itertools==4.1.0 # via pytest +pbr==4.0.2 # via mock +pluggy==0.6.0 # via pytest +py==1.5.3 # via pytest +pytest-forked==0.2 # via pytest-xdist +pytest-xdist==1.22.2 +pytest==3.5.1 +six==1.11.0 # via mock, more-itertools, pytest diff --git a/python/spec/fixtures/requirements/uv_pip_compile_unsafe.txt b/python/spec/fixtures/requirements/uv_pip_compile_unsafe.txt new file mode 100644 index 0000000000..c037e652c0 --- /dev/null +++ b/python/spec/fixtures/requirements/uv_pip_compile_unsafe.txt @@ -0,0 +1,10 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --output-file dev-requirements.txt dev-requirements.in + +flake8==3.5.0 +mccabe==0.6.1 # via flake8 +pycodestyle==2.4.0 # via flake8 +pyflakes==2.0.0 # via flake8 + +# The following packages are considered to be unsafe by pip-compile by default, but not by uv, in a requirements file: +setuptools==40.4.3 # via flake8 diff --git a/python/spec/fixtures/requirements/uv_pip_compile_vcs_url.txt b/python/spec/fixtures/requirements/uv_pip_compile_vcs_url.txt new file mode 100644 index 0000000000..3a0ace2a81 --- /dev/null +++ b/python/spec/fixtures/requirements/uv_pip_compile_vcs_url.txt @@ -0,0 +1,6 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --output-file req.txt req.in +mock @ git+https://github.com/testing-cabal/mock.git@286792b2cd5b5baa8338260538ed207391280e34 +attrs==17.3.0 +pbr==5.1.1 +six==1.12.0 From 8338b7744b64aa27cebbe9817231c99242a46c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Thu, 22 Aug 2024 09:26:04 -0300 Subject: [PATCH 06/17] Update python/helpers/requirements.txt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Edgar Ramírez Mondragón <16805946+edgarrmondragon@users.noreply.github.com> --- python/helpers/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/helpers/requirements.txt b/python/helpers/requirements.txt index 79f7b56da8..3dad85d43e 100644 --- a/python/helpers/requirements.txt +++ b/python/helpers/requirements.txt @@ -7,7 +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.37 +uv==0.3.1 # Some dependencies will only install if Cython is present Cython==3.0.10 From afa5d746e8ea37210d7d1c5c81a0abc8e19ada25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Thu, 22 Aug 2024 09:31:12 -0300 Subject: [PATCH 07/17] Update python/lib/dependabot/python/file_updater/pip_compile_file_updater.rb --- .../python/file_updater/pip_compile_file_updater.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 f1c4b32701..7e43ed38e9 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 @@ -505,7 +505,9 @@ def uv_pip_compile_options_from_compiled_file(requirements_file) options << "--no-strip-extras" if requirements_file.content.include?("--no-strip-extras") - options << "--emit-build-options" if (requirements_file.content.include?("--no-binary") || requirements_file.content.include?("--only-binary")) + if (requirements_file.content.include?("--no-binary") || requirements_file.content.include?("--only-binary")) + options << "--emit-build-options" + end if (resolver = RESOLVER_REGEX.match(requirements_file.content)) options << "--resolver=#{resolver}" From 2edf6b25d8f979e535e871bfaa0d87ca085ed808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Thu, 22 Aug 2024 11:09:09 -0300 Subject: [PATCH 08/17] Update python/lib/dependabot/python/file_updater/pip_compile_file_updater.rb --- .../dependabot/python/file_updater/pip_compile_file_updater.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7e43ed38e9..7bf33e1930 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 @@ -505,7 +505,7 @@ def uv_pip_compile_options_from_compiled_file(requirements_file) options << "--no-strip-extras" if requirements_file.content.include?("--no-strip-extras") - if (requirements_file.content.include?("--no-binary") || requirements_file.content.include?("--only-binary")) + if requirements_file.content.include?("--no-binary") || requirements_file.content.include?("--only-binary") options << "--emit-build-options" end From 1c081bf4bd3e9c7a97f305920e36e40f799a72ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Thu, 22 Aug 2024 13:52:35 -0300 Subject: [PATCH 09/17] Rename uv_pip_compile_file_updater_spec.rb to pip_compile_file_updater_v2_spec.rb --- ...e_file_updater_spec.rb => pip_compile_file_updater_v2_spec.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename python/spec/dependabot/python/file_updater/{uv_pip_compile_file_updater_spec.rb => pip_compile_file_updater_v2_spec.rb} (100%) diff --git a/python/spec/dependabot/python/file_updater/uv_pip_compile_file_updater_spec.rb b/python/spec/dependabot/python/file_updater/pip_compile_file_updater_v2_spec.rb similarity index 100% rename from python/spec/dependabot/python/file_updater/uv_pip_compile_file_updater_spec.rb rename to python/spec/dependabot/python/file_updater/pip_compile_file_updater_v2_spec.rb From 7965aebf7d7eb6405efcc98f24460d5562497789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Mon, 9 Sep 2024 09:52:35 -0300 Subject: [PATCH 10/17] Update python/lib/dependabot/python/file_updater/pip_compile_file_updater.rb Co-authored-by: beagold <86345081+beagold@users.noreply.github.com> --- .../dependabot/python/file_updater/pip_compile_file_updater.rb | 2 ++ 1 file changed, 2 insertions(+) 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 7bf33e1930..99ad899cc5 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 @@ -513,6 +513,8 @@ def uv_pip_compile_options_from_compiled_file(requirements_file) options << "--resolver=#{resolver}" end + options << "--universal" if requirement_file.content.include?("--universal") + options end From c9d1c92f3eaa60e020b61171da1cd6911963c362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Mon, 9 Sep 2024 09:53:29 -0300 Subject: [PATCH 11/17] Update python/lib/dependabot/python/file_updater/pip_compile_file_updater.rb --- .../dependabot/python/file_updater/pip_compile_file_updater.rb | 2 -- 1 file changed, 2 deletions(-) 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 99ad899cc5..8bc7eb0565 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 @@ -499,8 +499,6 @@ def uv_pip_compile_options_from_compiled_file(requirements_file) options << "--no-annotate" unless requirements_file.content.include?("# via ") - options << "--no-header" - options << "--pre" if requirements_file.content.include?("--pre") options << "--no-strip-extras" if requirements_file.content.include?("--no-strip-extras") From cbc1e12aab3bf8a38be42a463b45c299d16c57f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Mon, 9 Sep 2024 15:44:01 -0300 Subject: [PATCH 12/17] Update python/lib/dependabot/python/file_updater/pip_compile_file_updater.rb Co-authored-by: beagold <86345081+beagold@users.noreply.github.com> --- .../dependabot/python/file_updater/pip_compile_file_updater.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8bc7eb0565..89d2d46f69 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 @@ -511,7 +511,7 @@ def uv_pip_compile_options_from_compiled_file(requirements_file) options << "--resolver=#{resolver}" end - options << "--universal" if requirement_file.content.include?("--universal") + options << "--universal" if requirements_file.content.include?("--universal") options end From 55a1a2605dde9750f5929a0d6bdf421a390bf8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Thu, 12 Sep 2024 16:16:42 -0300 Subject: [PATCH 13/17] Update python/helpers/requirements.txt --- python/helpers/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/helpers/requirements.txt b/python/helpers/requirements.txt index 3dad85d43e..f69bc62261 100644 --- a/python/helpers/requirements.txt +++ b/python/helpers/requirements.txt @@ -7,7 +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.3.1 +uv==0.4.9 # Some dependencies will only install if Cython is present Cython==3.0.10 From 88fb54b3b21024dabc349c6aba93e21805b71771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Thu, 12 Sep 2024 16:19:38 -0300 Subject: [PATCH 14/17] Update python/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb --- .../python/update_checker/pip_compile_version_resolver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 67a67c41d1..0ffa1128d7 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 @@ -253,7 +253,7 @@ def pip_compile_options(filename) command = "pyenv exec pip-compile" if (requirements_file = compiled_file_for_filename(filename)) && - requirements_file.content.include?("autogenerated by uv") + requirements_file.content.include?("uv pip compile") command = "pyenv exec uv pip compile" end From 39cc41ea1a47dd9d177bec4e8f5524a0981b66e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Avila?= Date: Thu, 12 Sep 2024 16:23:10 -0300 Subject: [PATCH 15/17] Update pip_compile_version_resolver.rb --- .../update_checker/pip_compile_version_resolver.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) 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 0ffa1128d7..b31f5c984a 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 @@ -251,10 +251,14 @@ def pip_compile_options(filename) options << "--output-file=#{requirements_file.name}" end - command = "pyenv exec pip-compile" - if (requirements_file = compiled_file_for_filename(filename)) && - requirements_file.content.include?("uv pip compile") - command = "pyenv exec uv pip compile" + if (requirements_file = compiled_file_for_filename(filename)) + if requirements_file.content.include?("uv pip compile") + options += uv_pip_compile_options_from_compiled_file(requirements_file) + command = "pyenv exec uv pip compile" + else + options += pip_compile_options_from_compiled_file(requirements_file) + command = "pyenv exec pip-compile" + end end [options.join(" "), command] From fc2e7961657d63f67c19bb84601a8047f5360da6 Mon Sep 17 00:00:00 2001 From: Gaston Avila Date: Mon, 23 Sep 2024 19:45:06 -0300 Subject: [PATCH 16/17] Add missing functions --- .../pip_compile_version_resolver.rb | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) 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 b31f5c984a..274ac33ea9 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 @@ -287,6 +287,56 @@ def run_pip_compile_command(command, fingerprint:) run_command(command, fingerprint: fingerprint) end + def pip_compile_options_from_compiled_file(requirements_file) + options = ["--output-file=#{requirements_file.name}"] + + options << "--no-emit-index-url" unless requirements_file.content.include?("index-url http") + + options << "--generate-hashes" if requirements_file.content.include?("--hash=sha") + + options << "--allow-unsafe" if includes_unsafe_packages?(requirements_file.content) + + options << "--no-annotate" unless requirements_file.content.include?("# via ") + + options << "--no-header" unless requirements_file.content.include?("autogenerated by pip-c") + + options << "--pre" if requirements_file.content.include?("--pre") + + options << "--strip-extras" if requirements_file.content.include?("--strip-extras") + + if (resolver = RESOLVER_REGEX.match(requirements_file.content)) + options << "--resolver=#{resolver}" + end + + options + end + + def uv_pip_compile_options_from_compiled_file(requirements_file) + options = ["--output-file=#{requirements_file.name}"] + + options << "--no-emit-index-url" unless requirements_file.content.include?("index-url http") + + options << "--generate-hashes" if requirements_file.content.include?("--hash=sha") + + options << "--no-annotate" unless requirements_file.content.include?("# via ") + + options << "--pre" if requirements_file.content.include?("--pre") + + options << "--no-strip-extras" if requirements_file.content.include?("--no-strip-extras") + + if requirements_file.content.include?("--no-binary") || requirements_file.content.include?("--only-binary") + options << "--emit-build-options" + end + + if (resolver = RESOLVER_REGEX.match(requirements_file.content)) + options << "--resolver=#{resolver}" + end + + options << "--universal" if requirements_file.content.include?("--universal") + + options + end + def python_env env = {} From ef182273c39b352c43eb9f744452bc005c54c762 Mon Sep 17 00:00:00 2001 From: Gaston Avila Date: Mon, 23 Sep 2024 19:49:42 -0300 Subject: [PATCH 17/17] Remove legacy pip stuff I don't mean to mess with --- .../pip_compile_version_resolver.rb | 25 ------------------- 1 file changed, 25 deletions(-) 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 274ac33ea9..3c78b99601 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 @@ -256,7 +256,6 @@ def pip_compile_options(filename) options += uv_pip_compile_options_from_compiled_file(requirements_file) command = "pyenv exec uv pip compile" else - options += pip_compile_options_from_compiled_file(requirements_file) command = "pyenv exec pip-compile" end end @@ -287,30 +286,6 @@ def run_pip_compile_command(command, fingerprint:) run_command(command, fingerprint: fingerprint) end - def pip_compile_options_from_compiled_file(requirements_file) - options = ["--output-file=#{requirements_file.name}"] - - options << "--no-emit-index-url" unless requirements_file.content.include?("index-url http") - - options << "--generate-hashes" if requirements_file.content.include?("--hash=sha") - - options << "--allow-unsafe" if includes_unsafe_packages?(requirements_file.content) - - options << "--no-annotate" unless requirements_file.content.include?("# via ") - - options << "--no-header" unless requirements_file.content.include?("autogenerated by pip-c") - - options << "--pre" if requirements_file.content.include?("--pre") - - options << "--strip-extras" if requirements_file.content.include?("--strip-extras") - - if (resolver = RESOLVER_REGEX.match(requirements_file.content)) - options << "--resolver=#{resolver}" - end - - options - end - def uv_pip_compile_options_from_compiled_file(requirements_file) options = ["--output-file=#{requirements_file.name}"]