pip/tests/unit/test_wheel.py

673 lines
22 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
2013-05-28 23:58:08 +02:00
"""Tests for wheel binary packages and .dist-info."""
2019-01-24 03:44:54 +01:00
import csv
2018-06-23 23:07:39 +02:00
import logging
2018-06-23 23:10:40 +02:00
import os
2019-01-24 03:44:54 +01:00
import textwrap
2019-12-31 18:32:11 +01:00
from email import message_from_string
2014-04-25 23:42:14 +02:00
2017-05-16 12:16:30 +02:00
import pytest
from mock import patch
from pip._vendor.packaging.requirements import Requirement
from pip._internal.exceptions import InstallationError
from pip._internal.locations import get_scheme
2020-02-01 13:40:20 +01:00
from pip._internal.models.direct_url import (
DIRECT_URL_METADATA_NAME,
ArchiveInfo,
DirectUrl,
)
from pip._internal.models.scheme import Scheme
2020-09-23 16:27:09 +02:00
from pip._internal.operations.build.wheel_legacy import get_legacy_build_wheel_path
from pip._internal.operations.install import wheel
from pip._internal.utils.compat import WINDOWS
from pip._internal.utils.misc import hash_file
from pip._internal.utils.unpacking import unpack_file
from pip._internal.utils.wheel import pkg_resources_distribution_for_wheel
from tests.lib import DATA_DIR, assert_paths_equal, skip_if_python2
from tests.lib.wheel import make_wheel
def call_get_legacy_build_wheel_path(caplog, names):
wheel_path = get_legacy_build_wheel_path(
names=names,
temp_dir='/tmp/abcd',
name='pendulum',
command_args=['arg1', 'arg2'],
command_output='output line 1\noutput line 2\n',
)
return wheel_path
def test_get_legacy_build_wheel_path(caplog):
actual = call_get_legacy_build_wheel_path(caplog, names=['name'])
assert_paths_equal(actual, '/tmp/abcd/name')
assert not caplog.records
def test_get_legacy_build_wheel_path__no_names(caplog):
caplog.set_level(logging.INFO)
actual = call_get_legacy_build_wheel_path(caplog, names=[])
assert actual is None
assert len(caplog.records) == 1
record = caplog.records[0]
assert record.levelname == 'WARNING'
assert record.message.splitlines() == [
"Legacy build of wheel for 'pendulum' created no files.",
"Command arguments: arg1 arg2",
'Command output: [use --verbose to show]',
]
def test_get_legacy_build_wheel_path__multiple_names(caplog):
caplog.set_level(logging.INFO)
# Deliberately pass the names in non-sorted order.
actual = call_get_legacy_build_wheel_path(
caplog, names=['name2', 'name1'],
)
assert_paths_equal(actual, '/tmp/abcd/name1')
assert len(caplog.records) == 1
record = caplog.records[0]
assert record.levelname == 'WARNING'
assert record.message.splitlines() == [
"Legacy build of wheel for 'pendulum' created more than one file.",
"Filenames (choosing first): ['name1', 'name2']",
"Command arguments: arg1 arg2",
'Command output: [use --verbose to show]',
]
@pytest.mark.parametrize(
"console_scripts",
[
2020-05-28 10:15:40 +02:00
u"pip = pip._internal.main:pip",
u"pip:pip = pip._internal.main:pip",
pytest.param(u"進入點 = 套件.模組:函式", marks=skip_if_python2),
],
)
2020-07-03 16:49:44 +02:00
def test_get_entrypoints(console_scripts):
entry_points_text = u"""
[console_scripts]
{}
[section]
common:one = module:func
common:two = module:other_func
""".format(console_scripts)
wheel_zip = make_wheel(
"simple",
"0.1.0",
extra_metadata_files={
"entry_points.txt": entry_points_text,
},
).as_zipfile()
distribution = pkg_resources_distribution_for_wheel(
wheel_zip, "simple", "<in memory>"
)
2020-07-03 16:49:44 +02:00
assert wheel.get_entrypoints(distribution) == (
dict([console_scripts.split(' = ')]),
2013-11-19 14:32:58 +01:00
{},
)
2020-07-03 16:49:44 +02:00
def test_get_entrypoints_no_entrypoints():
wheel_zip = make_wheel("simple", "0.1.0").as_zipfile()
distribution = pkg_resources_distribution_for_wheel(
wheel_zip, "simple", "<in memory>"
)
2020-07-03 16:49:44 +02:00
console, gui = wheel.get_entrypoints(distribution)
assert console == {}
assert gui == {}
@pytest.mark.parametrize("outrows, expected", [
([
(u'', '', 'a'),
(u'', '', ''),
], [
('', '', ''),
('', '', 'a'),
]),
([
# Include an int to check avoiding the following error:
# > TypeError: '<' not supported between instances of 'str' and 'int'
(u'', '', 1),
(u'', '', ''),
], [
('', '', ''),
('', '', '1'),
]),
([
2020-05-13 00:33:05 +02:00
# Test the normalization correctly encode everything for csv.writer().
(u'😉', '', 1),
(u'', '', ''),
], [
('', '', ''),
('😉', '', '1'),
]),
])
def test_normalized_outrows(outrows, expected):
actual = wheel._normalized_outrows(outrows)
assert actual == expected
2019-01-24 03:44:54 +01:00
def call_get_csv_rows_for_installed(tmpdir, text):
path = tmpdir.joinpath('temp.txt')
path.write_text(text)
2019-01-24 03:44:54 +01:00
# Test that an installed file appearing in RECORD has its filename
# updated in the new RECORD file.
installed = {u'a': 'z'}
2019-01-24 03:44:54 +01:00
changed = set()
generated = []
lib_dir = '/lib/dir'
2020-03-29 12:19:34 +02:00
with open(path, **wheel.csv_io_kwargs('r')) as f:
record_rows = list(csv.reader(f))
outrows = wheel.get_csv_rows_for_installed(
record_rows, installed=installed, changed=changed,
generated=generated, lib_dir=lib_dir,
)
2019-01-24 03:44:54 +01:00
return outrows
def test_get_csv_rows_for_installed(tmpdir, caplog):
2019-01-24 03:44:54 +01:00
text = textwrap.dedent("""\
a,b,c
d,e,f
""")
outrows = call_get_csv_rows_for_installed(tmpdir, text)
expected = [
('z', 'b', 'c'),
2019-01-24 03:44:54 +01:00
('d', 'e', 'f'),
]
assert outrows == expected
# Check there were no warnings.
assert len(caplog.records) == 0
2019-01-24 03:44:54 +01:00
def test_get_csv_rows_for_installed__long_lines(tmpdir, caplog):
2019-01-24 03:44:54 +01:00
text = textwrap.dedent("""\
a,b,c,d
e,f,g
h,i,j,k
2019-01-24 03:44:54 +01:00
""")
outrows = call_get_csv_rows_for_installed(tmpdir, text)
expected = [
('z', 'b', 'c'),
('e', 'f', 'g'),
('h', 'i', 'j'),
]
assert outrows == expected
messages = [rec.message for rec in caplog.records]
expected = [
"RECORD line has more than three elements: ['a', 'b', 'c', 'd']",
"RECORD line has more than three elements: ['h', 'i', 'j', 'k']"
]
assert messages == expected
2019-01-24 03:44:54 +01:00
@pytest.mark.parametrize("text,expected", [
("Root-Is-Purelib: true", True),
("Root-Is-Purelib: false", False),
("Root-Is-Purelib: hello", False),
("", False),
("root-is-purelib: true", True),
("root-is-purelib: True", True),
])
def test_wheel_root_is_purelib(text, expected):
assert wheel.wheel_root_is_purelib(message_from_string(text)) == expected
2013-05-28 23:58:08 +02:00
class TestWheelFile(object):
2019-09-18 02:18:52 +02:00
def test_unpack_wheel_no_flatten(self, tmpdir):
filepath = os.path.join(DATA_DIR, 'packages',
'meta-1.0-py2.py3-none-any.whl')
unpack_file(filepath, tmpdir)
2019-09-18 02:18:52 +02:00
assert os.path.isdir(os.path.join(tmpdir, 'meta-1.0.dist-info'))
2019-10-12 03:31:35 +02:00
class TestInstallUnpackedWheel(object):
"""
Tests for moving files from wheel src to scheme paths
"""
def prep(self, data, tmpdir):
Normalize Path to str in wheel tests In our next commit we will use the scheme path to locate files to byte-compile. If the scheme path is a `Path`, then that causes `compileall.compile_file` (via `py_compile.compile`) to fail with: ``` .tox/py38/lib/python3.8/site-packages/pip/_internal/operations/install/wheel.py:615: in install_unpacked_wheel success = compileall.compile_file( ../../../.pyenv/versions/3.8.0/lib/python3.8/compileall.py:157: in compile_file ok = py_compile.compile(fullname, cfile, dfile, True, ../../../.pyenv/versions/3.8.0/lib/python3.8/py_compile.py:162: in compile bytecode = importlib._bootstrap_external._code_to_timestamp_pyc( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ code = <code object <module> at 0x7fa7e274f500, file "/tmp/user/1000/pytest-of-chris/pytest-37/test_std_install_with_direct_u0/dest/lib/sample/__init__.py", line 1>, mtime = 1593910285.2200587, source_size = 134 > ??? E ValueError: unmarshallable object ``` Debugging in gdb shows that the error is set due to the `Path` object being present in the code object, which `marshal.dumps` can't handle (frame 1): ``` 0 w_complex_object (v=<optimized out>, flag=<optimized out>, p=0x7fffffff7160) at Python/marshal.c:564 1 w_object (v=<Path at remote 0x7fffee51f120>, p=0x7fffffff7160) at Python/marshal.c:370 2 w_complex_object (v=<code at remote 0x7fffee591710>, flag=<optimized out>, p=0x7fffffff7160) at Python/marshal.c:544 3 w_object (v=<code at remote 0x7fffee591710>, p=0x7fffffff7160) at Python/marshal.c:370 4 w_complex_object (v=('1.2.0', <code at remote 0x7fffee591710>, 'main', None), flag=<optimized out>, p=0x7fffffff7160) at Python/marshal.c:475 5 w_object (v=('1.2.0', <code at remote 0x7fffee591710>, 'main', None), p=0x7fffffff7160) at Python/marshal.c:370 6 w_complex_object (v=<code at remote 0x7fffee591ea0>, flag=<optimized out>, p=0x7fffffff7160) at Python/marshal.c:539 7 w_object (p=0x7fffffff7160, v=<code at remote 0x7fffee591ea0>) at Python/marshal.c:370 8 PyMarshal_WriteObjectToString (version=<optimized out>, x=<code at remote 0x7fffee591ea0>) at Python/marshal.c:1598 9 marshal_dumps_impl (module=<optimized out>, version=<optimized out>, value=<code at remote 0x7fffee591ea0>) at Python/marshal.c:1739 10 marshal_dumps (module=<optimized out>, args=<optimized out>, nargs=<optimized out>) at Python/clinic/marshal.c.h:124 ``` In the interest of easy git bisects, we commit this fix before the code that would expose the bug.
2020-07-04 20:47:32 +02:00
# Since Path implements __add__, os.path.join returns a Path object.
# Passing Path objects to interfaces expecting str (like
# `compileall.compile_file`) can cause failures, so we normalize it
# to a string here.
tmpdir = str(tmpdir)
self.name = 'sample'
2020-07-06 02:10:55 +02:00
self.wheelpath = make_wheel(
"sample",
"1.2.0",
metadata_body=textwrap.dedent(
"""
A sample Python project
=======================
...
"""
),
metadata_updates={
"Requires-Dist": ["peppercorn"],
},
extra_files={
"sample/__init__.py": textwrap.dedent(
'''
__version__ = '1.2.0'
def main():
"""Entry point for the application script"""
print("Call your main application code here")
'''
),
"sample/package_data.dat": "some data",
},
extra_metadata_files={
"DESCRIPTION.rst": textwrap.dedent(
"""
A sample Python project
=======================
...
"""
),
"top_level.txt": "sample\n",
"empty_dir/empty_dir/": "",
2020-07-06 02:10:55 +02:00
},
extra_data_files={
"data/my_data/data_file": "some data",
},
entry_points={
"console_scripts": ["sample = sample:main"],
"gui_scripts": ["sample2 = sample:main"],
},
2020-07-06 02:10:55 +02:00
).save_to_dir(tmpdir)
self.req = Requirement('sample')
2014-04-25 23:42:14 +02:00
self.src = os.path.join(tmpdir, 'src')
self.dest = os.path.join(tmpdir, 'dest')
self.scheme = Scheme(
purelib=os.path.join(self.dest, 'lib'),
platlib=os.path.join(self.dest, 'lib'),
headers=os.path.join(self.dest, 'headers'),
scripts=os.path.join(self.dest, 'bin'),
data=os.path.join(self.dest, 'data'),
)
self.src_dist_info = os.path.join(
self.src, 'sample-1.2.0.dist-info')
self.dest_dist_info = os.path.join(
self.scheme.purelib, 'sample-1.2.0.dist-info')
def assert_permission(self, path, mode):
target_mode = os.stat(path).st_mode & 0o777
2020-04-26 12:17:33 +02:00
assert (target_mode & mode) == mode, oct(target_mode)
def assert_installed(self, expected_permission):
# lib
assert os.path.isdir(
os.path.join(self.scheme.purelib, 'sample'))
# dist-info
metadata = os.path.join(self.dest_dist_info, 'METADATA')
self.assert_permission(metadata, expected_permission)
record = os.path.join(self.dest_dist_info, 'RECORD')
self.assert_permission(record, expected_permission)
# data files
data_file = os.path.join(self.scheme.data, 'my_data', 'data_file')
assert os.path.isfile(data_file)
# package data
2014-04-25 23:42:14 +02:00
pkg_data = os.path.join(
self.scheme.purelib, 'sample', 'package_data.dat')
2014-04-25 23:42:14 +02:00
assert os.path.isfile(pkg_data)
def test_std_install(self, data, tmpdir):
self.prep(data, tmpdir)
wheel.install_wheel(
2019-10-12 04:24:50 +02:00
self.name,
self.wheelpath,
2019-10-12 04:24:50 +02:00
scheme=self.scheme,
req_description=str(self.req),
)
self.assert_installed(0o644)
@pytest.mark.parametrize("user_mask, expected_permission", [
(0o27, 0o640)
])
def test_std_install_with_custom_umask(self, data, tmpdir,
user_mask, expected_permission):
"""Test that the files created after install honor the permissions
set when the user sets a custom umask"""
prev_umask = os.umask(user_mask)
try:
self.prep(data, tmpdir)
wheel.install_wheel(
self.name,
self.wheelpath,
scheme=self.scheme,
req_description=str(self.req),
)
self.assert_installed(expected_permission)
finally:
os.umask(prev_umask)
2020-04-11 18:40:55 +02:00
def test_std_install_requested(self, data, tmpdir):
self.prep(data, tmpdir)
wheel.install_wheel(
self.name,
self.wheelpath,
scheme=self.scheme,
req_description=str(self.req),
requested=True,
)
self.assert_installed(0o644)
2020-04-11 18:40:55 +02:00
requested_path = os.path.join(self.dest_dist_info, 'REQUESTED')
assert os.path.isfile(requested_path)
2020-02-01 13:40:20 +01:00
def test_std_install_with_direct_url(self, data, tmpdir):
"""Test that install_wheel creates direct_url.json metadata when
provided with a direct_url argument. Also test that the RECORDS
file contains an entry for direct_url.json in that case.
Note direct_url.url is intentionally different from wheelpath,
because wheelpath is typically the result of a local build.
"""
self.prep(data, tmpdir)
direct_url = DirectUrl(
url="file:///home/user/archive.tgz",
info=ArchiveInfo(),
)
wheel.install_wheel(
self.name,
self.wheelpath,
scheme=self.scheme,
req_description=str(self.req),
direct_url=direct_url,
)
direct_url_path = os.path.join(
self.dest_dist_info, DIRECT_URL_METADATA_NAME
)
2020-04-26 12:22:21 +02:00
self.assert_permission(direct_url_path, 0o644)
2020-02-01 13:40:20 +01:00
with open(direct_url_path, 'rb') as f:
expected_direct_url_json = direct_url.to_json()
direct_url_json = f.read().decode("utf-8")
assert direct_url_json == expected_direct_url_json
# check that the direc_url file is part of RECORDS
with open(os.path.join(self.dest_dist_info, "RECORD")) as f:
assert DIRECT_URL_METADATA_NAME in f.read()
def test_install_prefix(self, data, tmpdir):
prefix = os.path.join(os.path.sep, 'some', 'path')
self.prep(data, tmpdir)
scheme = get_scheme(
self.name,
user=False,
home=None,
root=tmpdir,
isolated=False,
prefix=prefix,
)
wheel.install_wheel(
self.name,
self.wheelpath,
scheme=scheme,
2019-10-12 04:24:50 +02:00
req_description=str(self.req),
)
2015-12-05 18:13:31 +01:00
bin_dir = 'Scripts' if WINDOWS else 'bin'
assert os.path.exists(os.path.join(tmpdir, 'some', 'path', bin_dir))
assert os.path.exists(os.path.join(tmpdir, 'some', 'path', 'my_data'))
def test_dist_info_contains_empty_dir(self, data, tmpdir):
"""
Test that empty dirs are not installed
"""
# e.g. https://github.com/pypa/pip/issues/1632#issuecomment-38027275
self.prep(data, tmpdir)
wheel.install_wheel(
2019-10-12 04:24:50 +02:00
self.name,
self.wheelpath,
2019-10-12 04:24:50 +02:00
scheme=self.scheme,
req_description=str(self.req),
)
self.assert_installed(0o644)
2014-04-25 23:42:14 +02:00
assert not os.path.isdir(
os.path.join(self.dest_dist_info, 'empty_dir'))
@pytest.mark.parametrize(
"path",
["/tmp/example", "../example", "./../example"]
)
def test_wheel_install_rejects_bad_paths(self, data, tmpdir, path):
self.prep(data, tmpdir)
wheel_path = make_wheel(
"simple", "0.1.0", extra_files={path: "example contents\n"}
).save_to_dir(tmpdir)
with pytest.raises(InstallationError) as e:
wheel.install_wheel(
"simple",
str(wheel_path),
scheme=self.scheme,
req_description="simple",
)
exc_text = str(e.value)
assert os.path.basename(wheel_path) in exc_text
assert "example" in exc_text
@pytest.mark.xfail(strict=True)
@pytest.mark.parametrize(
"entrypoint", ["hello = hello", "hello = hello:"]
)
@pytest.mark.parametrize(
"entrypoint_type", ["console_scripts", "gui_scripts"]
)
def test_invalid_entrypoints_fail(
self, data, tmpdir, entrypoint, entrypoint_type
):
self.prep(data, tmpdir)
wheel_path = make_wheel(
"simple", "0.1.0", entry_points={entrypoint_type: [entrypoint]}
).save_to_dir(tmpdir)
with pytest.raises(InstallationError) as e:
wheel.install_wheel(
"simple",
str(wheel_path),
scheme=self.scheme,
req_description="simple",
)
exc_text = str(e.value)
assert os.path.basename(wheel_path) in exc_text
assert entrypoint in exc_text
class TestMessageAboutScriptsNotOnPATH(object):
tilde_warning_msg = (
"NOTE: The current PATH contains path(s) starting with `~`, "
"which may not be expanded by all applications."
)
def _template(self, paths, scripts):
with patch.dict('os.environ', {'PATH': os.pathsep.join(paths)}):
return wheel.message_about_scripts_not_on_PATH(scripts)
def test_no_script(self):
retval = self._template(
paths=['/a/b', '/c/d/bin'],
scripts=[]
)
assert retval is None
def test_single_script__single_dir_not_on_PATH(self):
retval = self._template(
paths=['/a/b', '/c/d/bin'],
scripts=['/c/d/foo']
)
assert retval is not None
assert "--no-warn-script-location" in retval
assert "foo is installed in '/c/d'" in retval
assert self.tilde_warning_msg not in retval
def test_two_script__single_dir_not_on_PATH(self):
retval = self._template(
paths=['/a/b', '/c/d/bin'],
scripts=['/c/d/foo', '/c/d/baz']
)
assert retval is not None
assert "--no-warn-script-location" in retval
assert "baz and foo are installed in '/c/d'" in retval
assert self.tilde_warning_msg not in retval
def test_multi_script__multi_dir_not_on_PATH(self):
retval = self._template(
paths=['/a/b', '/c/d/bin'],
scripts=['/c/d/foo', '/c/d/bar', '/c/d/baz', '/a/b/c/spam']
)
assert retval is not None
assert "--no-warn-script-location" in retval
assert "bar, baz and foo are installed in '/c/d'" in retval
assert "spam is installed in '/a/b/c'" in retval
assert self.tilde_warning_msg not in retval
def test_multi_script_all__multi_dir_not_on_PATH(self):
retval = self._template(
paths=['/a/b', '/c/d/bin'],
scripts=[
'/c/d/foo', '/c/d/bar', '/c/d/baz',
'/a/b/c/spam', '/a/b/c/eggs'
]
)
assert retval is not None
assert "--no-warn-script-location" in retval
assert "bar, baz and foo are installed in '/c/d'" in retval
assert "eggs and spam are installed in '/a/b/c'" in retval
assert self.tilde_warning_msg not in retval
def test_two_script__single_dir_on_PATH(self):
retval = self._template(
paths=['/a/b', '/c/d/bin'],
scripts=['/a/b/foo', '/a/b/baz']
)
assert retval is None
def test_multi_script__multi_dir_on_PATH(self):
retval = self._template(
paths=['/a/b', '/c/d/bin'],
scripts=['/a/b/foo', '/a/b/bar', '/a/b/baz', '/c/d/bin/spam']
)
assert retval is None
def test_multi_script__single_dir_on_PATH(self):
retval = self._template(
paths=['/a/b', '/c/d/bin'],
scripts=['/a/b/foo', '/a/b/bar', '/a/b/baz']
)
assert retval is None
def test_single_script__single_dir_on_PATH(self):
retval = self._template(
paths=['/a/b', '/c/d/bin'],
scripts=['/a/b/foo']
)
assert retval is None
def test_PATH_check_case_insensitive_on_windows(self):
retval = self._template(
paths=['C:\\A\\b'],
scripts=['c:\\a\\b\\c', 'C:/A/b/d']
)
if WINDOWS:
assert retval is None
else:
assert retval is not None
assert self.tilde_warning_msg not in retval
def test_trailing_ossep_removal(self):
retval = self._template(
paths=[os.path.join('a', 'b', '')],
scripts=[os.path.join('a', 'b', 'c')]
)
assert retval is None
def test_missing_PATH_env_treated_as_empty_PATH_env(self, monkeypatch):
scripts = ['a/b/foo']
monkeypatch.delenv('PATH')
retval_missing = wheel.message_about_scripts_not_on_PATH(scripts)
monkeypatch.setenv('PATH', '')
retval_empty = wheel.message_about_scripts_not_on_PATH(scripts)
assert retval_missing == retval_empty
Add SHA256 hash of .whl as info output (#5908) * Add SHA256 hash of .whl as info output Currently I'm trying to debug some issues with what appear to be corrupt wheels. It would be very useful to see what pip thought the state of things was as it wrote the wheel output; if a final corrupt distributed file is then different to what pip has saved in its build logs, you know the problem is somewhere after pip but before distribution. Currently we get a log of the initial creation, then the stamp when it gets moved in the final output location, e.g.: creating '/tmp/pip-wheel-71CpBe/foo-1.2.3-py2.py3-none-any.whl ... Stored in directory: /opt/wheel/workspace A lot happens in between this, so my suggestion is we add the final output file and it's hash before the "Stored in directory:", e.g. you now see: Building wheels for collected packages: simple Running setup.py bdist_wheel for simple: started Running setup.py bdist_wheel for simple: finished with status 'done' Finished: simple-3.0-py3-none-any.whl sha256=39005a57a6327972575072af82e11d0817439fe6a069381f6f2a123a8c0bf1cf Stored in directory: /tmp/pytest-of-iwienand/pytest-18/test_pip_wheel_success0/workspace/scratch Successfully built simple Despite the hash being fairly important for things like --require-hashes, AFAICS the final hash is not put in the logs at all currently, so I think this is generically helpful. * Reword wheel hash details output This rewords the output to be more like the form of the preceding messages. Additionally the size is added, since we have calculated it anyway. The output will now look like: Collecting simple==3.0 Building wheels for collected packages: simple Building wheel for simple (setup.py): started Building wheel for simple (setup.py): finished with status 'done' Created wheel for simple: filename=simple-3.0-py3-none-any.whl size=1138 sha256=2a980a802c9d38a24d29aded2dc2df2b080e58370902e5fdf950090ff67aec10 Stored in directory: /tmp/pytest-of-iwienand/pytest-0/test_pip_wheel_success0/workspace/scratch Successfully built simple
2019-06-26 11:44:43 +02:00
def test_no_script_tilde_in_path(self):
retval = self._template(
paths=['/a/b', '/c/d/bin', '~/e', '/f/g~g'],
scripts=[]
)
assert retval is None
def test_multi_script_all_tilde__multi_dir_not_on_PATH(self):
retval = self._template(
paths=['/a/b', '/c/d/bin', '~e/f'],
scripts=[
'/c/d/foo', '/c/d/bar', '/c/d/baz',
'/a/b/c/spam', '/a/b/c/eggs', '/e/f/tilde'
]
)
assert retval is not None
assert "--no-warn-script-location" in retval
assert "bar, baz and foo are installed in '/c/d'" in retval
assert "eggs and spam are installed in '/a/b/c'" in retval
assert "tilde is installed in '/e/f'" in retval
assert self.tilde_warning_msg in retval
def test_multi_script_all_tilde_not_at_start__multi_dir_not_on_PATH(self):
retval = self._template(
paths=['/e/f~f', '/c/d/bin'],
scripts=[
'/c/d/foo', '/c/d/bar', '/c/d/baz',
'/e/f~f/c/spam', '/e/f~f/c/eggs'
]
)
assert retval is not None
assert "--no-warn-script-location" in retval
assert "bar, baz and foo are installed in '/c/d'" in retval
assert "eggs and spam are installed in '/e/f~f/c'" in retval
assert self.tilde_warning_msg not in retval
Add SHA256 hash of .whl as info output (#5908) * Add SHA256 hash of .whl as info output Currently I'm trying to debug some issues with what appear to be corrupt wheels. It would be very useful to see what pip thought the state of things was as it wrote the wheel output; if a final corrupt distributed file is then different to what pip has saved in its build logs, you know the problem is somewhere after pip but before distribution. Currently we get a log of the initial creation, then the stamp when it gets moved in the final output location, e.g.: creating '/tmp/pip-wheel-71CpBe/foo-1.2.3-py2.py3-none-any.whl ... Stored in directory: /opt/wheel/workspace A lot happens in between this, so my suggestion is we add the final output file and it's hash before the "Stored in directory:", e.g. you now see: Building wheels for collected packages: simple Running setup.py bdist_wheel for simple: started Running setup.py bdist_wheel for simple: finished with status 'done' Finished: simple-3.0-py3-none-any.whl sha256=39005a57a6327972575072af82e11d0817439fe6a069381f6f2a123a8c0bf1cf Stored in directory: /tmp/pytest-of-iwienand/pytest-18/test_pip_wheel_success0/workspace/scratch Successfully built simple Despite the hash being fairly important for things like --require-hashes, AFAICS the final hash is not put in the logs at all currently, so I think this is generically helpful. * Reword wheel hash details output This rewords the output to be more like the form of the preceding messages. Additionally the size is added, since we have calculated it anyway. The output will now look like: Collecting simple==3.0 Building wheels for collected packages: simple Building wheel for simple (setup.py): started Building wheel for simple (setup.py): finished with status 'done' Created wheel for simple: filename=simple-3.0-py3-none-any.whl size=1138 sha256=2a980a802c9d38a24d29aded2dc2df2b080e58370902e5fdf950090ff67aec10 Stored in directory: /tmp/pytest-of-iwienand/pytest-0/test_pip_wheel_success0/workspace/scratch Successfully built simple
2019-06-26 11:44:43 +02:00
class TestWheelHashCalculators(object):
def prep(self, tmpdir):
self.test_file = tmpdir.joinpath("hash.file")
Add SHA256 hash of .whl as info output (#5908) * Add SHA256 hash of .whl as info output Currently I'm trying to debug some issues with what appear to be corrupt wheels. It would be very useful to see what pip thought the state of things was as it wrote the wheel output; if a final corrupt distributed file is then different to what pip has saved in its build logs, you know the problem is somewhere after pip but before distribution. Currently we get a log of the initial creation, then the stamp when it gets moved in the final output location, e.g.: creating '/tmp/pip-wheel-71CpBe/foo-1.2.3-py2.py3-none-any.whl ... Stored in directory: /opt/wheel/workspace A lot happens in between this, so my suggestion is we add the final output file and it's hash before the "Stored in directory:", e.g. you now see: Building wheels for collected packages: simple Running setup.py bdist_wheel for simple: started Running setup.py bdist_wheel for simple: finished with status 'done' Finished: simple-3.0-py3-none-any.whl sha256=39005a57a6327972575072af82e11d0817439fe6a069381f6f2a123a8c0bf1cf Stored in directory: /tmp/pytest-of-iwienand/pytest-18/test_pip_wheel_success0/workspace/scratch Successfully built simple Despite the hash being fairly important for things like --require-hashes, AFAICS the final hash is not put in the logs at all currently, so I think this is generically helpful. * Reword wheel hash details output This rewords the output to be more like the form of the preceding messages. Additionally the size is added, since we have calculated it anyway. The output will now look like: Collecting simple==3.0 Building wheels for collected packages: simple Building wheel for simple (setup.py): started Building wheel for simple (setup.py): finished with status 'done' Created wheel for simple: filename=simple-3.0-py3-none-any.whl size=1138 sha256=2a980a802c9d38a24d29aded2dc2df2b080e58370902e5fdf950090ff67aec10 Stored in directory: /tmp/pytest-of-iwienand/pytest-0/test_pip_wheel_success0/workspace/scratch Successfully built simple
2019-06-26 11:44:43 +02:00
# Want this big enough to trigger the internal read loops.
self.test_file_len = 2 * 1024 * 1024
with open(str(self.test_file), "w") as fp:
fp.truncate(self.test_file_len)
self.test_file_hash = \
'5647f05ec18958947d32874eeb788fa396a05d0bab7c1b71f112ceb7e9b31eee'
self.test_file_hash_encoded = \
'sha256=VkfwXsGJWJR9ModO63iPo5agXQurfBtx8RLOt-mzHu4'
def test_hash_file(self, tmpdir):
self.prep(tmpdir)
h, length = hash_file(self.test_file)
Add SHA256 hash of .whl as info output (#5908) * Add SHA256 hash of .whl as info output Currently I'm trying to debug some issues with what appear to be corrupt wheels. It would be very useful to see what pip thought the state of things was as it wrote the wheel output; if a final corrupt distributed file is then different to what pip has saved in its build logs, you know the problem is somewhere after pip but before distribution. Currently we get a log of the initial creation, then the stamp when it gets moved in the final output location, e.g.: creating '/tmp/pip-wheel-71CpBe/foo-1.2.3-py2.py3-none-any.whl ... Stored in directory: /opt/wheel/workspace A lot happens in between this, so my suggestion is we add the final output file and it's hash before the "Stored in directory:", e.g. you now see: Building wheels for collected packages: simple Running setup.py bdist_wheel for simple: started Running setup.py bdist_wheel for simple: finished with status 'done' Finished: simple-3.0-py3-none-any.whl sha256=39005a57a6327972575072af82e11d0817439fe6a069381f6f2a123a8c0bf1cf Stored in directory: /tmp/pytest-of-iwienand/pytest-18/test_pip_wheel_success0/workspace/scratch Successfully built simple Despite the hash being fairly important for things like --require-hashes, AFAICS the final hash is not put in the logs at all currently, so I think this is generically helpful. * Reword wheel hash details output This rewords the output to be more like the form of the preceding messages. Additionally the size is added, since we have calculated it anyway. The output will now look like: Collecting simple==3.0 Building wheels for collected packages: simple Building wheel for simple (setup.py): started Building wheel for simple (setup.py): finished with status 'done' Created wheel for simple: filename=simple-3.0-py3-none-any.whl size=1138 sha256=2a980a802c9d38a24d29aded2dc2df2b080e58370902e5fdf950090ff67aec10 Stored in directory: /tmp/pytest-of-iwienand/pytest-0/test_pip_wheel_success0/workspace/scratch Successfully built simple
2019-06-26 11:44:43 +02:00
assert length == self.test_file_len
assert h.hexdigest() == self.test_file_hash
def test_rehash(self, tmpdir):
self.prep(tmpdir)
h, length = wheel.rehash(self.test_file)
assert length == str(self.test_file_len)
assert h == self.test_file_hash_encoded