refactor: annotation with __future__.annotations
Adds `from __future__ import annotations` to the top of every module,
right below the module's docstring. Replaces any usages of t.List,
t.Dict, t.Set, t.Tuple, and t.Type with their built-in equivalents:
list, dict, set, tuple, and type. Ensures that make test still passes
under Python 3.7, 3.8 and 3.9.
2023-01-17 18:57:23 +00:00
|
|
|
from __future__ import annotations
|
2023-01-06 18:02:17 +00:00
|
|
|
|
2019-01-22 20:25:04 +00:00
|
|
|
import sys
|
2022-02-07 17:11:43 +00:00
|
|
|
import typing as t
|
2019-01-22 20:25:04 +00:00
|
|
|
|
2019-12-12 16:05:56 +00:00
|
|
|
import appdirs
|
2019-01-22 20:25:04 +00:00
|
|
|
import click
|
|
|
|
|
2022-02-07 17:11:43 +00:00
|
|
|
from tutor import exceptions, fmt, hooks, utils
|
|
|
|
from tutor.__about__ import __app__, __version__
|
|
|
|
from tutor.commands.config import config_command
|
|
|
|
from tutor.commands.context import Context
|
|
|
|
from tutor.commands.dev import dev
|
|
|
|
from tutor.commands.images import images_command
|
|
|
|
from tutor.commands.k8s import k8s
|
|
|
|
from tutor.commands.local import local
|
2023-05-04 12:06:28 +00:00
|
|
|
from tutor.commands.mounts import mounts_command
|
2022-02-07 17:11:43 +00:00
|
|
|
from tutor.commands.plugins import plugins_command
|
|
|
|
|
2019-01-22 20:25:04 +00:00
|
|
|
|
2021-02-25 08:09:14 +00:00
|
|
|
def main() -> None:
|
2019-01-22 20:25:04 +00:00
|
|
|
try:
|
2022-04-20 16:32:23 +00:00
|
|
|
# Everyone on board
|
|
|
|
# Note that this action should not be triggered in the module scope, because it
|
|
|
|
# makes it difficult for tests to rollback changes.
|
|
|
|
hooks.Actions.CORE_READY.do()
|
2019-12-12 16:05:56 +00:00
|
|
|
cli() # pylint: disable=no-value-for-parameter
|
2020-09-17 10:53:14 +00:00
|
|
|
except KeyboardInterrupt:
|
|
|
|
pass
|
2019-01-22 20:25:04 +00:00
|
|
|
except exceptions.TutorError as e:
|
2022-02-07 17:11:43 +00:00
|
|
|
fmt.echo_error(f"Error: {e.args[0]}")
|
2019-01-22 20:25:04 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
2019-04-23 07:57:55 +00:00
|
|
|
|
2023-08-29 14:21:43 +00:00
|
|
|
class TutorCli(click.Group):
|
2022-02-07 17:11:43 +00:00
|
|
|
"""
|
|
|
|
Dynamically load subcommands at runtime.
|
|
|
|
|
|
|
|
This is necessary to load plugin subcommands, based on the list of enabled
|
|
|
|
plugins (and thus of config.yml).
|
|
|
|
Docs: https://click.palletsprojects.com/en/latest/commands/#custom-multi-commands
|
|
|
|
"""
|
|
|
|
|
|
|
|
IS_ROOT_READY = False
|
|
|
|
|
2023-08-29 14:21:43 +00:00
|
|
|
def get_command(
|
|
|
|
self, ctx: click.Context, cmd_name: str
|
|
|
|
) -> t.Optional[click.Command]:
|
2022-02-07 17:11:43 +00:00
|
|
|
"""
|
2023-08-29 14:21:43 +00:00
|
|
|
This is run when passing a command from the CLI. E.g: tutor config ...
|
2022-02-07 17:11:43 +00:00
|
|
|
"""
|
2023-08-29 14:21:43 +00:00
|
|
|
self.ensure_plugins_enabled(ctx)
|
|
|
|
return super().get_command(ctx, cmd_name=cmd_name)
|
2022-02-07 17:11:43 +00:00
|
|
|
|
refactor: annotation with __future__.annotations
Adds `from __future__ import annotations` to the top of every module,
right below the module's docstring. Replaces any usages of t.List,
t.Dict, t.Set, t.Tuple, and t.Type with their built-in equivalents:
list, dict, set, tuple, and type. Ensures that make test still passes
under Python 3.7, 3.8 and 3.9.
2023-01-17 18:57:23 +00:00
|
|
|
def list_commands(self, ctx: click.Context) -> list[str]:
|
2022-02-07 17:11:43 +00:00
|
|
|
"""
|
|
|
|
This is run in the following cases:
|
|
|
|
- shell autocompletion: tutor <tab>
|
|
|
|
- print help: tutor, tutor -h
|
|
|
|
"""
|
2023-08-29 14:21:43 +00:00
|
|
|
self.ensure_plugins_enabled(ctx)
|
|
|
|
return super().list_commands(ctx)
|
2022-02-07 17:11:43 +00:00
|
|
|
|
2023-08-29 14:21:43 +00:00
|
|
|
def ensure_plugins_enabled(self, ctx: click.Context) -> None:
|
2022-02-07 17:11:43 +00:00
|
|
|
"""
|
2023-08-29 14:21:43 +00:00
|
|
|
We enable plugins as soon as possible to have access to commands.
|
2022-02-07 17:11:43 +00:00
|
|
|
"""
|
2023-08-29 14:21:43 +00:00
|
|
|
if not "root" in ctx.params:
|
|
|
|
# When generating docs, this function is called with empty args.
|
|
|
|
# That's ok, we just ignore it.
|
|
|
|
return
|
|
|
|
if not self.IS_ROOT_READY:
|
|
|
|
hooks.Actions.PROJECT_ROOT_READY.do(ctx.params["root"])
|
|
|
|
self.IS_ROOT_READY = True
|
|
|
|
for cmd in hooks.Filters.CLI_COMMANDS.iterate():
|
|
|
|
self.add_command(cmd)
|
2022-02-07 17:11:43 +00:00
|
|
|
|
|
|
|
|
2021-11-23 08:25:09 +00:00
|
|
|
@click.group(
|
2022-02-07 17:11:43 +00:00
|
|
|
cls=TutorCli,
|
|
|
|
invoke_without_command=True,
|
|
|
|
add_help_option=False, # Context is incorrectly loaded when help option is automatically added
|
2021-11-23 08:25:09 +00:00
|
|
|
help="Tutor is the Docker-based Open edX distribution designed for peace of mind.",
|
|
|
|
)
|
Fix version information in bundle
`./dist/tutor --version` raises an error:
Traceback (most recent call last):
File "main", line 4, in <module>
File "tutor/cli.py", line 21, in main
File "click/core.py", line 764, in __call__
File "click/core.py", line 716, in main
File "click/core.py", line 641, in make_context
File "click/core.py", line 1089, in parse_args
File "click/core.py", line 940, in parse_args
File "click/core.py", line 1477, in handle_parse_result
File "click/core.py", line 96, in invoke_param_callback
File "click/decorators.py", line 270, in callback
RuntimeError: Could not determine version
[1659] Failed to execute script main
To address this, we pass the version value directly to click.
This is for issue #156.
2019-02-10 17:31:48 +00:00
|
|
|
@click.version_option(version=__version__)
|
2019-12-12 16:05:56 +00:00
|
|
|
@click.option(
|
|
|
|
"-r",
|
|
|
|
"--root",
|
|
|
|
envvar="TUTOR_ROOT",
|
2021-09-16 14:48:16 +00:00
|
|
|
default=appdirs.user_data_dir(appname=__app__),
|
2019-12-12 16:05:56 +00:00
|
|
|
show_default=True,
|
|
|
|
type=click.Path(resolve_path=True),
|
|
|
|
help="Root project directory (environment variable: TUTOR_ROOT)",
|
|
|
|
)
|
2022-02-07 17:11:43 +00:00
|
|
|
@click.option(
|
|
|
|
"-h",
|
|
|
|
"--help",
|
|
|
|
"show_help",
|
|
|
|
is_flag=True,
|
|
|
|
help="Print this help",
|
|
|
|
)
|
2019-12-12 16:05:56 +00:00
|
|
|
@click.pass_context
|
2022-02-07 17:11:43 +00:00
|
|
|
def cli(context: click.Context, root: str, show_help: bool) -> None:
|
2020-11-16 11:17:15 +00:00
|
|
|
if utils.is_root():
|
2020-03-16 16:39:43 +00:00
|
|
|
fmt.echo_alert(
|
2020-11-07 16:48:20 +00:00
|
|
|
"You are running Tutor as root. This is strongly not recommended. If you are doing this in order to access"
|
|
|
|
" the Docker daemon, you should instead add your user to the 'docker' group. (see https://docs.docker.com"
|
|
|
|
"/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user)"
|
2020-03-16 16:39:43 +00:00
|
|
|
)
|
2019-12-12 16:05:56 +00:00
|
|
|
context.obj = Context(root)
|
2022-11-11 13:13:36 +00:00
|
|
|
context.help_option_names = ["-h", "--help"]
|
2022-02-07 17:11:43 +00:00
|
|
|
if context.invoked_subcommand is None or show_help:
|
|
|
|
click.echo(context.get_help())
|
2019-01-22 20:25:04 +00:00
|
|
|
|
2019-04-23 07:57:55 +00:00
|
|
|
|
2019-05-05 09:45:24 +00:00
|
|
|
@click.command(help="Print this help", name="help")
|
2022-02-07 17:11:43 +00:00
|
|
|
@click.pass_context
|
|
|
|
def help_command(context: click.Context) -> None:
|
|
|
|
context.invoke(cli, show_help=True)
|
|
|
|
|
|
|
|
|
2022-11-14 11:22:42 +00:00
|
|
|
hooks.Filters.CLI_COMMANDS.add_items(
|
2023-05-04 12:06:28 +00:00
|
|
|
[
|
|
|
|
config_command,
|
|
|
|
dev,
|
|
|
|
help_command,
|
|
|
|
images_command,
|
|
|
|
k8s,
|
|
|
|
local,
|
|
|
|
mounts_command,
|
|
|
|
plugins_command,
|
|
|
|
]
|
2022-02-07 17:11:43 +00:00
|
|
|
)
|
2019-01-22 20:25:04 +00:00
|
|
|
|
2019-04-23 07:57:55 +00:00
|
|
|
|
2019-01-22 20:25:04 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|