pypa-hatch/tests/cli/env/test_show.py

508 lines
19 KiB
Python

import json
import os
import pytest
from hatch.env.utils import get_env_var
from hatch.project.core import Project
from hatch.utils.structures import EnvVars
from hatchling.utils.constants import DEFAULT_CONFIG_FILE
@pytest.fixture(scope='module', autouse=True)
def _terminal_width():
with EnvVars({'COLUMNS': '200'}, exclude=[get_env_var(plugin_name='virtual', option='uv_path')]):
yield
def test_default(hatch, helpers, temp_dir, config_file):
config_file.model.template.plugins['default']['tests'] = False
config_file.save()
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
project_path = temp_dir / 'my-app'
data_path = temp_dir / 'data'
data_path.mkdir()
with project_path.as_cwd():
result = hatch('env', 'show', '--ascii')
assert result.exit_code == 0, result.output
assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
"""
Standalone
+---------+---------+
| Name | Type |
+=========+=========+
| default | virtual |
+---------+---------+
"""
)
def test_default_as_json(hatch, temp_dir, config_file):
config_file.model.template.plugins['default']['tests'] = False
config_file.save()
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
project_path = temp_dir / 'my-app'
data_path = temp_dir / 'data'
data_path.mkdir()
with project_path.as_cwd():
result = hatch('env', 'show', '--json')
assert result.exit_code == 0, result.output
environments = json.loads(result.output)
assert list(environments) == [
'default',
'hatch-build',
'hatch-static-analysis',
'hatch-test.py3.13',
'hatch-test.py3.12',
'hatch-test.py3.11',
'hatch-test.py3.10',
'hatch-test.py3.9',
'hatch-test.py3.8',
'hatch-uv',
]
assert environments['default'] == {'type': 'virtual'}
def test_single_only(hatch, helpers, temp_dir, config_file):
config_file.model.template.plugins['default']['tests'] = False
config_file.save()
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
project_path = temp_dir / 'my-app'
data_path = temp_dir / 'data'
data_path.mkdir()
project = Project(project_path)
helpers.update_project_environment(project, 'foo', {})
helpers.update_project_environment(project, 'bar', {})
with project_path.as_cwd():
result = hatch('env', 'show', '--ascii')
assert result.exit_code == 0, result.output
assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
"""
Standalone
+---------+---------+
| Name | Type |
+=========+=========+
| default | virtual |
+---------+---------+
| foo | virtual |
+---------+---------+
| bar | virtual |
+---------+---------+
"""
)
def test_single_and_matrix(hatch, helpers, temp_dir, config_file):
config_file.model.template.plugins['default']['tests'] = False
config_file.save()
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
project_path = temp_dir / 'my-app'
data_path = temp_dir / 'data'
data_path.mkdir()
project = Project(project_path)
helpers.update_project_environment(project, 'foo', {'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}]})
with project_path.as_cwd():
result = hatch('env', 'show', '--ascii')
assert result.exit_code == 0, result.output
assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
"""
Standalone
+---------+---------+
| Name | Type |
+=========+=========+
| default | virtual |
+---------+---------+
Matrices
+------+---------+----------------+
| Name | Type | Envs |
+======+=========+================+
| foo | virtual | foo.py39-9000 |
| | | foo.py39-3.14 |
| | | foo.py310-9000 |
| | | foo.py310-3.14 |
+------+---------+----------------+
"""
)
def test_default_matrix_only(hatch, helpers, temp_dir, config_file):
config_file.model.template.plugins['default']['tests'] = False
config_file.save()
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
project_path = temp_dir / 'my-app'
data_path = temp_dir / 'data'
data_path.mkdir()
project = Project(project_path)
helpers.update_project_environment(
project, 'default', {'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}]}
)
with project_path.as_cwd():
result = hatch('env', 'show', '--ascii')
assert result.exit_code == 0, result.output
assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
"""
Matrices
+---------+---------+------------+
| Name | Type | Envs |
+=========+=========+============+
| default | virtual | py39-9000 |
| | | py39-3.14 |
| | | py310-9000 |
| | | py310-3.14 |
+---------+---------+------------+
"""
)
def test_all_matrix_types_with_single(hatch, helpers, temp_dir, config_file):
config_file.model.template.plugins['default']['tests'] = False
config_file.save()
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
project_path = temp_dir / 'my-app'
data_path = temp_dir / 'data'
data_path.mkdir()
project = Project(project_path)
helpers.update_project_environment(
project, 'default', {'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}]}
)
helpers.update_project_environment(project, 'foo', {'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}]})
helpers.update_project_environment(project, 'bar', {})
with project_path.as_cwd():
result = hatch('env', 'show', '--ascii')
assert result.exit_code == 0, result.output
assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
"""
Standalone
+------+---------+
| Name | Type |
+======+=========+
| bar | virtual |
+------+---------+
Matrices
+---------+---------+----------------+
| Name | Type | Envs |
+=========+=========+================+
| default | virtual | py39-9000 |
| | | py39-3.14 |
| | | py310-9000 |
| | | py310-3.14 |
+---------+---------+----------------+
| foo | virtual | foo.py39-9000 |
| | | foo.py39-3.14 |
| | | foo.py310-9000 |
| | | foo.py310-3.14 |
+---------+---------+----------------+
"""
)
def test_specific(hatch, helpers, temp_dir, config_file):
config_file.model.template.plugins['default']['tests'] = False
config_file.save()
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
project_path = temp_dir / 'my-app'
data_path = temp_dir / 'data'
data_path.mkdir()
project = Project(project_path)
helpers.update_project_environment(project, 'foo', {})
helpers.update_project_environment(project, 'bar', {})
with project_path.as_cwd():
result = hatch('env', 'show', 'bar', 'foo', '--ascii')
assert result.exit_code == 0, result.output
assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
"""
Standalone
+------+---------+
| Name | Type |
+======+=========+
| foo | virtual |
+------+---------+
| bar | virtual |
+------+---------+
"""
)
def test_specific_unknown(hatch, helpers, temp_dir, config_file):
config_file.model.template.plugins['default']['tests'] = False
config_file.save()
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
project_path = temp_dir / 'my-app'
data_path = temp_dir / 'data'
data_path.mkdir()
with project_path.as_cwd():
result = hatch('env', 'show', 'foo', '--ascii')
assert result.exit_code == 1, result.output
assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
"""
Environment `foo` is not defined by project config
"""
)
def test_optional_columns(hatch, helpers, temp_dir, config_file):
config_file.model.template.plugins['default']['tests'] = False
config_file.save()
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
project_path = temp_dir / 'my-app'
data_path = temp_dir / 'data'
data_path.mkdir()
dependencies = ['python___dateutil', 'bAr.Baz[TLS] >=1.2RC5']
extra_dependencies = ['Foo;python_version<"3.8"']
env_vars = {'FOO': '1', 'BAR': '2'}
description = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna \
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint \
occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
"""
project = Project(project_path)
config = dict(project.raw_config)
config['project']['optional-dependencies'] = {'foo_bar': [], 'baz': []}
project.save_config(config)
helpers.update_project_environment(
project,
'default',
{
'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}],
'description': description,
'dependencies': dependencies,
'extra-dependencies': extra_dependencies,
'env-vars': env_vars,
'features': ['Foo...Bar', 'Baz', 'baZ'],
'scripts': {'test': 'pytest', 'build': 'python -m build', '_foo': 'test'},
},
)
helpers.update_project_environment(
project,
'foo',
{
'description': description,
'dependencies': dependencies,
'extra-dependencies': extra_dependencies,
'env-vars': env_vars,
},
)
with project_path.as_cwd():
result = hatch('env', 'show', '--ascii')
assert result.exit_code == 0, result.output
assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
"""
Standalone
+------+---------+----------+-----------------------------+-----------------------+---------+----------------------------------------------------------------------------------------------------------+
| Name | Type | Features | Dependencies | Environment variables | Scripts | Description |
+======+=========+==========+=============================+=======================+=========+==========================================================================================================+
| foo | virtual | baz | bar-baz[tls]>=1.2rc5 | BAR=2 | build | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et |
| | | foo-bar | foo; python_version < '3.8' | FOO=1 | test | dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip |
| | | | python-dateutil | | | ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu |
| | | | | | | fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia |
| | | | | | | deserunt mollit anim id est laborum. |
+------+---------+----------+-----------------------------+-----------------------+---------+----------------------------------------------------------------------------------------------------------+
Matrices
+---------+---------+------------+----------+-----------------------------+-----------------------+---------+------------------------------------------------------------------------------------------+
| Name | Type | Envs | Features | Dependencies | Environment variables | Scripts | Description |
+=========+=========+============+==========+=============================+=======================+=========+==========================================================================================+
| default | virtual | py39-9000 | baz | bar-baz[tls]>=1.2rc5 | BAR=2 | build | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor |
| | | py39-3.14 | foo-bar | foo; python_version < '3.8' | FOO=1 | test | incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud |
| | | py310-9000 | | python-dateutil | | | exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure |
| | | py310-3.14 | | | | | dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. |
| | | | | | | | Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt |
| | | | | | | | mollit anim id est laborum. |
+---------+---------+------------+----------+-----------------------------+-----------------------+---------+------------------------------------------------------------------------------------------+
"""
)
def test_context_formatting(hatch, helpers, temp_dir, config_file):
config_file.model.template.plugins['default']['tests'] = False
config_file.save()
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
project_path = temp_dir / 'my-app'
data_path = temp_dir / 'data'
data_path.mkdir()
project = Project(project_path)
# Without context formatting
helpers.update_project_environment(
project,
'default',
{
'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}],
'dependencies': ['foo @ {root:uri}/../foo'],
},
)
# With context formatting
helpers.update_project_environment(
project,
'foo',
{
'env-vars': {'BAR': '{env:FOO_BAZ}'},
'dependencies': ['pydantic'],
},
)
with project_path.as_cwd(env_vars={'FOO_BAZ': 'FOO_BAR'}):
result = hatch('env', 'show', '--ascii')
assert result.exit_code == 0, result.output
assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
"""
Standalone
+------+---------+--------------+-----------------------+
| Name | Type | Dependencies | Environment variables |
+======+=========+==============+=======================+
| foo | virtual | pydantic | BAR=FOO_BAR |
+------+---------+--------------+-----------------------+
Matrices
+---------+---------+------------+------------------------+
| Name | Type | Envs | Dependencies |
+=========+=========+============+========================+
| default | virtual | py39-9000 | foo@ {root:uri}/../foo |
| | | py39-3.14 | |
| | | py310-9000 | |
| | | py310-3.14 | |
+---------+---------+------------+------------------------+
"""
)
def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_plugin_installation):
config_file.model.template.plugins['default']['tests'] = False
config_file.save()
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
project_path = temp_dir / 'my-app'
data_path = temp_dir / 'data'
data_path.mkdir()
dependency = os.urandom(16).hex()
(project_path / DEFAULT_CONFIG_FILE).write_text(
helpers.dedent(
f"""
[env]
requires = ["{dependency}"]
"""
)
)
with project_path.as_cwd():
result = hatch('env', 'show', '--ascii')
assert result.exit_code == 0, result.output
assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
"""
Syncing environment plugin requirements
Standalone
+---------+---------+
| Name | Type |
+=========+=========+
| default | virtual |
+---------+---------+
"""
)
helpers.assert_plugin_installation(mock_plugin_installation, [dependency])