pypa-hatch/hatch/cli/__init__.py

203 lines
6.3 KiB
Python

import os
import click
from ..__about__ import __version__
from ..config.constants import AppEnvVars, ConfigEnvVars
from ..project.core import Project
from ..utils.ci import running_in_ci
from ..utils.fs import Path
from .application import Application
from .build import build
from .clean import clean
from .config import config
from .dep import dep
from .env import env
from .new import new
from .publish import publish
from .run import run
from .shell import shell
from .status import status
from .version import version
@click.group(context_settings={'help_option_names': ['-h', '--help']}, invoke_without_command=True)
@click.option(
'--env',
'-e',
'env_name',
envvar=AppEnvVars.ENV,
default='default',
help='The name of the environment to use [env var: `HATCH_ENV`]',
)
@click.option(
'--project',
'-p',
envvar=ConfigEnvVars.PROJECT,
help='The name of the project to work on [env var: `HATCH_PROJECT`]',
)
@click.option(
'--color/--no-color',
default=None,
help='Whether or not to display colored output (default is auto-detection) [env vars: `FORCE_COLOR`/`NO_COLOR`]',
)
@click.option(
'--interactive/--no-interactive',
envvar=AppEnvVars.INTERACTIVE,
default=None,
help=(
'Whether or not to allow features like prompts and progress bars (default is auto-detection) '
'[env var: `HATCH_INTERACTIVE`]'
),
)
@click.option(
'--verbose',
'-v',
envvar=AppEnvVars.VERBOSE,
count=True,
help='Increase verbosity (can be used additively) [env var: `HATCH_VERBOSE`]',
)
@click.option(
'--quiet',
'-q',
envvar=AppEnvVars.QUIET,
count=True,
help='Decrease verbosity (can be used additively) [env var: `HATCH_QUIET`]',
)
@click.option(
'--data-dir',
envvar=ConfigEnvVars.DATA,
help='The path to a custom directory used to persist data [env var: `HATCH_DATA_DIR`]',
)
@click.option(
'--cache-dir',
envvar=ConfigEnvVars.CACHE,
help='The path to a custom directory used to cache data [env var: `HATCH_CACHE_DIR`]',
)
@click.option(
'--config',
'config_file',
envvar=ConfigEnvVars.CONFIG,
help='The path to a custom config file to use [env var: `HATCH_CONFIG`]',
)
@click.version_option(version=__version__, prog_name='Hatch')
@click.pass_context
def hatch(ctx: click.Context, env_name, project, color, interactive, verbose, quiet, data_dir, cache_dir, config_file):
"""
\b
_ _ _ _
| | | | | | | |
| |_| | __ _| |_ ___| |__
| _ |/ _` | __/ __| '_ \\
| | | | (_| | || (__| | | |
\\_| |_/\\__,_|\\__\\___|_| |_|
"""
if color is None:
if os.environ.get(AppEnvVars.NO_COLOR) == '1':
color = False
elif os.environ.get(AppEnvVars.FORCE_COLOR) == '1':
color = True
if interactive is None:
interactive = not running_in_ci()
app = Application(ctx.exit, verbose - quiet, color, interactive)
app.env_active = os.environ.get(AppEnvVars.ENV_ACTIVE)
if app.env_active and ctx.get_parameter_source('env_name').name == 'DEFAULT': # type: ignore
app.env = app.env_active
else:
app.env = env_name
if config_file:
app.config_file.path = Path(config_file).resolve()
if not app.config_file.path.is_file():
app.abort(f'The selected config file `{str(app.config_file.path)}` does not exist.')
elif not app.config_file.path.is_file():
if not app.quiet:
app.display_waiting('No config file found, creating one with default settings now...')
try:
app.config_file.restore()
if not app.quiet:
app.display_success('Success! Please see `hatch config`.')
except OSError: # no cov
app.abort(
f'Unable to create config file located at `{str(app.config_file.path)}`. Please check your permissions.'
)
if not ctx.invoked_subcommand:
app.display_info(ctx.get_help())
return
# Persist app data for sub-commands
ctx.obj = app
try:
app.config_file.load()
except OSError as e: # no cov
app.abort(f'Error loading configuration: {e}')
app.config.terminal.styles.parse_fields()
errors = app.initialize_styles(app.config.terminal.styles.raw_data)
if errors and color is not False and not app.quiet: # no cov
for error in errors:
app.display_warning(error)
app.data_dir = Path(data_dir or app.config.dirs.data)
app.cache_dir = Path(cache_dir or app.config.dirs.cache)
if project:
app.project = Project.from_config(app.config, project)
if app.project is None or app.project.root is None:
app.abort(f'Unable to locate project {project}')
return
app.project = Project(Path.cwd())
if app.config.mode == 'local':
return
# The following logic is mostly duplicated for each branch so coverage can be asserted
elif app.config.mode == 'project':
if not app.config.project:
app.display_warning('Mode is set to `project` but no project is set, defaulting to the current directory')
return
possible_project = Project.from_config(app.config, app.config.project)
if possible_project is None:
app.display_warning(f'Unable to locate project {app.config.project}, defaulting to the current directory')
else:
app.project = possible_project
return
elif app.config.mode == 'aware' and app.project.root is None:
if not app.config.project:
app.display_warning('Mode is set to `aware` but no project is set, defaulting to the current directory')
return
possible_project = Project.from_config(app.config, app.config.project)
if possible_project is None:
app.display_warning(f'Unable to locate project {app.config.project}, defaulting to the current directory')
else:
app.project = possible_project
return
hatch.add_command(build)
hatch.add_command(clean)
hatch.add_command(config)
hatch.add_command(dep)
hatch.add_command(env)
hatch.add_command(new)
hatch.add_command(publish)
hatch.add_command(run)
hatch.add_command(shell)
hatch.add_command(status)
hatch.add_command(version)
def main(): # no cov
return hatch(windows_expand_args=False)