Skip to content

Commit

Permalink
Drop python 2.x support and fix pipenv 2021.5.29 tests and
Browse files Browse the repository at this point in the history
Python 2 has been unsupported for a year and a half now, pip 21 no
longer supports it and we see pretty small usage numbers at GitHub.
Maintaining support for it has become increasingly harder, and this
pipenv upgrade made it even more cumbersome.

It's been a good ride, python 2. See ya!
  • Loading branch information
jurre committed Jun 9, 2021
1 parent 257de2a commit b6ae9d3
Show file tree
Hide file tree
Showing 33 changed files with 477 additions and 798 deletions.
1 change: 0 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ RUN mkdir -p "$PYENV_ROOT" && chown dependabot:dependabot "$PYENV_ROOT"
USER dependabot
RUN git clone https://github.com/pyenv/pyenv.git --branch 1.2.26 --single-branch --depth=1 /usr/local/.pyenv \
&& pyenv install 3.9.4 \
&& pyenv install 2.7.18 \
&& pyenv global 3.9.4 \
&& rm -Rf /tmp/python-build*
USER root
Expand Down
1 change: 0 additions & 1 deletion python/helpers/build
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ cp -r \
"$install_dir"

cd "$install_dir"
PYENV_VERSION=2.7.18 pyenv exec pip install -r "requirements.txt"
PYENV_VERSION=3.9.4 pyenv exec pip install -r "requirements.txt"

# Workaround of https://github.com/python-poetry/poetry/issues/3010
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,24 +170,6 @@ def run_pip_compile_command(command, allow_unsafe_shell_command: false)
command,
allow_unsafe_shell_command: allow_unsafe_shell_command
)
rescue SharedHelpers::HelperSubprocessFailed => e
original_error ||= e
msg = e.message

relevant_error =
if error_suggests_bad_python_version?(msg) then original_error
else e
end

raise relevant_error unless error_suggests_bad_python_version?(msg)
raise relevant_error if user_specified_python_version
raise relevant_error if python_version == "2.7.18"

@python_version = "2.7.18"
retry
ensure
@python_version = nil
FileUtils.remove_entry(".python-version", true)
end

def python_env
Expand All @@ -205,14 +187,6 @@ def python_env
env
end

def error_suggests_bad_python_version?(message)
return true if message.include?("UnsupportedPythonVersion")
return true if message.include?("not find a version that satisfies")

message.include?('Command "python setup.py egg_info" failed') ||
message.include?("exit status 1: python setup.py egg_info")
end

def write_updated_dependency_files
dependency_files.each do |file|
path = file.name
Expand Down
28 changes: 0 additions & 28 deletions python/lib/dependabot/python/file_updater/pipfile_file_updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -264,34 +264,6 @@ def run_command(command, env: {})
def run_pipenv_command(command, env: pipenv_env_variables)
run_command("pyenv local #{python_version}")
run_command(command, env: env)
rescue SharedHelpers::HelperSubprocessFailed => e
original_error ||= e
msg = e.message

relevant_error =
if error_suggests_bad_python_version?(msg) then original_error
else e
end

raise relevant_error unless error_suggests_bad_python_version?(msg)
raise relevant_error if python_version.start_with?("2")

# Clear the existing virtualenv, so that we use the new Python version
run_command("pyenv local #{python_version}")
run_command("pyenv exec pipenv --rm")

@python_version = "2.7.18"
retry
ensure
@python_version = nil
FileUtils.remove_entry(".python-version", true)
end

def error_suggests_bad_python_version?(message)
return true if message.include?("UnsupportedPythonVersion")

message.include?('Command "python setup.py egg_info" failed') ||
message.include?("exit status 1: python setup.py egg_info")
end

def write_temporary_dependency_files(pipfile_content)
Expand Down
7 changes: 3 additions & 4 deletions python/lib/dependabot/python/python_versions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Dependabot
module Python
module PythonVersions
PRE_INSTALLED_PYTHON_VERSIONS = %w(
3.9.4 2.7.18
3.9.4
).freeze

# Due to an OpenSSL issue we can only install the following versions in
Expand All @@ -15,14 +15,13 @@ module PythonVersions
3.7.10 3.7.9 3.7.8 3.7.7 3.7.6 3.7.5 3.7.4 3.7.3 3.7.2 3.7.1 3.7.0
3.6.13 3.6.12 3.6.11 3.6.10 3.6.9 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3
3.6.2 3.6.1 3.6.0 3.5.10 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3
2.7.18 2.7.17 2.7.16 2.7.15 2.7.14 2.7.13
).freeze

# This list gets iterated through to find a valid version, so we have
# the two pre-installed versions listed first.
# the pre-installed versions listed first.
SUPPORTED_VERSIONS_TO_ITERATE =
[
*PRE_INSTALLED_PYTHON_VERSIONS.select { |v| v.start_with?("3") },
*PRE_INSTALLED_PYTHON_VERSIONS,
*SUPPORTED_VERSIONS
].freeze
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,35 +235,6 @@ def pip_compile_index_options
def run_pip_compile_command(command)
run_command("pyenv local #{python_version}")
run_command(command)
rescue SharedHelpers::HelperSubprocessFailed => e
original_err ||= e
msg = e.message

relevant_error = choose_relevant_error(original_err, e)
raise relevant_error unless error_suggests_bad_python_version?(msg)
raise relevant_error if user_specified_python_version
raise relevant_error if python_version == "2.7.18"

@python_version = "2.7.18"
retry
ensure
@python_version = nil
FileUtils.remove_entry(".python-version", true)
end

def choose_relevant_error(previous_error, new_error)
return previous_error if previous_error == new_error

# If the previous error was definitely due to using the wrong Python
# version, return the new error (which can't be worse)
return new_error if error_certainly_bad_python_version?(previous_error.message)

# Otherwise, if the new error may be due to using the wrong Python
# version, return the old error (which can't be worse)
return previous_error if error_suggests_bad_python_version?(new_error.message)

# Otherwise, default to the new error
new_error
end

def python_env
Expand Down Expand Up @@ -292,15 +263,6 @@ def error_certainly_bad_python_version?(message)
message.include?("SyntaxError")
end

def error_suggests_bad_python_version?(message)
return true if error_certainly_bad_python_version?(message)
return true if message.include?("not find a version that satisfies")
return true if message.include?("No matching distribution found")

message.include?('Command "python setup.py egg_info" failed') ||
message.include?("exit status 1: python setup.py egg_info")
end

def write_temporary_dependency_files(updated_req: nil,
update_requirement: true)
dependency_files.each do |file|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
require "dependabot/python/name_normaliser"
require "dependabot/python/version"

# rubocop:disable Metrics/ClassLength
module Dependabot
module Python
class UpdateChecker
Expand All @@ -35,16 +34,14 @@ class PipenvVersionResolver
GIT_REFERENCE_NOT_FOUND_REGEX =
%r{git checkout -q (?<tag>[^\n"]+)\n?[^\n]*/(?<name>.*?)(\\n'\]|$)}m.
freeze
UNSUPPORTED_DEPS = %w(pyobjc).freeze
UNSUPPORTED_DEP_REGEX =
/"python setup\.py egg_info".*(?:#{UNSUPPORTED_DEPS.join("|")})/.
freeze
PIPENV_INSTALLATION_ERROR = "pipenv.patched.notpip._internal."\
"exceptions.InstallationError: "\
"Command \"python setup.py egg_info\" "\
"failed with error code 1 in"
PIPENV_INSTALLATION_ERROR = "pipenv.patched.notpip._internal.exceptions.InstallationError: Command errored out"\
" with exit status 1: python setup.py egg_info"
TRACEBACK = "Traceback (most recent call last):"
PIPENV_INSTALLATION_ERROR_REGEX =
%r{#{Regexp.quote(PIPENV_INSTALLATION_ERROR)}.+/(?<name>.+)/$}.freeze
/#{Regexp.quote(TRACEBACK)}[\s\S]*^\s+import\s(?<name>.+)[\s\S]*^#{Regexp.quote(PIPENV_INSTALLATION_ERROR)}/.
freeze
UNSUPPORTED_DEP_REGEX = /(?:pyobjc)[\s\S]*#{Regexp.quote(PIPENV_INSTALLATION_ERROR)}/.freeze
PIPENV_RANGE_WARNING = /Warning:\sPython\s[<>].* was not found/.freeze

attr_reader :dependency, :dependency_files, :credentials

Expand Down Expand Up @@ -151,9 +148,19 @@ def handle_pipenv_errors(error)
raise DependencyFileNotResolvable, msg
end

if error.message.include?("Could not find a version") ||
error.message.include?("is not a python version")
check_original_requirements_resolvable
if error.message.match?(PIPENV_RANGE_WARNING)
msg = "Pipenv does not support specifying Python ranges "\
"(see https://github.com/pypa/pipenv/issues/1050 for more "\
"details)."
raise DependencyFileNotResolvable, msg
end

check_original_requirements_resolvable if error.message.include?("Could not find a version")

if error.message.include?("SyntaxError: invalid syntax")
raise DependencyFileNotResolvable,
"SyntaxError while installing dependencies. Is one of the dependencies not Python 3 compatible? "\
"Pip v21 no longer supports Python 2."
end

if (error.message.include?('Command "python setup.py egg_info"') ||
Expand Down Expand Up @@ -221,13 +228,6 @@ def handle_pipenv_errors_resolving_original_reqs(error)
raise DependencyFileNotResolvable, msg
end

if error.message.include?("is not a python version")
msg = "Pipenv does not support specifying Python ranges "\
"(see https://github.com/pypa/pipenv/issues/1050 for more "\
"details)."
raise DependencyFileNotResolvable, msg
end

if error.message.include?("UnsupportedPythonVersion") &&
user_specified_python_requirement
msg = clean_error_message(error.message).
Expand Down Expand Up @@ -465,36 +465,6 @@ def run_command(command, env: {})
def run_pipenv_command(command, env: pipenv_env_variables)
run_command("pyenv local #{python_version}")
run_command(command, env: env)
rescue SharedHelpers::HelperSubprocessFailed => e
original_error ||= e
msg = e.message

relevant_error =
if may_be_using_wrong_python_version?(msg) then original_error
else e
end

raise relevant_error unless may_be_using_wrong_python_version?(msg)
raise relevant_error if python_version.start_with?("2")

# Clear the existing virtualenv, so that we use the new Python version
run_command("pyenv local #{python_version}")
run_command("pyenv exec pipenv --rm")

@python_version = "2.7.18"
retry
ensure
@python_version = nil
FileUtils.remove_entry(".python-version", true)
end

def may_be_using_wrong_python_version?(error_message)
return false if user_specified_python_requirement
return true if error_message.include?("UnsupportedPythonVersion")
return true if error_message.include?("at matches #{dependency.name}")

error_message.include?('Command "python setup.py egg_info" failed') ||
error_message.include?("exit status 1: python setup.py egg_info")
end

def pipenv_env_variables
Expand Down Expand Up @@ -530,4 +500,3 @@ def setup_cfg_files
end
end
end
# rubocop:enable Metrics/ClassLength
Original file line number Diff line number Diff line change
Expand Up @@ -175,17 +175,6 @@
end
end

context "with a met marker that forces a difference Python version" do
let(:manifest_fixture_name) { "met_marker.in" }
let(:generated_fixture_name) { "pip_compile_met_marker.txt" }

it "updates the requirements.txt, keeping the unmet dep in 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).to include("flaky")
end
end

context "with an unsafe dependency" do
let(:manifest_fixture_name) { "unsafe.in" }
let(:dependency_name) { "flake8" }
Expand Down Expand Up @@ -498,91 +487,5 @@
end
end
end

context "when the upgrade requires Python 2.7" do
let(:manifest_fixture_name) { "legacy_python.in" }
let(:generated_fixture_name) { "pip_compile_legacy_python.txt" }

let(:dependency_name) { "wsgiref" }
let(:dependency_version) { "0.1.2" }
let(:dependency_previous_version) { "0.1.1" }
let(:dependency_requirements) do
[{
file: "requirements/test.in",
requirement: "<=0.1.2",
groups: [],
source: nil
}]
end
let(:dependency_previous_requirements) { dependency_requirements }

it "updates the requirements.txt" do
expect(updated_files.count).to eq(1)
expect(updated_files.last.content).to include("wsgiref==0.1.2")
end

context "for a dependency that uses markers correctly" do
let(:manifest_fixture_name) { "legacy_python_2.in" }
let(:generated_fixture_name) { "pip_compile_legacy_python_2.txt" }

let(:dependency_name) { "astroid" }
let(:dependency_version) { "1.6.5" }
let(:dependency_previous_version) { "1.6.4" }
let(:dependency_requirements) do
[{
file: "requirements/test.in",
requirement: "<2",
groups: [],
source: nil
}]
end
let(:dependency_previous_requirements) { dependency_requirements }

it "updates the requirements.txt" do
expect(updated_files.count).to eq(1)
expect(updated_files.last.content).to include("astroid==1.6.5")
end
end
end

context "when the upgrade would resolve differently on Python 3" do
let(:dependency_files) { [manifest_file, generated_file, python_file] }
let(:manifest_fixture_name) { "resolves_differently_by_python.in" }
let(:generated_fixture_name) do
"pip_compile_resolves_differently_by_python.txt"
end
let(:python_file) do
Dependabot::DependencyFile.new(
name: ".python-version",
content: "2.7.18"
)
end

let(:dependency_name) { "tornado" }
let(:dependency_version) { "5.1.1" }
let(:dependency_previous_version) { "5.1.0" }
let(:dependency_requirements) do
[{
file: "requirements/test.in",
requirement: "==5.1.1",
groups: [],
source: nil
}]
end
let(:dependency_previous_requirements) do
[{
file: "requirements/test.in",
requirement: "==5.1.0",
groups: [],
source: nil
}]
end

it "updates the requirements.txt using Python 2.7" do
expect(updated_files.count).to eq(2)
expect(updated_files.last.content).to include("tornado==5.1.1")
expect(updated_files.last.content).to include("futures")
end
end
end
end
Loading

0 comments on commit b6ae9d3

Please sign in to comment.