Skip to content

Commit

Permalink
pipenv: handle installs with missing system deps
Browse files Browse the repository at this point in the history
Raise a `DependencyFileNotResolvable` error when pipenv fails to install
dependencies require missing system level dependenceis.

The error message parsing is pretty gnarly as the only reference of the
offending dependency is in a tmp build path in the error message.

In the test case pipenv failed to install `rtree` because it requires a
system level dependency: `libspatialindex`.

We don't want to install this in our ever growning dockerfile so attempt
to explain the error to users.

The underlying cause might be obscured by this open issue in pipenv:
pypa/pipenv#2791

Similarly, there's an open issue to fix the system requirement in rtree:
Toblerity/rtree#147

This is the error pipenv raises:

```
pipenv.patched.notpip._internal.exceptions.InstallationError: Command "python setup.py egg_info" failed with error code 1 in /tmp/tmp8jk2atvgbuild/rtree/
File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/resolver.py", line 126, in <module>
    main()
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/resolver.py", line 118, in main
    _main(parsed.pre, parsed.clear, parsed.verbose, parsed.system,
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/resolver.py", line 78, in _main
    results = resolve(
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/resolver.py", line 61, in resolve
    return resolve_deps(
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/utils.py", line 718, in resolve_deps
    resolved_tree, hashes, markers_lookup, resolver = actually_resolve_deps(
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/utils.py", line 480, in actually_resolve_deps
    resolved_tree = resolver.resolve()
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/utils.py", line 385, in resolve
    results = self.resolver.resolve(max_rounds=environments.PIPENV_MAX_ROUNDS)
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/patched/piptools/resolver.py", line 102, in resolve
    has_changed, best_matches = self._resolve_one_round()
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/patched/piptools/resolver.py", line 206, in _resolve_one_round
    for dep in self._iter_dependencies(best_match):
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/patched/piptools/resolver.py", line 301, in _iter_dependencies
    dependencies = self.repository.get_dependencies(ireq)
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/patched/piptools/repositories/pypi.py", line 234, in get_dependencies
    legacy_results = self.get_legacy_dependencies(ireq)
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/patched/piptools/repositories/pypi.py", line 426, in get_legacy_dependencies
    results, ireq = self.resolve_reqs(download_dir, ireq, wheel_cache)
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/patched/piptools/repositories/pypi.py", line 297, in resolve_reqs
    results = resolver._resolve_one(reqset, ireq)
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/patched/notpip/_internal/resolve.py", line 260, in _resolve_one
    abstract_dist = self._get_abstract_dist_for(req_to_install)
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/patched/notpip/_internal/resolve.py", line 211, in _get_abstract_dist_for
    abstract_dist = self.preparer.prepare_linked_requirement(
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/patched/notpip/_internal/operations/prepare.py", line 294, in prepare_linked_
    abstract_dist.prep_for_dist(finder, self.build_isolation)
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/patched/notpip/_internal/operations/prepare.py", line 127, in prep_for_dist
    self.req.run_egg_info()
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/patched/notpip/_internal/req/req_install.py", line 470, in run_egg_info
    call_subprocess(
  File "/usr/local/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pipenv/patched/notpip/_internal/utils/misc.py", line 703, in call_subprocess
    raise InstallationError(
pipenv.patched.notpip._internal.exceptions.InstallationError: Command "python setup.py egg_info" failed with error code 1 in /tmp/tmp8jk2atvgbuild/rtree/
```
  • Loading branch information
feelepxyz committed Dec 8, 2020
1 parent 6ff186e commit 4306afe
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class PipenvVersionResolver
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_REGEX =
%r{#{Regexp.quote(PIPENV_INSTALLATION_ERROR)}.+/(?<name>.+)/$}.freeze

attr_reader :dependency, :dependency_files, :credentials

Expand Down Expand Up @@ -169,7 +175,6 @@ def handle_pipenv_errors(error)
return if error.message.match?(/#{Regexp.quote(dependency.name)}/i)
end

puts error.message
if error.message.match?(GIT_DEPENDENCY_UNREACHABLE_REGEX)
url = error.message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX).
named_captures.fetch("url")
Expand Down Expand Up @@ -232,6 +237,10 @@ def handle_pipenv_errors_resolving_original_reqs(error)
raise DependencyFileNotResolvable, msg
end

# NOTE: Pipenv masks the actualy error, see this issue for updates:
# https://github.com/pypa/pipenv/issues/2791
handle_pipenv_installation_error(error.message) if error.message.match?(PIPENV_INSTALLATION_ERROR_REGEX)

# Raise an unhandled error, as this could be a problem with
# Dependabot's infrastructure, rather than the Pipfile
raise
Expand All @@ -257,6 +266,19 @@ def clean_error_message(message)
msg.gsub(/http.*?(?=\s)/, "<redacted>")
end

def handle_pipenv_installation_error(error_message)
# Find the dependency that's causing resolution to fail
dependency_name = error_message.match(PIPENV_INSTALLATION_ERROR_REGEX).named_captures["name"]
raise unless dependency_name

msg = "Pipenv failed to install \"#{dependency_name}\". This could be caused by missing system "\
"dependencies that can't be installed by Dependabot or required installation flags.\n\n"\
"Error output from running \"pipenv lock\":\n"\
"#{clean_error_message(error_message)}"

raise DependencyFileNotResolvable, msg
end

def write_temporary_dependency_files(updated_req: nil,
update_pipfile: true)
dependency_files.each do |file|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,35 @@
end
end
end

context "with a missing system libary" do
# NOTE: Attempt to update an unrelated dependency (tensorflow) to cause
# resolution to fail for rtree which has a system dependency on
# libspatialindex which isn't installed in dependabot-core's Dockerfile.
let(:dependency_files) do
project_dependency_files("pipenv/missing-system-library")
end
let(:updated_requirement) { "==2.3.1" }
let(:dependency_name) { "tensorflow" }
let(:dependency_version) { "2.1.0" }
let(:dependency_requirements) do
[{
file: "Pipfile",
requirement: "==2.1.0",
groups: ["default"],
source: nil
}]
end

it "raises a helpful error" do
expect { subject }.
to raise_error(Dependabot::DependencyFileNotResolvable) do |error|
expect(error.message).to include(
"Pipenv failed to install \"rtree\""
)
end
end
end
end

describe "#resolvable?" do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
rtree = "==0.9.3"
tensorflow = "==2.1.0"

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4306afe

Please sign in to comment.