mirror of https://github.com/pypa/hatch.git
291 lines
10 KiB
Python
291 lines
10 KiB
Python
import json
|
|
import secrets
|
|
|
|
import pytest
|
|
|
|
from hatch.errors import PythonDistributionResolutionError
|
|
from hatch.python.core import InstalledDistribution
|
|
from hatch.python.distributions import ORDERED_DISTRIBUTIONS
|
|
from hatch.python.resolve import get_distribution
|
|
|
|
|
|
def test_unknown(hatch, helpers, path_append, mocker):
|
|
install = mocker.patch('hatch.python.core.PythonManager.install')
|
|
|
|
result = hatch('python', 'install', 'foo', 'bar')
|
|
|
|
assert result.exit_code == 1, result.output
|
|
assert result.output == helpers.dedent(
|
|
"""
|
|
Unknown distributions: foo, bar
|
|
"""
|
|
)
|
|
|
|
install.assert_not_called()
|
|
path_append.assert_not_called()
|
|
|
|
|
|
def test_incompatible_single(hatch, helpers, path_append, dist_name, mocker):
|
|
mocker.patch('hatch.python.resolve.get_distribution', side_effect=PythonDistributionResolutionError)
|
|
install = mocker.patch('hatch.python.core.PythonManager.install')
|
|
|
|
result = hatch('python', 'install', dist_name)
|
|
|
|
assert result.exit_code == 1, result.output
|
|
assert result.output == helpers.dedent(
|
|
f"""
|
|
Incompatible distributions: {dist_name}
|
|
"""
|
|
)
|
|
|
|
install.assert_not_called()
|
|
path_append.assert_not_called()
|
|
|
|
|
|
def test_incompatible_all(hatch, helpers, path_append, mocker):
|
|
mocker.patch('hatch.python.resolve.get_distribution', side_effect=PythonDistributionResolutionError)
|
|
install = mocker.patch('hatch.python.core.PythonManager.install')
|
|
|
|
result = hatch('python', 'install', 'all')
|
|
|
|
assert result.exit_code == 1, result.output
|
|
assert result.output == helpers.dedent(
|
|
f"""
|
|
Incompatible distributions: {', '.join(ORDERED_DISTRIBUTIONS)}
|
|
"""
|
|
)
|
|
|
|
install.assert_not_called()
|
|
path_append.assert_not_called()
|
|
|
|
|
|
@pytest.mark.requires_internet
|
|
def test_installation(
|
|
hatch, helpers, temp_dir_data, platform, path_append, default_shells, compatible_python_distributions
|
|
):
|
|
selection = [name for name in compatible_python_distributions if not name.startswith('pypy')]
|
|
dist_name = secrets.choice(selection)
|
|
result = hatch('python', 'install', dist_name)
|
|
|
|
install_dir = temp_dir_data / 'data' / 'pythons' / dist_name
|
|
metadata_file = install_dir / InstalledDistribution.metadata_filename()
|
|
python_path = install_dir / json.loads(metadata_file.read_text())['python_path']
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert result.output == helpers.dedent(
|
|
f"""
|
|
Installing {dist_name}
|
|
Installed {dist_name} @ {install_dir}
|
|
|
|
The following directory has been added to your PATH (pending a shell restart):
|
|
|
|
{python_path.parent}
|
|
"""
|
|
)
|
|
|
|
assert python_path.is_file()
|
|
|
|
output = platform.check_command_output([python_path, '-c', 'import sys;print(sys.executable)']).strip()
|
|
assert output == str(python_path)
|
|
|
|
output = platform.check_command_output([python_path, '--version']).strip()
|
|
assert output.startswith(f'Python {dist_name}.')
|
|
|
|
path_append.assert_called_once_with(str(python_path.parent), shells=default_shells)
|
|
|
|
|
|
def test_already_installed_latest(hatch, helpers, temp_dir_data, path_append, dist_name, mocker):
|
|
install = mocker.patch('hatch.python.core.PythonManager.install')
|
|
install_dir = temp_dir_data / 'data' / 'pythons'
|
|
installed_dist = helpers.write_distribution(install_dir, dist_name)
|
|
|
|
result = hatch('python', 'install', dist_name)
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert result.output == helpers.dedent(
|
|
f"""
|
|
The latest version is already installed: {installed_dist.version}
|
|
"""
|
|
)
|
|
|
|
install.assert_not_called()
|
|
path_append.assert_not_called()
|
|
|
|
|
|
def test_already_installed_update_disabled(hatch, helpers, temp_dir_data, path_append, dist_name, mocker):
|
|
install = mocker.patch('hatch.python.core.PythonManager.install')
|
|
install_dir = temp_dir_data / 'data' / 'pythons'
|
|
helpers.write_distribution(install_dir, dist_name)
|
|
helpers.downgrade_distribution_metadata(install_dir / dist_name)
|
|
|
|
result = hatch('python', 'install', dist_name, input='n\n')
|
|
|
|
assert result.exit_code == 1, result.output
|
|
assert result.output == helpers.dedent(
|
|
f"""
|
|
Update {dist_name}? [y/N]: n
|
|
Distribution is already installed: {dist_name}
|
|
"""
|
|
)
|
|
|
|
install.assert_not_called()
|
|
path_append.assert_not_called()
|
|
|
|
|
|
def test_already_installed_update_prompt(hatch, helpers, temp_dir_data, path_append, default_shells, dist_name, mocker):
|
|
install_dir = temp_dir_data / 'data' / 'pythons'
|
|
helpers.write_distribution(install_dir, dist_name)
|
|
|
|
dist_dir = install_dir / dist_name
|
|
metadata = helpers.downgrade_distribution_metadata(dist_dir)
|
|
python_path = dist_dir / metadata['python_path']
|
|
install = mocker.patch(
|
|
'hatch.python.core.PythonManager.install', return_value=mocker.MagicMock(path=dist_dir, python_path=python_path)
|
|
)
|
|
|
|
result = hatch('python', 'install', dist_name, input='y\n')
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert result.output == helpers.dedent(
|
|
f"""
|
|
Update {dist_name}? [y/N]: y
|
|
Updating {dist_name}
|
|
Updated {dist_name} @ {dist_dir}
|
|
|
|
The following directory has been added to your PATH (pending a shell restart):
|
|
|
|
{python_path.parent}
|
|
"""
|
|
)
|
|
|
|
install.assert_called_once_with(dist_name)
|
|
path_append.assert_called_once_with(str(python_path.parent), shells=default_shells)
|
|
|
|
|
|
def test_already_installed_update_flag(hatch, helpers, temp_dir_data, path_append, default_shells, dist_name, mocker):
|
|
install_dir = temp_dir_data / 'data' / 'pythons'
|
|
helpers.write_distribution(install_dir, dist_name)
|
|
|
|
dist_dir = install_dir / dist_name
|
|
metadata = helpers.downgrade_distribution_metadata(dist_dir)
|
|
python_path = dist_dir / metadata['python_path']
|
|
install = mocker.patch(
|
|
'hatch.python.core.PythonManager.install', return_value=mocker.MagicMock(path=dist_dir, python_path=python_path)
|
|
)
|
|
|
|
result = hatch('python', 'install', '--update', dist_name)
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert result.output == helpers.dedent(
|
|
f"""
|
|
Updating {dist_name}
|
|
Updated {dist_name} @ {dist_dir}
|
|
|
|
The following directory has been added to your PATH (pending a shell restart):
|
|
|
|
{python_path.parent}
|
|
"""
|
|
)
|
|
|
|
install.assert_called_once_with(dist_name)
|
|
path_append.assert_called_once_with(str(python_path.parent), shells=default_shells)
|
|
|
|
|
|
@pytest.mark.parametrize('detector', ['in_current_path', 'in_new_path'])
|
|
def test_already_in_path(hatch, helpers, temp_dir_data, path_append, mocker, detector, dist_name):
|
|
mocker.patch(f'userpath.{detector}', return_value=True)
|
|
dist_dir = temp_dir_data / 'data' / 'pythons' / dist_name
|
|
python_path = dist_dir / get_distribution(dist_name).python_path
|
|
install = mocker.patch(
|
|
'hatch.python.core.PythonManager.install', return_value=mocker.MagicMock(path=dist_dir, python_path=python_path)
|
|
)
|
|
|
|
result = hatch('python', 'install', dist_name)
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert result.output == helpers.dedent(
|
|
f"""
|
|
Installing {dist_name}
|
|
Installed {dist_name} @ {dist_dir}
|
|
"""
|
|
)
|
|
|
|
install.assert_called_once_with(dist_name)
|
|
path_append.assert_not_called()
|
|
|
|
|
|
def test_private(hatch, helpers, temp_dir_data, path_append, dist_name, mocker):
|
|
dist_dir = temp_dir_data / 'data' / 'pythons' / dist_name
|
|
python_path = dist_dir / get_distribution(dist_name).python_path
|
|
install = mocker.patch(
|
|
'hatch.python.core.PythonManager.install', return_value=mocker.MagicMock(path=dist_dir, python_path=python_path)
|
|
)
|
|
|
|
result = hatch('python', 'install', '--private', dist_name)
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert result.output == helpers.dedent(
|
|
f"""
|
|
Installing {dist_name}
|
|
Installed {dist_name} @ {dist_dir}
|
|
"""
|
|
)
|
|
|
|
install.assert_called_once_with(dist_name)
|
|
path_append.assert_not_called()
|
|
|
|
|
|
def test_specific_location(hatch, helpers, temp_dir_data, path_append, dist_name, mocker):
|
|
install_dir = temp_dir_data / 'foo' / 'bar' / 'baz'
|
|
dist_dir = install_dir / dist_name
|
|
python_path = dist_dir / get_distribution(dist_name).python_path
|
|
install = mocker.patch(
|
|
'hatch.python.core.PythonManager.install', return_value=mocker.MagicMock(path=dist_dir, python_path=python_path)
|
|
)
|
|
|
|
result = hatch('python', 'install', '--private', '-d', str(install_dir), dist_name)
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert result.output == helpers.dedent(
|
|
f"""
|
|
Installing {dist_name}
|
|
Installed {dist_name} @ {dist_dir}
|
|
"""
|
|
)
|
|
|
|
install.assert_called_once_with(dist_name)
|
|
path_append.assert_not_called()
|
|
|
|
|
|
def test_all(hatch, temp_dir_data, path_append, default_shells, mocker, compatible_python_distributions):
|
|
mocked_dists = []
|
|
for name in compatible_python_distributions:
|
|
dist_dir = temp_dir_data / 'data' / 'pythons' / name
|
|
python_path = dist_dir / get_distribution(name).python_path
|
|
mocked_dists.append(mocker.MagicMock(path=dist_dir, python_path=python_path))
|
|
|
|
install = mocker.patch('hatch.python.core.PythonManager.install', side_effect=mocked_dists)
|
|
|
|
result = hatch('python', 'install', 'all')
|
|
|
|
assert result.exit_code == 0, result.output
|
|
|
|
expected_lines = []
|
|
for dist in mocked_dists:
|
|
expected_lines.extend((f'Installing {dist.path.name}', f'Installed {dist.path.name} @ {dist.path}'))
|
|
|
|
expected_lines.extend((
|
|
'',
|
|
'The following directories have been added to your PATH (pending a shell restart):',
|
|
'',
|
|
))
|
|
expected_lines.extend(str(dist.python_path.parent) for dist in mocked_dists)
|
|
expected_lines.append('')
|
|
|
|
assert result.output == '\n'.join(expected_lines)
|
|
|
|
assert install.call_args_list == [mocker.call(name) for name in compatible_python_distributions]
|
|
assert path_append.call_args_list == [
|
|
mocker.call(str(dist.python_path.parent), shells=default_shells) for dist in mocked_dists
|
|
]
|