Explicitly handle incorrect .data paths during wheel install

Previously our wheel installation process allowed wheels which contained
non-conforming contents in a contained .data directory.

After the refactoring to enable direct-from-wheel installation, pip
throws an exception when encountering these wheels, but does not include
any helpful information to pinpoint the cause.

Now if we encounter such a wheel, we trace an error that includes the
name of the requirement we're trying to install, the path to the wheel
file, the path we didn't understand, and a hint about what we expect.
This commit is contained in:
Chris Hunt 2020-07-29 17:51:58 -04:00
parent 31299ee370
commit 864f0e0efa
2 changed files with 27 additions and 1 deletions

View File

@ -583,7 +583,15 @@ def _install_wheel(
def make_data_scheme_file(record_path):
# type: (RecordPath) -> File
normed_path = os.path.normpath(record_path)
_, scheme_key, dest_subpath = normed_path.split(os.path.sep, 2)
try:
_, scheme_key, dest_subpath = normed_path.split(os.path.sep, 2)
except ValueError:
message = (
"Unexpected file in {}: {!r}. .data directory contents"
" should be named like: '<scheme key>/<path>'."
).format(wheel_path, record_path)
raise InstallationError(message)
scheme_path = scheme_paths[scheme_key]
dest_path = os.path.join(scheme_path, dest_subpath)
assert_no_path_traversal(scheme_path, dest_path)

View File

@ -681,3 +681,21 @@ def test_correct_package_name_while_creating_wheel_bug(script, package_name):
package = create_basic_wheel_for_package(script, package_name, '1.0')
wheel_name = os.path.basename(package)
assert wheel_name == 'simple_package-1.0-py2.py3-none-any.whl'
@pytest.mark.parametrize("name", ["purelib", "abc"])
def test_wheel_with_file_in_data_dir_has_reasonable_error(
script, tmpdir, name
):
"""Normally we expect entities in the .data directory to be in a
subdirectory, but if they are not then we should show a reasonable error
message that includes the path.
"""
wheel_path = make_wheel(
"simple", "0.1.0", extra_data_files={name: "hello world"}
).save_to_dir(tmpdir)
result = script.pip(
"install", "--no-index", str(wheel_path), expect_error=True
)
assert "simple-0.1.0.data/{}".format(name) in result.stderr