pypa-hatch/tests/cli/project/test_metadata.py

423 lines
14 KiB
Python

import os
import pytest
from hatch.config.constants import ConfigEnvVars
from hatch.project.core import Project
from hatchling.utils.constants import DEFAULT_CONFIG_FILE
pytestmark = [pytest.mark.usefixtures('local_backend_process_output')]
def read_readme(project_dir):
return repr((project_dir / 'README.txt').read_text())[1:-1]
class TestBuildDependenciesInstalled:
def test_default_all(self, hatch, temp_dir, helpers):
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
path = temp_dir / 'my-app'
(path / 'README.md').replace(path / 'README.txt')
project = Project(path)
config = dict(project.raw_config)
config['project']['readme'] = 'README.txt'
project.save_config(config)
with path.as_cwd():
result = hatch('project', 'metadata')
assert result.exit_code == 0, result.output
assert result.output == helpers.dedent(
f"""
{{
"name": "my-app",
"version": "0.0.1",
"readme": {{
"content-type": "text/plain",
"text": "{read_readme(path)}"
}},
"requires-python": ">=3.8",
"license": "MIT",
"authors": [
{{
"name": "Foo Bar",
"email": "foo@bar.baz"
}}
],
"classifiers": [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy"
],
"urls": {{
"Documentation": "https://github.com/Foo Bar/my-app#readme",
"Issues": "https://github.com/Foo Bar/my-app/issues",
"Source": "https://github.com/Foo Bar/my-app"
}}
}}
"""
)
def test_field_readme(self, hatch, temp_dir):
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
path = temp_dir / 'my-app'
(path / 'README.md').replace(path / 'README.txt')
project = Project(path)
config = dict(project.raw_config)
config['project']['readme'] = 'README.txt'
project.save_config(config)
with path.as_cwd():
result = hatch('project', 'metadata', 'readme')
assert result.exit_code == 0, result.output
assert result.output == (
f"""\
{(path / 'README.txt').read_text()}
"""
)
def test_field_string(self, hatch, temp_dir, helpers):
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
path = temp_dir / 'my-app'
with path.as_cwd():
result = hatch('project', 'metadata', 'license')
assert result.exit_code == 0, result.output
assert result.output == helpers.dedent(
"""
MIT
"""
)
def test_field_complex(self, hatch, temp_dir, helpers):
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
path = temp_dir / 'my-app'
with path.as_cwd():
result = hatch('project', 'metadata', 'urls')
assert result.exit_code == 0, result.output
assert result.output == helpers.dedent(
"""
{
"Documentation": "https://github.com/Foo Bar/my-app#readme",
"Issues": "https://github.com/Foo Bar/my-app/issues",
"Source": "https://github.com/Foo Bar/my-app"
}
"""
)
class TestBuildDependenciesMissing:
@pytest.mark.allow_backend_process
def test_incompatible_environment(self, hatch, temp_dir, helpers):
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
path = temp_dir / 'my-app'
project = Project(path)
config = dict(project.raw_config)
config['build-system']['requires'].append('foo')
project.save_config(config)
helpers.update_project_environment(
project, 'default', {'skip-install': True, 'python': '9000', **project.config.envs['default']}
)
with path.as_cwd():
result = hatch('project', 'metadata')
assert result.exit_code == 1, result.output
assert result.output == helpers.dedent(
"""
Environment `default` is incompatible: cannot locate Python: 9000
"""
)
def test_default_all(self, hatch, temp_dir, helpers):
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
path = temp_dir / 'my-app'
(path / 'README.md').replace(path / 'README.txt')
project = Project(path)
config = dict(project.raw_config)
config['build-system']['requires'].append('foo')
config['project']['readme'] = 'README.txt'
project.save_config(config)
with path.as_cwd():
result = hatch('project', 'metadata')
assert result.exit_code == 0, result.output
assert result.output == helpers.dedent(
f"""
Setting up build environment for missing dependencies
{{
"name": "my-app",
"version": "0.0.1",
"readme": {{
"content-type": "text/plain",
"text": "{read_readme(path)}"
}},
"requires-python": ">=3.8",
"license": "MIT",
"authors": [
{{
"name": "Foo Bar",
"email": "foo@bar.baz"
}}
],
"classifiers": [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy"
],
"urls": {{
"Documentation": "https://github.com/Foo Bar/my-app#readme",
"Issues": "https://github.com/Foo Bar/my-app/issues",
"Source": "https://github.com/Foo Bar/my-app"
}}
}}
"""
)
def test_field_readme(self, hatch, temp_dir):
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
path = temp_dir / 'my-app'
(path / 'README.md').replace(path / 'README.txt')
project = Project(path)
config = dict(project.raw_config)
config['build-system']['requires'].append('foo')
config['project']['readme'] = 'README.txt'
project.save_config(config)
with path.as_cwd():
result = hatch('project', 'metadata', 'readme')
assert result.exit_code == 0, result.output
assert result.output == (
f"""\
Setting up build environment for missing dependencies
{(path / 'README.txt').read_text()}
"""
)
def test_field_string(self, hatch, temp_dir, helpers):
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
path = temp_dir / 'my-app'
project = Project(path)
config = dict(project.raw_config)
config['build-system']['requires'].append('foo')
project.save_config(config)
with path.as_cwd():
result = hatch('project', 'metadata', 'license')
assert result.exit_code == 0, result.output
assert result.output == helpers.dedent(
"""
Setting up build environment for missing dependencies
MIT
"""
)
def test_field_complex(self, hatch, temp_dir, helpers):
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
path = temp_dir / 'my-app'
project = Project(path)
config = dict(project.raw_config)
config['build-system']['requires'].append('foo')
project.save_config(config)
with path.as_cwd():
result = hatch('project', 'metadata', 'urls')
assert result.exit_code == 0, result.output
assert result.output == helpers.dedent(
"""
Setting up build environment for missing dependencies
{
"Documentation": "https://github.com/Foo Bar/my-app#readme",
"Issues": "https://github.com/Foo Bar/my-app/issues",
"Source": "https://github.com/Foo Bar/my-app"
}
"""
)
def test_plugin_dependencies_unmet(self, hatch, temp_dir, helpers, mock_plugin_installation):
project_name = 'My.App'
with temp_dir.as_cwd():
result = hatch('new', project_name)
assert result.exit_code == 0, result.output
path = temp_dir / 'my-app'
dependency = os.urandom(16).hex()
(path / DEFAULT_CONFIG_FILE).write_text(
helpers.dedent(
f"""
[env]
requires = ["{dependency}"]
"""
)
)
(path / 'README.md').replace(path / 'README.txt')
project = Project(path)
config = dict(project.raw_config)
config['build-system']['requires'].append('foo')
config['project']['readme'] = 'README.txt'
project.save_config(config)
with path.as_cwd():
result = hatch('project', 'metadata')
assert result.exit_code == 0, result.output
assert result.output == helpers.dedent(
f"""
Syncing environment plugin requirements
Setting up build environment for missing dependencies
{{
"name": "my-app",
"version": "0.0.1",
"readme": {{
"content-type": "text/plain",
"text": "{read_readme(path)}"
}},
"requires-python": ">=3.8",
"license": "MIT",
"authors": [
{{
"name": "Foo Bar",
"email": "foo@bar.baz"
}}
],
"classifiers": [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy"
],
"urls": {{
"Documentation": "https://github.com/Foo Bar/my-app#readme",
"Issues": "https://github.com/Foo Bar/my-app/issues",
"Source": "https://github.com/Foo Bar/my-app"
}}
}}
"""
)
helpers.assert_plugin_installation(mock_plugin_installation, [dependency])
@pytest.mark.allow_backend_process
@pytest.mark.requires_internet
def test_no_compatibility_check_if_exists(self, hatch, temp_dir, helpers, mocker):
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)
config = dict(project.raw_config)
config['build-system']['requires'].append('binary')
project.save_config(config)
with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}):
result = hatch('project', 'metadata', 'license')
assert result.exit_code == 0, result.output
assert result.output == helpers.dedent(
"""
Setting up build environment for missing dependencies
MIT
"""
)
mocker.patch('hatch.env.virtual.VirtualEnvironment.check_compatibility', side_effect=Exception('incompatible'))
with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}):
result = hatch('project', 'metadata', 'license')
assert result.exit_code == 0, result.output
assert result.output == helpers.dedent(
"""
MIT
"""
)