pypa-hatch/hatch/cli/new/__init__.py

139 lines
4.8 KiB
Python

import click
@click.command(short_help='Create or initialize a project')
@click.argument('name', required=False)
@click.argument('location', required=False)
@click.option('--interactive', '-i', 'interactive', is_flag=True, help='Interactively choose details about the project')
@click.option('--cli', 'feature_cli', is_flag=True, help='Give the project a command line interface')
@click.option('--init', 'initialize', is_flag=True, help='Initialize an existing project')
@click.pass_obj
def new(app, name, location, interactive, feature_cli, initialize):
"""Create or initialize a project."""
from copy import deepcopy
from datetime import datetime, timezone
from ...config.model import TemplateConfig
from ...project.core import Project
from ...template import File
from ...utils.fs import Path
if initialize:
interactive = True
if not name:
if not interactive:
app.abort('Missing required argument for the project name, use the -i/--interactive flag.')
name = app.prompt('Project name')
normalized_name = Project.canonicalize_name(name, strict=False)
if location:
location = Path(location).resolve()
elif initialize:
location = Path.cwd()
else:
location = Path(normalized_name).resolve()
needs_config_update = False
if location.is_file():
app.abort(f'Path `{location}` points to a file.')
elif location.is_dir() and any(location.iterdir()):
if not initialize:
app.abort(f'Directory `{location}` is not empty.')
needs_config_update = (location / 'pyproject.toml').is_file()
default_config = {
'description': '',
'dependencies': set(),
'package_name': normalized_name.replace('-', '_'),
'project_name': name,
'project_name_normalized': normalized_name,
'args': {'cli': feature_cli},
}
if interactive:
default_config['description'] = app.prompt('Description', default='')
app.display_info()
if needs_config_update:
app.project.initialize(str(location / 'pyproject.toml'), default_config)
app.display_success('Updated: pyproject.toml')
return
template_config = deepcopy(app.config.template.raw_data)
if 'plugins' in template_config and not template_config['plugins']:
del template_config['plugins']
TemplateConfig(template_config, ('template',)).parse_fields()
plugin_config = template_config.pop('plugins')
# Set up default config for template files
template_config.update(default_config)
template_classes = app.plugins.template.collect()
templates = []
for template_name, template_class in sorted(template_classes.items(), key=lambda item: -item[1].PRIORITY):
if template_name in plugin_config:
templates.append(
template_class(plugin_config.pop(template_name), app.cache_dir, datetime.now(timezone.utc))
)
if not templates:
app.abort(f'None of the defined plugins were found: {", ".join(sorted(plugin_config))}')
elif plugin_config:
app.abort(f'Some of the defined plugins were not found: {", ".join(sorted(plugin_config))}')
for template in templates:
template.initialize_config(template_config)
template_files = []
for template in templates:
for template_file in template.get_files(config=deepcopy(template_config)):
if template_file.__class__ is not File:
template_file = template_file(deepcopy(template_config), template.plugin_config)
if template_file.path is None: # no cov
continue
elif initialize and str(template_file.path) != 'pyproject.toml':
continue
template_files.append(template_file)
for template in templates:
template.finalize_files(config=deepcopy(template_config), files=template_files)
for template_file in template_files:
template_file.write(location)
if initialize:
app.display_success('Wrote: pyproject.toml')
return
from rich.markup import escape
from rich.tree import Tree
def recurse_directory(directory, tree):
paths = sorted(Path(directory).iterdir(), key=lambda p: (p.is_file(), p.name))
for path in paths:
if path.is_dir():
recurse_directory(
path, tree.add(f'[bold magenta][link={app.platform.format_file_uri(path)}]{escape(path.name)}')
)
else:
tree.add(f'[green][link={app.platform.format_file_uri(path)}]{escape(path.name)}')
root = Tree(
f'[bold magenta][link={app.platform.format_file_uri(location)}]{escape(location.name)}',
guide_style='bright_blue',
hide_root=location == Path.cwd(),
)
recurse_directory(location, root)
app.display(root, markup=True)