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
|
2014-04-25 23:42:14 +02:00
|
|
|
|
2017-05-16 12:16:30 +02:00
|
|
|
import pytest
|
|
|
|
from mock import Mock, patch
|
2017-06-13 14:17:00 +02:00
|
|
|
from pip._vendor.packaging.requirements import Requirement
|
|
|
|
|
2019-11-03 14:07:30 +01:00
|
|
|
import pip._internal.wheel_builder
|
2017-08-31 17:48:18 +02:00
|
|
|
from pip._internal import pep425tags, wheel
|
2019-09-25 13:37:53 +02:00
|
|
|
from pip._internal.commands.wheel import WheelCommand
|
2017-08-31 17:48:18 +02:00
|
|
|
from pip._internal.exceptions import InvalidWheelFilename, UnsupportedWheel
|
2019-10-12 03:49:39 +02:00
|
|
|
from pip._internal.locations import distutils_scheme
|
2019-02-01 11:23:58 +01:00
|
|
|
from pip._internal.models.link import Link
|
|
|
|
from pip._internal.req.req_install import InstallRequirement
|
2018-07-29 13:11:37 +02:00
|
|
|
from pip._internal.utils.compat import WINDOWS
|
2019-11-03 14:24:11 +01:00
|
|
|
from pip._internal.utils.misc import hash_file
|
2019-09-18 02:27:35 +02:00
|
|
|
from pip._internal.utils.unpacking import unpack_file
|
2019-09-07 15:59:41 +02:00
|
|
|
from pip._internal.wheel import (
|
|
|
|
MissingCallableSuffix,
|
|
|
|
_raise_for_invalid_entrypoint,
|
|
|
|
)
|
2019-11-02 15:14:39 +01:00
|
|
|
from tests.lib import DATA_DIR, _create_test_package, assert_paths_equal
|
2017-08-31 19:17:56 +02:00
|
|
|
|
2013-05-28 23:58:08 +02:00
|
|
|
|
2019-10-27 17:52:19 +01:00
|
|
|
class ReqMock:
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
name="pendulum",
|
|
|
|
is_wheel=False,
|
|
|
|
editable=False,
|
|
|
|
link=None,
|
|
|
|
constraint=False,
|
|
|
|
source_dir="/tmp/pip-install-123/pendulum",
|
|
|
|
):
|
|
|
|
self.name = name
|
|
|
|
self.is_wheel = is_wheel
|
|
|
|
self.editable = editable
|
|
|
|
self.link = link
|
|
|
|
self.constraint = constraint
|
|
|
|
self.source_dir = source_dir
|
|
|
|
|
|
|
|
|
2018-10-12 21:16:38 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"s, expected",
|
|
|
|
[
|
|
|
|
# Trivial.
|
|
|
|
("pip-18.0", True),
|
|
|
|
|
|
|
|
# Ambiguous.
|
|
|
|
("foo-2-2", True),
|
|
|
|
("im-valid", True),
|
|
|
|
|
|
|
|
# Invalid.
|
|
|
|
("invalid", False),
|
|
|
|
("im_invalid", False),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_contains_egg_info(s, expected):
|
2019-11-03 14:07:30 +01:00
|
|
|
result = pip._internal.wheel_builder._contains_egg_info(s)
|
2018-10-12 21:16:38 +02:00
|
|
|
assert result == expected
|
|
|
|
|
|
|
|
|
2019-02-10 21:36:59 +01:00
|
|
|
def make_test_install_req(base_name=None):
|
2019-02-01 11:23:58 +01:00
|
|
|
"""
|
2019-02-10 21:36:59 +01:00
|
|
|
Return an InstallRequirement object for testing purposes.
|
2019-02-01 11:23:58 +01:00
|
|
|
"""
|
2019-02-10 21:36:59 +01:00
|
|
|
if base_name is None:
|
|
|
|
base_name = 'pendulum-2.0.4'
|
|
|
|
|
2019-02-01 11:23:58 +01:00
|
|
|
req = Requirement('pendulum')
|
|
|
|
link_url = (
|
2019-02-01 11:40:23 +01:00
|
|
|
'https://files.pythonhosted.org/packages/aa/{base_name}.tar.gz'
|
2019-02-01 11:23:58 +01:00
|
|
|
'#sha256=cf535d36c063575d4752af36df928882b2e0e31541b4482c97d637527'
|
|
|
|
'85f9fcb'
|
2019-02-01 11:40:23 +01:00
|
|
|
).format(base_name=base_name)
|
2019-02-01 11:23:58 +01:00
|
|
|
link = Link(
|
|
|
|
url=link_url,
|
|
|
|
comes_from='https://pypi.org/simple/pendulum/',
|
|
|
|
requires_python='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
|
|
|
|
)
|
|
|
|
req = InstallRequirement(
|
|
|
|
req=req,
|
|
|
|
comes_from=None,
|
|
|
|
constraint=False,
|
|
|
|
editable=False,
|
|
|
|
link=link,
|
|
|
|
source_dir='/tmp/pip-install-9py5m2z1/pendulum',
|
|
|
|
)
|
2019-02-10 21:36:59 +01:00
|
|
|
|
|
|
|
return req
|
|
|
|
|
|
|
|
|
2019-05-26 05:31:41 +02:00
|
|
|
@pytest.mark.parametrize('file_tag, expected', [
|
|
|
|
(('py27', 'none', 'any'), 'py27-none-any'),
|
|
|
|
(('cp33', 'cp32dmu', 'linux_x86_64'), 'cp33-cp32dmu-linux_x86_64'),
|
|
|
|
])
|
|
|
|
def test_format_tag(file_tag, expected):
|
|
|
|
actual = wheel.format_tag(file_tag)
|
|
|
|
assert actual == expected
|
|
|
|
|
|
|
|
|
2019-10-27 17:52:19 +01:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"req, need_wheel, disallow_binaries, expected",
|
|
|
|
[
|
|
|
|
# pip wheel (need_wheel=True)
|
|
|
|
(ReqMock(), True, False, True),
|
|
|
|
(ReqMock(), True, True, True),
|
|
|
|
(ReqMock(constraint=True), True, False, False),
|
|
|
|
(ReqMock(is_wheel=True), True, False, False),
|
|
|
|
(ReqMock(editable=True), True, False, True),
|
|
|
|
(ReqMock(source_dir=None), True, False, True),
|
|
|
|
(ReqMock(link=Link("git+https://g.c/org/repo")), True, False, True),
|
|
|
|
(ReqMock(link=Link("git+https://g.c/org/repo")), True, True, True),
|
|
|
|
# pip install (need_wheel=False)
|
|
|
|
(ReqMock(), False, False, True),
|
|
|
|
(ReqMock(), False, True, False),
|
|
|
|
(ReqMock(constraint=True), False, False, False),
|
|
|
|
(ReqMock(is_wheel=True), False, False, False),
|
|
|
|
(ReqMock(editable=True), False, False, False),
|
|
|
|
(ReqMock(source_dir=None), False, False, False),
|
|
|
|
# By default (i.e. when binaries are allowed), VCS requirements
|
|
|
|
# should be built in install mode.
|
|
|
|
(ReqMock(link=Link("git+https://g.c/org/repo")), False, False, True),
|
|
|
|
# Disallowing binaries, however, should cause them not to be built.
|
|
|
|
(ReqMock(link=Link("git+https://g.c/org/repo")), False, True, False),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_should_build(req, need_wheel, disallow_binaries, expected):
|
2019-11-03 14:07:30 +01:00
|
|
|
should_build = pip._internal.wheel_builder.should_build(
|
2019-10-27 17:52:19 +01:00
|
|
|
req,
|
|
|
|
need_wheel,
|
|
|
|
check_binary_allowed=lambda req: not disallow_binaries,
|
|
|
|
)
|
|
|
|
assert should_build is expected
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"req, disallow_binaries, expected",
|
|
|
|
[
|
|
|
|
(ReqMock(editable=True), False, False),
|
|
|
|
(ReqMock(source_dir=None), False, False),
|
|
|
|
(ReqMock(link=Link("git+https://g.c/org/repo")), False, False),
|
|
|
|
(ReqMock(link=Link("https://g.c/dist.tgz")), False, False),
|
|
|
|
(ReqMock(link=Link("https://g.c/dist-2.0.4.tgz")), False, True),
|
|
|
|
(ReqMock(editable=True), True, False),
|
|
|
|
(ReqMock(source_dir=None), True, False),
|
|
|
|
(ReqMock(link=Link("git+https://g.c/org/repo")), True, False),
|
|
|
|
(ReqMock(link=Link("https://g.c/dist.tgz")), True, False),
|
|
|
|
(ReqMock(link=Link("https://g.c/dist-2.0.4.tgz")), True, False),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_should_cache(
|
|
|
|
req, disallow_binaries, expected
|
|
|
|
):
|
|
|
|
def check_binary_allowed(req):
|
|
|
|
return not disallow_binaries
|
|
|
|
|
2019-11-03 14:07:30 +01:00
|
|
|
should_cache = pip._internal.wheel_builder.should_cache(
|
|
|
|
req, check_binary_allowed
|
|
|
|
)
|
|
|
|
if not pip._internal.wheel_builder.should_build(
|
2019-10-27 17:52:19 +01:00
|
|
|
req, need_wheel=False, check_binary_allowed=check_binary_allowed
|
|
|
|
):
|
|
|
|
# never cache if pip install (need_wheel=False) would not have built)
|
|
|
|
assert not should_cache
|
|
|
|
assert should_cache is expected
|
|
|
|
|
|
|
|
|
2019-11-02 15:14:39 +01:00
|
|
|
def test_should_cache_git_sha(script, tmpdir):
|
|
|
|
repo_path = _create_test_package(script, name="mypkg")
|
|
|
|
commit = script.run(
|
|
|
|
"git", "rev-parse", "HEAD", cwd=repo_path
|
|
|
|
).stdout.strip()
|
|
|
|
# a link referencing a sha should be cached
|
|
|
|
url = "git+https://g.c/o/r@" + commit + "#egg=mypkg"
|
|
|
|
req = ReqMock(link=Link(url), source_dir=repo_path)
|
|
|
|
assert wheel.should_cache(req, check_binary_allowed=lambda r: True)
|
|
|
|
# a link not referencing a sha should not be cached
|
|
|
|
url = "git+https://g.c/o/r@master#egg=mypkg"
|
|
|
|
req = ReqMock(link=Link(url), source_dir=repo_path)
|
|
|
|
assert not wheel.should_cache(req, check_binary_allowed=lambda r: True)
|
|
|
|
|
|
|
|
|
2019-02-11 17:27:26 +01:00
|
|
|
def test_format_command_result__INFO(caplog):
|
2019-02-12 21:02:37 +01:00
|
|
|
caplog.set_level(logging.INFO)
|
2019-11-03 14:07:30 +01:00
|
|
|
actual = pip._internal.wheel_builder.format_command_result(
|
2019-02-22 09:30:53 +01:00
|
|
|
# Include an argument with a space to test argument quoting.
|
|
|
|
command_args=['arg1', 'second arg'],
|
2019-02-12 21:02:37 +01:00
|
|
|
command_output='output line 1\noutput line 2\n',
|
|
|
|
)
|
|
|
|
assert actual.splitlines() == [
|
2019-02-22 09:30:53 +01:00
|
|
|
"Command arguments: arg1 'second arg'",
|
2019-02-12 21:02:37 +01:00
|
|
|
'Command output: [use --verbose to show]',
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('command_output', [
|
|
|
|
# Test trailing newline.
|
|
|
|
'output line 1\noutput line 2\n',
|
|
|
|
# Test no trailing newline.
|
|
|
|
'output line 1\noutput line 2',
|
|
|
|
])
|
2019-02-11 17:27:26 +01:00
|
|
|
def test_format_command_result__DEBUG(caplog, command_output):
|
2019-02-12 21:02:37 +01:00
|
|
|
caplog.set_level(logging.DEBUG)
|
2019-11-03 14:07:30 +01:00
|
|
|
actual = pip._internal.wheel_builder.format_command_result(
|
2019-02-12 21:02:37 +01:00
|
|
|
command_args=['arg1', 'arg2'],
|
|
|
|
command_output=command_output,
|
|
|
|
)
|
|
|
|
assert actual.splitlines() == [
|
2019-02-22 09:30:53 +01:00
|
|
|
"Command arguments: arg1 arg2",
|
2019-02-12 21:02:37 +01:00
|
|
|
'Command output:',
|
|
|
|
'output line 1',
|
|
|
|
'output line 2',
|
2019-02-11 18:03:32 +01:00
|
|
|
'----------------------------------------',
|
2019-02-12 21:02:37 +01:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('log_level', ['DEBUG', 'INFO'])
|
2019-02-11 17:27:26 +01:00
|
|
|
def test_format_command_result__empty_output(caplog, log_level):
|
2019-02-12 21:02:37 +01:00
|
|
|
caplog.set_level(log_level)
|
2019-11-03 14:07:30 +01:00
|
|
|
actual = pip._internal.wheel_builder.format_command_result(
|
2019-02-12 21:02:37 +01:00
|
|
|
command_args=['arg1', 'arg2'],
|
|
|
|
command_output='',
|
|
|
|
)
|
|
|
|
assert actual.splitlines() == [
|
2019-02-22 09:30:53 +01:00
|
|
|
"Command arguments: arg1 arg2",
|
2019-02-12 21:02:37 +01:00
|
|
|
'Command output: None',
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2019-02-10 21:36:59 +01:00
|
|
|
def call_get_legacy_build_wheel_path(caplog, names):
|
|
|
|
req = make_test_install_req()
|
2019-11-03 14:07:30 +01:00
|
|
|
wheel_path = pip._internal.wheel_builder.get_legacy_build_wheel_path(
|
2019-02-10 21:36:59 +01:00
|
|
|
names=names,
|
|
|
|
temp_dir='/tmp/abcd',
|
|
|
|
req=req,
|
|
|
|
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):
|
2019-09-14 02:51:02 +02:00
|
|
|
caplog.set_level(logging.INFO)
|
2019-02-10 21:36:59 +01:00
|
|
|
actual = call_get_legacy_build_wheel_path(caplog, names=[])
|
|
|
|
assert actual is None
|
|
|
|
assert len(caplog.records) == 1
|
|
|
|
record = caplog.records[0]
|
2019-02-12 21:02:37 +01:00
|
|
|
assert record.levelname == 'WARNING'
|
2019-02-10 21:36:59 +01:00
|
|
|
assert record.message.splitlines() == [
|
2019-02-12 21:02:37 +01:00
|
|
|
"Legacy build of wheel for 'pendulum' created no files.",
|
2019-02-22 09:30:53 +01:00
|
|
|
"Command arguments: arg1 arg2",
|
2019-02-12 21:02:37 +01:00
|
|
|
'Command output: [use --verbose to show]',
|
2019-02-10 21:36:59 +01:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
def test_get_legacy_build_wheel_path__multiple_names(caplog):
|
2019-09-14 02:51:02 +02:00
|
|
|
caplog.set_level(logging.INFO)
|
2019-02-10 21:36:59 +01:00
|
|
|
# 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() == [
|
2019-02-12 21:02:37 +01:00
|
|
|
"Legacy build of wheel for 'pendulum' created more than one file.",
|
|
|
|
"Filenames (choosing first): ['name1', 'name2']",
|
2019-02-22 09:30:53 +01:00
|
|
|
"Command arguments: arg1 arg2",
|
2019-02-12 21:02:37 +01:00
|
|
|
'Command output: [use --verbose to show]',
|
2019-02-10 21:36:59 +01:00
|
|
|
]
|
|
|
|
|
|
|
|
|
2016-11-19 00:27:16 +01:00
|
|
|
@pytest.mark.parametrize("console_scripts",
|
2017-08-31 17:48:18 +02:00
|
|
|
["pip = pip._internal.main:pip",
|
|
|
|
"pip:pip = pip._internal.main:pip"])
|
2016-11-19 00:27:16 +01:00
|
|
|
def test_get_entrypoints(tmpdir, console_scripts):
|
2019-07-02 07:00:32 +02:00
|
|
|
entry_points = tmpdir.joinpath("entry_points.txt")
|
2016-11-19 00:27:16 +01:00
|
|
|
with open(str(entry_points), "w") as fp:
|
2013-11-19 14:32:58 +01:00
|
|
|
fp.write("""
|
|
|
|
[console_scripts]
|
2017-12-15 06:56:04 +01:00
|
|
|
{}
|
2016-11-19 00:27:16 +01:00
|
|
|
[section]
|
|
|
|
common:one = module:func
|
|
|
|
common:two = module:other_func
|
|
|
|
""".format(console_scripts))
|
2013-11-19 14:32:58 +01:00
|
|
|
|
2016-11-19 00:27:16 +01:00
|
|
|
assert wheel.get_entrypoints(str(entry_points)) == (
|
|
|
|
dict([console_scripts.split(' = ')]),
|
2013-11-19 14:32:58 +01:00
|
|
|
{},
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2019-09-07 15:59:41 +02:00
|
|
|
def test_raise_for_invalid_entrypoint_ok():
|
|
|
|
_raise_for_invalid_entrypoint("hello = hello:main")
|
2019-07-22 04:49:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("entrypoint", [
|
|
|
|
"hello = hello",
|
|
|
|
"hello = hello:",
|
|
|
|
])
|
2019-09-07 15:59:41 +02:00
|
|
|
def test_raise_for_invalid_entrypoint_fail(entrypoint):
|
2019-07-22 04:49:51 +02:00
|
|
|
with pytest.raises(MissingCallableSuffix):
|
2019-09-07 15:59:41 +02:00
|
|
|
_raise_for_invalid_entrypoint(entrypoint)
|
2019-07-22 04:49:51 +02:00
|
|
|
|
|
|
|
|
2018-10-24 18:19:58 +02:00
|
|
|
@pytest.mark.parametrize("outrows, expected", [
|
|
|
|
([
|
|
|
|
('', '', 'a'),
|
|
|
|
('', '', ''),
|
|
|
|
], [
|
|
|
|
('', '', ''),
|
|
|
|
('', '', 'a'),
|
|
|
|
]),
|
|
|
|
([
|
|
|
|
# Include an int to check avoiding the following error:
|
|
|
|
# > TypeError: '<' not supported between instances of 'str' and 'int'
|
|
|
|
('', '', 1),
|
|
|
|
('', '', ''),
|
|
|
|
], [
|
|
|
|
('', '', ''),
|
|
|
|
('', '', 1),
|
|
|
|
]),
|
|
|
|
])
|
|
|
|
def test_sorted_outrows(outrows, expected):
|
|
|
|
actual = wheel.sorted_outrows(outrows)
|
|
|
|
assert actual == expected
|
|
|
|
|
|
|
|
|
2019-01-24 03:44:54 +01:00
|
|
|
def call_get_csv_rows_for_installed(tmpdir, text):
|
2019-07-02 07:00:32 +02:00
|
|
|
path = tmpdir.joinpath('temp.txt')
|
|
|
|
path.write_text(text)
|
2019-01-24 03:44:54 +01:00
|
|
|
|
2019-02-15 11:26:18 +01:00
|
|
|
# Test that an installed file appearing in RECORD has its filename
|
|
|
|
# updated in the new RECORD file.
|
|
|
|
installed = {'a': 'z'}
|
2019-01-24 03:44:54 +01:00
|
|
|
changed = set()
|
|
|
|
generated = []
|
|
|
|
lib_dir = '/lib/dir'
|
|
|
|
|
|
|
|
with wheel.open_for_csv(path, 'r') as f:
|
|
|
|
reader = csv.reader(f)
|
|
|
|
outrows = wheel.get_csv_rows_for_installed(
|
|
|
|
reader, installed=installed, changed=changed,
|
|
|
|
generated=generated, lib_dir=lib_dir,
|
|
|
|
)
|
|
|
|
return outrows
|
|
|
|
|
|
|
|
|
2019-01-24 04:16:10 +01:00
|
|
|
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 = [
|
2019-02-15 11:26:18 +01:00
|
|
|
('z', 'b', 'c'),
|
2019-01-24 03:44:54 +01:00
|
|
|
('d', 'e', 'f'),
|
|
|
|
]
|
|
|
|
assert outrows == expected
|
2019-01-24 04:16:10 +01:00
|
|
|
# Check there were no warnings.
|
|
|
|
assert len(caplog.records) == 0
|
2019-01-24 03:44:54 +01:00
|
|
|
|
|
|
|
|
2019-01-24 04:16:10 +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
|
2019-01-24 04:16:10 +01:00
|
|
|
e,f,g
|
|
|
|
h,i,j,k
|
2019-01-24 03:44:54 +01:00
|
|
|
""")
|
|
|
|
outrows = call_get_csv_rows_for_installed(tmpdir, text)
|
|
|
|
|
2019-01-24 04:16:10 +01:00
|
|
|
expected = [
|
2019-02-15 11:26:18 +01:00
|
|
|
('z', 'b', 'c', 'd'),
|
2019-01-24 04:16:10 +01:00
|
|
|
('e', 'f', 'g'),
|
|
|
|
('h', 'i', 'j', 'k'),
|
|
|
|
]
|
|
|
|
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
|
|
|
|
2014-02-18 05:20:12 +01:00
|
|
|
def test_wheel_version(tmpdir, data):
|
2014-02-15 07:21:22 +01:00
|
|
|
future_wheel = 'futurewheel-1.9-py2.py3-none-any.whl'
|
|
|
|
broken_wheel = 'brokenwheel-1.0-py2.py3-none-any.whl'
|
|
|
|
future_version = (1, 9)
|
|
|
|
|
2019-09-20 02:02:47 +02:00
|
|
|
unpack_file(data.packages.joinpath(future_wheel), tmpdir + 'future')
|
|
|
|
unpack_file(data.packages.joinpath(broken_wheel), tmpdir + 'broken')
|
2014-02-15 07:21:22 +01:00
|
|
|
|
|
|
|
assert wheel.wheel_version(tmpdir + 'future') == future_version
|
|
|
|
assert not wheel.wheel_version(tmpdir + 'broken')
|
|
|
|
|
|
|
|
|
2018-10-16 14:53:34 +02:00
|
|
|
def test_python_tag():
|
|
|
|
wheelnames = [
|
|
|
|
'simplewheel-1.0-py2.py3-none-any.whl',
|
|
|
|
'simplewheel-1.0-py27-none-any.whl',
|
|
|
|
'simplewheel-2.0-1-py2.py3-none-any.whl',
|
|
|
|
]
|
|
|
|
newnames = [
|
|
|
|
'simplewheel-1.0-py37-none-any.whl',
|
|
|
|
'simplewheel-1.0-py37-none-any.whl',
|
|
|
|
'simplewheel-2.0-1-py37-none-any.whl',
|
|
|
|
]
|
2019-11-03 14:07:30 +01:00
|
|
|
for name, expected in zip(wheelnames, newnames):
|
|
|
|
result = pip._internal.wheel_builder.replace_python_tag(name, 'py37')
|
|
|
|
assert result == expected
|
2018-10-16 14:53:34 +02:00
|
|
|
|
|
|
|
|
2014-02-18 05:20:12 +01:00
|
|
|
def test_check_compatibility():
|
|
|
|
name = 'test'
|
2014-02-19 00:31:12 +01:00
|
|
|
vc = wheel.VERSION_COMPATIBLE
|
2014-02-18 05:20:12 +01:00
|
|
|
|
2014-02-19 00:31:12 +01:00
|
|
|
# Major version is higher - should be incompatible
|
|
|
|
higher_v = (vc[0] + 1, vc[1])
|
2014-02-18 05:20:12 +01:00
|
|
|
|
|
|
|
# test raises with correct error
|
2014-02-19 08:32:41 +01:00
|
|
|
with pytest.raises(UnsupportedWheel) as e:
|
2014-02-18 05:20:12 +01:00
|
|
|
wheel.check_compatibility(higher_v, name)
|
2014-02-19 08:32:41 +01:00
|
|
|
assert 'is not compatible' in str(e)
|
2014-02-18 05:20:12 +01:00
|
|
|
|
2016-04-15 15:30:28 +02:00
|
|
|
# Should only log.warning - minor version is greater
|
2014-02-19 00:31:12 +01:00
|
|
|
higher_v = (vc[0], vc[1] + 1)
|
|
|
|
wheel.check_compatibility(higher_v, name)
|
2014-02-18 05:20:12 +01:00
|
|
|
|
|
|
|
# These should work fine
|
|
|
|
wheel.check_compatibility(wheel.VERSION_COMPATIBLE, name)
|
|
|
|
|
2014-02-19 00:31:12 +01:00
|
|
|
# E.g if wheel to install is 1.0 and we support up to 1.2
|
|
|
|
lower_v = (vc[0], max(0, vc[1] - 1))
|
2014-02-18 05:20:12 +01:00
|
|
|
wheel.check_compatibility(lower_v, name)
|
|
|
|
|
|
|
|
|
2013-05-28 23:58:08 +02:00
|
|
|
class TestWheelFile(object):
|
|
|
|
|
2014-01-16 05:42:14 +01:00
|
|
|
def test_std_wheel_pattern(self):
|
|
|
|
w = wheel.Wheel('simple-1.1.1-py2-none-any.whl')
|
|
|
|
assert w.name == 'simple'
|
|
|
|
assert w.version == '1.1.1'
|
|
|
|
assert w.pyversions == ['py2']
|
|
|
|
assert w.abis == ['none']
|
|
|
|
assert w.plats == ['any']
|
|
|
|
|
|
|
|
def test_wheel_pattern_multi_values(self):
|
|
|
|
w = wheel.Wheel('simple-1.1-py2.py3-abi1.abi2-any.whl')
|
|
|
|
assert w.name == 'simple'
|
|
|
|
assert w.version == '1.1'
|
|
|
|
assert w.pyversions == ['py2', 'py3']
|
|
|
|
assert w.abis == ['abi1', 'abi2']
|
|
|
|
assert w.plats == ['any']
|
|
|
|
|
|
|
|
def test_wheel_with_build_tag(self):
|
|
|
|
# pip doesn't do anything with build tags, but theoretically, we might
|
|
|
|
# see one, in this case the build tag = '4'
|
|
|
|
w = wheel.Wheel('simple-1.1-4-py2-none-any.whl')
|
|
|
|
assert w.name == 'simple'
|
|
|
|
assert w.version == '1.1'
|
|
|
|
assert w.pyversions == ['py2']
|
|
|
|
assert w.abis == ['none']
|
|
|
|
assert w.plats == ['any']
|
|
|
|
|
|
|
|
def test_single_digit_version(self):
|
|
|
|
w = wheel.Wheel('simple-1-py2-none-any.whl')
|
|
|
|
assert w.version == '1'
|
|
|
|
|
2017-03-28 04:49:21 +02:00
|
|
|
def test_non_pep440_version(self):
|
|
|
|
w = wheel.Wheel('simple-_invalid_-py2-none-any.whl')
|
|
|
|
assert w.version == '-invalid-'
|
|
|
|
|
2014-01-16 05:42:14 +01:00
|
|
|
def test_missing_version_raises(self):
|
|
|
|
with pytest.raises(InvalidWheelFilename):
|
|
|
|
wheel.Wheel('Cython-cp27-none-linux_x86_64.whl')
|
|
|
|
|
2014-01-16 00:21:04 +01:00
|
|
|
def test_invalid_filename_raises(self):
|
2013-11-15 01:35:24 +01:00
|
|
|
with pytest.raises(InvalidWheelFilename):
|
2014-01-28 15:17:51 +01:00
|
|
|
wheel.Wheel('invalid.whl')
|
2013-11-15 01:35:24 +01:00
|
|
|
|
2013-05-28 23:58:08 +02:00
|
|
|
def test_supported_single_version(self):
|
|
|
|
"""
|
|
|
|
Test single-version wheel is known to be supported
|
|
|
|
"""
|
|
|
|
w = wheel.Wheel('simple-0.1-py2-none-any.whl')
|
2013-06-30 20:54:45 +02:00
|
|
|
assert w.supported(tags=[('py2', 'none', 'any')])
|
2013-05-28 23:58:08 +02:00
|
|
|
|
|
|
|
def test_supported_multi_version(self):
|
|
|
|
"""
|
|
|
|
Test multi-version wheel is known to be supported
|
|
|
|
"""
|
|
|
|
w = wheel.Wheel('simple-0.1-py2.py3-none-any.whl')
|
2013-06-30 20:54:45 +02:00
|
|
|
assert w.supported(tags=[('py3', 'none', 'any')])
|
2013-05-28 23:58:08 +02:00
|
|
|
|
|
|
|
def test_not_supported_version(self):
|
|
|
|
"""
|
|
|
|
Test unsupported wheel is known to be unsupported
|
|
|
|
"""
|
|
|
|
w = wheel.Wheel('simple-0.1-py2-none-any.whl')
|
2013-06-30 20:54:45 +02:00
|
|
|
assert not w.supported(tags=[('py1', 'none', 'any')])
|
2013-05-28 23:58:08 +02:00
|
|
|
|
2014-02-24 08:24:48 +01:00
|
|
|
@patch('sys.platform', 'darwin')
|
2017-08-31 17:48:18 +02:00
|
|
|
@patch('pip._internal.pep425tags.get_abbr_impl', lambda: 'cp')
|
|
|
|
@patch('pip._internal.pep425tags.get_platform',
|
|
|
|
lambda: 'macosx_10_9_intel')
|
2014-02-24 08:24:48 +01:00
|
|
|
def test_supported_osx_version(self):
|
2014-01-12 22:22:36 +01:00
|
|
|
"""
|
2016-11-06 18:24:43 +01:00
|
|
|
Wheels built for macOS 10.6 are supported on 10.9
|
2014-01-12 22:22:36 +01:00
|
|
|
"""
|
2014-02-24 08:24:48 +01:00
|
|
|
tags = pep425tags.get_supported(['27'], False)
|
2014-01-12 22:22:36 +01:00
|
|
|
w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_6_intel.whl')
|
|
|
|
assert w.supported(tags=tags)
|
2014-02-24 08:24:48 +01:00
|
|
|
w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_9_intel.whl')
|
|
|
|
assert w.supported(tags=tags)
|
2014-01-12 22:22:36 +01:00
|
|
|
|
2014-02-24 08:24:48 +01:00
|
|
|
@patch('sys.platform', 'darwin')
|
2017-08-31 17:48:18 +02:00
|
|
|
@patch('pip._internal.pep425tags.get_abbr_impl', lambda: 'cp')
|
|
|
|
@patch('pip._internal.pep425tags.get_platform',
|
|
|
|
lambda: 'macosx_10_6_intel')
|
2014-02-24 08:24:48 +01:00
|
|
|
def test_not_supported_osx_version(self):
|
2014-01-12 22:22:36 +01:00
|
|
|
"""
|
2016-11-06 18:24:43 +01:00
|
|
|
Wheels built for macOS 10.9 are not supported on 10.6
|
2014-01-12 22:22:36 +01:00
|
|
|
"""
|
2014-02-24 08:24:48 +01:00
|
|
|
tags = pep425tags.get_supported(['27'], False)
|
2014-01-12 22:22:36 +01:00
|
|
|
w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_9_intel.whl')
|
|
|
|
assert not w.supported(tags=tags)
|
|
|
|
|
2014-02-24 08:24:48 +01:00
|
|
|
@patch('sys.platform', 'darwin')
|
2017-08-31 17:48:18 +02:00
|
|
|
@patch('pip._internal.pep425tags.get_abbr_impl', lambda: 'cp')
|
2014-02-24 08:24:48 +01:00
|
|
|
def test_supported_multiarch_darwin(self):
|
|
|
|
"""
|
|
|
|
Multi-arch wheels (intel) are supported on components (i386, x86_64)
|
|
|
|
"""
|
2017-08-31 17:48:18 +02:00
|
|
|
with patch('pip._internal.pep425tags.get_platform',
|
2014-02-24 08:24:48 +01:00
|
|
|
lambda: 'macosx_10_5_universal'):
|
|
|
|
universal = pep425tags.get_supported(['27'], False)
|
2017-08-31 17:48:18 +02:00
|
|
|
with patch('pip._internal.pep425tags.get_platform',
|
2014-02-24 08:24:48 +01:00
|
|
|
lambda: 'macosx_10_5_intel'):
|
|
|
|
intel = pep425tags.get_supported(['27'], False)
|
2017-08-31 17:48:18 +02:00
|
|
|
with patch('pip._internal.pep425tags.get_platform',
|
2014-02-24 08:24:48 +01:00
|
|
|
lambda: 'macosx_10_5_x86_64'):
|
|
|
|
x64 = pep425tags.get_supported(['27'], False)
|
2017-08-31 17:48:18 +02:00
|
|
|
with patch('pip._internal.pep425tags.get_platform',
|
2014-02-24 08:24:48 +01:00
|
|
|
lambda: 'macosx_10_5_i386'):
|
|
|
|
i386 = pep425tags.get_supported(['27'], False)
|
2017-08-31 17:48:18 +02:00
|
|
|
with patch('pip._internal.pep425tags.get_platform',
|
2014-02-24 08:24:48 +01:00
|
|
|
lambda: 'macosx_10_5_ppc'):
|
|
|
|
ppc = pep425tags.get_supported(['27'], False)
|
2017-08-31 17:48:18 +02:00
|
|
|
with patch('pip._internal.pep425tags.get_platform',
|
2014-02-24 08:24:48 +01:00
|
|
|
lambda: 'macosx_10_5_ppc64'):
|
|
|
|
ppc64 = pep425tags.get_supported(['27'], False)
|
|
|
|
|
|
|
|
w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_5_intel.whl')
|
|
|
|
assert w.supported(tags=intel)
|
|
|
|
assert w.supported(tags=x64)
|
|
|
|
assert w.supported(tags=i386)
|
|
|
|
assert not w.supported(tags=universal)
|
|
|
|
assert not w.supported(tags=ppc)
|
|
|
|
assert not w.supported(tags=ppc64)
|
|
|
|
w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_5_universal.whl')
|
|
|
|
assert w.supported(tags=universal)
|
|
|
|
assert w.supported(tags=intel)
|
|
|
|
assert w.supported(tags=x64)
|
|
|
|
assert w.supported(tags=i386)
|
|
|
|
assert w.supported(tags=ppc)
|
|
|
|
assert w.supported(tags=ppc64)
|
|
|
|
|
|
|
|
@patch('sys.platform', 'darwin')
|
2017-08-31 17:48:18 +02:00
|
|
|
@patch('pip._internal.pep425tags.get_abbr_impl', lambda: 'cp')
|
2014-02-24 08:24:48 +01:00
|
|
|
def test_not_supported_multiarch_darwin(self):
|
|
|
|
"""
|
|
|
|
Single-arch wheels (x86_64) are not supported on multi-arch (intel)
|
|
|
|
"""
|
2017-08-31 17:48:18 +02:00
|
|
|
with patch('pip._internal.pep425tags.get_platform',
|
2014-02-24 08:24:48 +01:00
|
|
|
lambda: 'macosx_10_5_universal'):
|
|
|
|
universal = pep425tags.get_supported(['27'], False)
|
2017-08-31 17:48:18 +02:00
|
|
|
with patch('pip._internal.pep425tags.get_platform',
|
2014-02-24 08:24:48 +01:00
|
|
|
lambda: 'macosx_10_5_intel'):
|
|
|
|
intel = pep425tags.get_supported(['27'], False)
|
|
|
|
|
|
|
|
w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_5_i386.whl')
|
|
|
|
assert not w.supported(tags=intel)
|
|
|
|
assert not w.supported(tags=universal)
|
|
|
|
w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_5_x86_64.whl')
|
|
|
|
assert not w.supported(tags=intel)
|
|
|
|
assert not w.supported(tags=universal)
|
|
|
|
|
2013-05-28 23:58:08 +02:00
|
|
|
def test_support_index_min(self):
|
|
|
|
"""
|
|
|
|
Test results from `support_index_min`
|
|
|
|
"""
|
2013-06-30 20:54:45 +02:00
|
|
|
tags = [
|
2014-01-28 15:17:51 +01:00
|
|
|
('py2', 'none', 'TEST'),
|
|
|
|
('py2', 'TEST', 'any'),
|
|
|
|
('py2', 'none', 'any'),
|
2013-06-30 20:54:45 +02:00
|
|
|
]
|
2013-05-28 23:58:08 +02:00
|
|
|
w = wheel.Wheel('simple-0.1-py2-none-any.whl')
|
2013-06-30 20:56:43 +02:00
|
|
|
assert w.support_index_min(tags=tags) == 2
|
2013-05-28 23:58:08 +02:00
|
|
|
w = wheel.Wheel('simple-0.1-py2-none-TEST.whl')
|
2013-06-30 20:56:43 +02:00
|
|
|
assert w.support_index_min(tags=tags) == 0
|
2013-05-28 23:58:08 +02:00
|
|
|
|
2019-08-09 07:58:17 +02:00
|
|
|
def test_support_index_min__none_supported(self):
|
2013-05-28 23:58:08 +02:00
|
|
|
"""
|
2019-08-09 07:58:17 +02:00
|
|
|
Test a wheel not supported by the given tags.
|
2013-05-28 23:58:08 +02:00
|
|
|
"""
|
|
|
|
w = wheel.Wheel('simple-0.1-py2-none-any.whl')
|
2019-08-09 07:58:17 +02:00
|
|
|
with pytest.raises(ValueError):
|
|
|
|
w.support_index_min(tags=[])
|
2013-05-28 23:58:08 +02:00
|
|
|
|
2019-09-18 02:18:52 +02:00
|
|
|
def test_unpack_wheel_no_flatten(self, tmpdir):
|
2017-08-31 19:17:56 +02:00
|
|
|
filepath = os.path.join(DATA_DIR, 'packages',
|
|
|
|
'meta-1.0-py2.py3-none-any.whl')
|
2019-09-20 02:02:47 +02:00
|
|
|
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'))
|
2013-07-28 05:48:15 +02:00
|
|
|
|
2013-08-23 13:12:37 +02:00
|
|
|
def test_purelib_platlib(self, data):
|
2013-07-01 05:40:20 +02:00
|
|
|
"""
|
|
|
|
Test the "wheel is purelib/platlib" code.
|
|
|
|
"""
|
2013-08-23 13:12:37 +02:00
|
|
|
packages = [
|
2019-07-02 07:00:32 +02:00
|
|
|
("pure_wheel", data.packages.joinpath("pure_wheel-1.7"), True),
|
|
|
|
("plat_wheel", data.packages.joinpath("plat_wheel-1.7"), False),
|
|
|
|
("pure_wheel", data.packages.joinpath(
|
2017-03-28 04:49:21 +02:00
|
|
|
"pure_wheel-_invalidversion_"), True),
|
2019-07-02 07:00:32 +02:00
|
|
|
("plat_wheel", data.packages.joinpath(
|
2017-03-28 04:49:21 +02:00
|
|
|
"plat_wheel-_invalidversion_"), False),
|
2013-08-23 13:12:37 +02:00
|
|
|
]
|
|
|
|
|
2013-07-01 05:40:20 +02:00
|
|
|
for name, path, expected in packages:
|
|
|
|
assert wheel.root_is_purelib(name, path) == expected
|
2013-07-24 02:18:26 +02:00
|
|
|
|
2013-08-22 08:30:15 +02:00
|
|
|
def test_version_underscore_conversion(self):
|
|
|
|
"""
|
2014-01-28 15:17:51 +01:00
|
|
|
Test that we convert '_' to '-' for versions parsed out of wheel
|
|
|
|
filenames
|
2013-08-22 08:30:15 +02:00
|
|
|
"""
|
|
|
|
w = wheel.Wheel('simple-0.1_1-py2-none-any.whl')
|
|
|
|
assert w.version == '0.1-1'
|
|
|
|
|
|
|
|
|
2019-10-12 03:31:35 +02:00
|
|
|
class TestInstallUnpackedWheel(object):
|
2014-04-22 08:07:25 +02:00
|
|
|
"""
|
|
|
|
Tests for moving files from wheel src to scheme paths
|
|
|
|
"""
|
|
|
|
|
|
|
|
def prep(self, data, tmpdir):
|
|
|
|
self.name = 'sample'
|
2019-07-02 07:00:32 +02:00
|
|
|
self.wheelpath = data.packages.joinpath(
|
2014-04-25 23:42:14 +02:00
|
|
|
'sample-1.2.0-py2.py3-none-any.whl')
|
2015-11-12 00:51:46 +01:00
|
|
|
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')
|
2019-09-20 02:02:47 +02:00
|
|
|
unpack_file(self.wheelpath, self.src)
|
2014-04-22 08:07:25 +02:00
|
|
|
self.scheme = {
|
|
|
|
'scripts': os.path.join(self.dest, 'bin'),
|
|
|
|
'purelib': os.path.join(self.dest, 'lib'),
|
|
|
|
'data': os.path.join(self.dest, 'data'),
|
2014-04-25 23:42:14 +02:00
|
|
|
}
|
2014-04-22 08:07:25 +02:00
|
|
|
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_installed(self):
|
|
|
|
# lib
|
|
|
|
assert os.path.isdir(
|
|
|
|
os.path.join(self.scheme['purelib'], 'sample'))
|
|
|
|
# dist-info
|
|
|
|
metadata = os.path.join(self.dest_dist_info, 'METADATA')
|
|
|
|
assert os.path.isfile(metadata)
|
|
|
|
# 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')
|
|
|
|
assert os.path.isfile(pkg_data)
|
2014-04-22 08:07:25 +02:00
|
|
|
|
|
|
|
def test_std_install(self, data, tmpdir):
|
|
|
|
self.prep(data, tmpdir)
|
2019-10-12 03:31:35 +02:00
|
|
|
wheel.install_unpacked_wheel(
|
2019-10-12 04:24:50 +02:00
|
|
|
self.name,
|
|
|
|
self.src,
|
|
|
|
scheme=self.scheme,
|
|
|
|
req_description=str(self.req),
|
|
|
|
)
|
2014-04-22 08:07:25 +02:00
|
|
|
self.assert_installed()
|
|
|
|
|
2015-11-16 17:39:44 +01:00
|
|
|
def test_install_prefix(self, data, tmpdir):
|
|
|
|
prefix = os.path.join(os.path.sep, 'some', 'path')
|
|
|
|
self.prep(data, tmpdir)
|
2019-10-12 03:49:39 +02:00
|
|
|
scheme = distutils_scheme(
|
2015-11-16 17:39:44 +01:00
|
|
|
self.name,
|
2019-10-12 03:49:39 +02:00
|
|
|
user=False,
|
|
|
|
home=None,
|
2015-11-16 17:39:44 +01:00
|
|
|
root=tmpdir,
|
2019-10-12 03:49:39 +02:00
|
|
|
isolated=False,
|
2015-11-16 17:39:44 +01:00
|
|
|
prefix=prefix,
|
|
|
|
)
|
2019-10-12 03:31:35 +02:00
|
|
|
wheel.install_unpacked_wheel(
|
2015-11-16 17:39:44 +01:00
|
|
|
self.name,
|
|
|
|
self.src,
|
2019-10-12 03:49:39 +02:00
|
|
|
scheme=scheme,
|
2019-10-12 04:24:50 +02:00
|
|
|
req_description=str(self.req),
|
2015-11-16 17:39:44 +01:00
|
|
|
)
|
|
|
|
|
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))
|
2015-11-16 17:39:44 +01:00
|
|
|
assert os.path.exists(os.path.join(tmpdir, 'some', 'path', 'my_data'))
|
|
|
|
|
2014-04-22 08:07:25 +02:00
|
|
|
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)
|
2014-04-25 23:42:14 +02:00
|
|
|
src_empty_dir = os.path.join(
|
|
|
|
self.src_dist_info, 'empty_dir', 'empty_dir')
|
2014-04-22 08:07:25 +02:00
|
|
|
os.makedirs(src_empty_dir)
|
|
|
|
assert os.path.isdir(src_empty_dir)
|
2019-10-12 03:31:35 +02:00
|
|
|
wheel.install_unpacked_wheel(
|
2019-10-12 04:24:50 +02:00
|
|
|
self.name,
|
|
|
|
self.src,
|
|
|
|
scheme=self.scheme,
|
|
|
|
req_description=str(self.req),
|
|
|
|
)
|
2014-04-22 08:07:25 +02:00
|
|
|
self.assert_installed()
|
2014-04-25 23:42:14 +02:00
|
|
|
assert not os.path.isdir(
|
|
|
|
os.path.join(self.dest_dist_info, 'empty_dir'))
|
2014-05-03 19:02:23 +02:00
|
|
|
|
|
|
|
|
|
|
|
class TestWheelBuilder(object):
|
|
|
|
|
2014-08-31 01:52:28 +02:00
|
|
|
def test_skip_building_wheels(self, caplog):
|
2017-08-31 17:48:18 +02:00
|
|
|
with patch('pip._internal.wheel.WheelBuilder._build_one') \
|
|
|
|
as mock_build_one:
|
2015-06-02 05:39:10 +02:00
|
|
|
wheel_req = Mock(is_wheel=True, editable=False, constraint=False)
|
2019-11-03 14:07:30 +01:00
|
|
|
wb = pip._internal.wheel_builder.WheelBuilder(
|
2019-08-13 14:14:23 +02:00
|
|
|
preparer=Mock(),
|
|
|
|
wheel_cache=Mock(cache_dir=None),
|
2017-11-16 10:46:21 +01:00
|
|
|
)
|
2018-06-23 23:07:39 +02:00
|
|
|
with caplog.at_level(logging.INFO):
|
2019-08-11 13:15:49 +02:00
|
|
|
wb.build([wheel_req])
|
2017-03-20 16:45:29 +01:00
|
|
|
assert "due to already being wheel" in caplog.text
|
2014-08-31 01:52:28 +02:00
|
|
|
assert mock_build_one.mock_calls == []
|
2017-10-02 18:54:37 +02:00
|
|
|
|
|
|
|
|
|
|
|
class TestMessageAboutScriptsNotOnPATH(object):
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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
|
2018-04-07 13:55:41 +02:00
|
|
|
|
|
|
|
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
|
2018-04-23 17:51:43 +02:00
|
|
|
|
|
|
|
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
|
2018-07-02 14:14:28 +02:00
|
|
|
|
|
|
|
def test_missing_PATH_env_treated_as_empty_PATH_env(self):
|
|
|
|
scripts = ['a/b/foo']
|
|
|
|
|
|
|
|
env = os.environ.copy()
|
|
|
|
del env['PATH']
|
|
|
|
with patch.dict('os.environ', env, clear=True):
|
|
|
|
retval_missing = wheel.message_about_scripts_not_on_PATH(scripts)
|
|
|
|
|
|
|
|
with patch.dict('os.environ', {'PATH': ''}):
|
|
|
|
retval_empty = wheel.message_about_scripts_not_on_PATH(scripts)
|
|
|
|
|
|
|
|
assert retval_missing == retval_empty
|
2019-06-26 11:44:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
class TestWheelHashCalculators(object):
|
|
|
|
|
|
|
|
def prep(self, tmpdir):
|
2019-07-02 07:00:32 +02:00
|
|
|
self.test_file = tmpdir.joinpath("hash.file")
|
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)
|
2019-11-03 14:24:11 +01:00
|
|
|
h, length = hash_file(self.test_file)
|
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
|
2019-09-25 13:37:53 +02:00
|
|
|
|
|
|
|
|
|
|
|
class TestWheelCommand(object):
|
|
|
|
|
|
|
|
def test_save_wheelnames(self, tmpdir):
|
|
|
|
wheel_filenames = ['Flask-1.1.dev0-py2.py3-none-any.whl']
|
|
|
|
links_filenames = [
|
|
|
|
'flask',
|
|
|
|
'Werkzeug-0.15.4-py2.py3-none-any.whl',
|
|
|
|
'Jinja2-2.10.1-py2.py3-none-any.whl',
|
|
|
|
'itsdangerous-1.1.0-py2.py3-none-any.whl',
|
|
|
|
'Click-7.0-py2.py3-none-any.whl'
|
|
|
|
]
|
|
|
|
|
|
|
|
expected = wheel_filenames + links_filenames[1:]
|
|
|
|
expected = [filename + '\n' for filename in expected]
|
|
|
|
temp_file = tmpdir.joinpath('wheelfiles')
|
|
|
|
|
|
|
|
WheelCommand('name', 'summary').save_wheelnames(
|
|
|
|
links_filenames,
|
|
|
|
temp_file,
|
|
|
|
wheel_filenames
|
|
|
|
)
|
|
|
|
|
|
|
|
with open(temp_file, 'r') as f:
|
|
|
|
test_content = f.readlines()
|
|
|
|
assert test_content == expected
|