Skip to content

Commit

Permalink
refactor(cli): add CustomCommand to support custom help messages
Browse files Browse the repository at this point in the history
  • Loading branch information
Lee-000 committed Jun 23, 2021
1 parent f71a826 commit c30d0c1
Showing 1 changed file with 155 additions and 26 deletions.
181 changes: 155 additions & 26 deletions tensorbay/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,88 @@

"""Command-line interface.
Use 'gas' + COMMAND in terminal to operate on datasets.
Use 'gas <command>' in terminal to operate on datasets.
Use 'gas config' to configure environment.
Use ``gas auth`` to authenticate the accessKey of gas.
Use 'gas ls' to list data.
Use ``gas branch`` to list, create or delete branches.
Use 'gas draft' to operate a draft.
Use ``gas commit`` to commit drafts.
Use 'gas branch' to operate a branch.
Use ``gas config`` to configure the options when using gas CLI.
Use ``gas cp`` to copy local data to a remote path.
Use ``gas dataset`` to list, create or delete datasets.
Use ``gas draft`` to list or create drafts.
Use ``gas log`` to show commit logs.
Use ``gas ls`` to list data under the path.
Use ``gas rm`` to remove the remote data.
Use ``gas tag`` to list, create or delete tags.
"""

from functools import partial
from typing import Any, Dict, Iterable, Optional, Tuple

import click

from .. import __version__


class DeprecatedCommand(click.Command):
class CustomCommand(click.Command):
"""``click.Command`` wrapper class for CLI commands with custom help.
Arguments:
kwargs: The keyword arguments pass to ``click.Command``.
"""

def __init__(self, **kwargs: Any) -> None:
self.synopsis = kwargs.pop("synopsis", [])
super().__init__(**kwargs)

def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
"""Writes the custom help into the formatter if it exists.
The custom method adds :meth:`CustomCommand.format_synopsis`
to the original ``Command.format_help`` method.
Arguments:
ctx: The context of the command.
formatter: The help formatter of the command.
"""
formatter.width = 100
self.format_usage(ctx, formatter)
self.format_help_text(ctx, formatter)
self.format_synopsis(formatter)
self.format_options(ctx, formatter)
self.format_epilog(ctx, formatter)

def format_synopsis(self, formatter: click.HelpFormatter) -> None:
"""Wirte the synopsis to the formatter if exist.
Arguments:
formatter: The help formatter of the command.
"""
if not self.synopsis:
return

with formatter.section("Synopsis"):
for index, example in enumerate(self.synopsis):
if example.startswith("#") and index != 0:
formatter.write_paragraph()
formatter.write_text(example)


class DeprecatedCommand(CustomCommand):
"""Customized ``click.Command`` wrapper class for deprecated CLI commands.
Arguments:
Expand All @@ -34,7 +96,6 @@ class DeprecatedCommand(click.Command):
removed_in: The version the function will be removed in.
substitute: The substitute command.
kwargs: The keyword arguments pass to ``click.Command``.
"""

def __init__(
Expand Down Expand Up @@ -93,7 +154,7 @@ def invoke(self, ctx: click.Context) -> Any:
@click.option("-d", "--debug", is_flag=True, help="Debug mode.")
@click.pass_context
def cli(ctx: click.Context, access_key: str, url: str, profile_name: str, debug: bool) -> None:
"""You can use 'gas' + COMMAND to operate on your dataset.\f
"""You can use "gas <command>" to operate on your datasets.\f
Arguments:
ctx: The context to be passed as the first argument.
Expand All @@ -108,7 +169,10 @@ def cli(ctx: click.Context, access_key: str, url: str, profile_name: str, debug:
_implement_cli(ctx, access_key, url, profile_name, debug)


@cli.command(
command = partial(cli.command, cls=CustomCommand)


@command(
cls=DeprecatedCommand,
hidden=True,
since="v1.5.0",
Expand All @@ -130,7 +194,7 @@ def create(obj: Dict[str, str], name: str) -> None:
_implement_dataset(obj, name, is_delete=False, yes=False)


@cli.command(
@command(
cls=DeprecatedCommand,
hidden=True,
since="v1.5.0",
Expand All @@ -154,7 +218,13 @@ def delete(obj: Dict[str, str], name: str, yes: bool) -> None:
_implement_dataset(obj, name, is_delete=True, yes=yes)


@cli.command()
@command(
synopsis=[
"$ gas ls # List all the datasets.",
"$ gas ls tb:<dataset_name> # List segments of a dataset.",
"$ gas ls tb:<dataset_name>:<segment_name> # List files of a segment.",
]
)
@click.argument("tbrn", type=str, default="")
@click.option(
"-a", "--all", "list_all_files", is_flag=True, help="List all files under the segment."
Expand All @@ -176,7 +246,13 @@ def ls( # pylint: disable=invalid-name
_implement_ls(obj, tbrn, list_all_files)


@cli.command()
@command(
synopsis=[
"$ gas config [key] # Show the config.",
"$ gas config <key> <value> # Set the config.",
"$ gas config --unset [key] # Unset the config.",
]
)
@click.argument("key", type=str, default="")
@click.argument("value", type=str, default="")
@click.option("-u", "--unset", is_flag=True, help="Unset the config option")
Expand All @@ -194,13 +270,19 @@ def config(key: str, value: str, unset: bool) -> None:
_implement_config(key, value, unset)


@cli.command()
@command(
synopsis=[
"$ gas dataset # List datasets.",
"$ gas dataset tb:<dataset_name> # Create a dataset.",
"$ gas dataset -d [-y] tb:<dataset_name> # Delete a dataset.",
]
)
@click.argument("tbrn", type=str, default="")
@click.option("-d", "--delete", "is_delete", is_flag=True, help="Delete TensorBay dataset")
@click.option("-y", "--yes", is_flag=True, help="Confirm to delete the dataset completely.")
@click.pass_obj
def dataset(obj: Dict[str, str], tbrn: str, is_delete: bool, yes: bool) -> None:
"""Work with TensorBay datasets\f
"""List, create or delete datasets.\f
Arguments:
obj: A dict including config information.
Expand All @@ -214,7 +296,12 @@ def dataset(obj: Dict[str, str], tbrn: str, is_delete: bool, yes: bool) -> None:
_implement_dataset(obj, tbrn, is_delete, yes)


@cli.command()
@command(
synopsis=[
"$ gas draft -l tb:<dataset_name> # List drafts.",
"$ gas draft tb:<dataset_name>[@<branch_name>] [-t <title>] # Create a draft.",
]
)
@click.argument("tbrn", type=str)
@click.option("-l", "--list", "is_list", is_flag=True, help="List the drafts.")
@click.option("-t", "--title", type=str, default="", help="The title of the draft.")
Expand All @@ -225,7 +312,7 @@ def draft(
is_list: bool,
title: str,
) -> None:
"""Work with draft.\f
"""List or create drafts.\f
Arguments:
obj: A dict contains config information.
Expand All @@ -239,14 +326,16 @@ def draft(
_implement_draft(obj, tbrn, is_list, title)


@cli.command()
@command(
synopsis=["$ gas commit tb:<dataset_name>#<draft_number> [-m <message>] # Commit a draft."]
)
@click.argument("tbrn", type=str)
@click.option(
"-m", "--message", type=str, multiple=True, default=(), help="The message of the commit."
)
@click.pass_obj
def commit(obj: Dict[str, str], tbrn: str, message: Tuple[str, ...]) -> None:
"""Work with commit.\f
"""Commit drafts.\f
Arguments:
obj: A dict contains config information.
Expand All @@ -259,7 +348,16 @@ def commit(obj: Dict[str, str], tbrn: str, message: Tuple[str, ...]) -> None:
_implement_commit(obj, tbrn, message)


@cli.command()
@command(
synopsis=[
"# Upload a file.",
"$ gas cp <local_path> tb:<dataset_name>#<draft_number>:<segment_name>[://<remote_path]",
"# Upload files.",
"$ gas cp <local_path1> [local_path2 ...] tb:<dataset_name>#<draft_number>:<segment_name>",
"# Upload files in a folder.",
"$ gas cp -r <local_folder> tb:<dataset_name>#<draft_number>:<segment_name>",
]
)
@click.argument("local_paths", type=str, nargs=-1)
@click.argument("tbrn", type=str, nargs=1)
@click.option(
Expand Down Expand Up @@ -298,7 +396,12 @@ def cp( # pylint: disable=invalid-name, too-many-arguments
_implement_cp(obj, local_paths, tbrn, is_recursive, jobs, skip_uploaded_files)


@cli.command()
@command(
synopsis=[
"$ gas rm -r tb:<dataset_name>#<draft_number>:<segment_name> # Remove a segment.",
"$ gas rm tb:<dataset_name>@<revision>:<segment_name>://<remote_path> # Remove a file.",
]
)
@click.argument("tbrn", type=str)
@click.option(
"-r", "--recursive", "is_recursive", is_flag=True, help="Remove directories recursively."
Expand All @@ -320,14 +423,20 @@ def rm( # pylint: disable=invalid-name, too-many-arguments
_implement_rm(obj, tbrn, is_recursive)


@cli.command()
@command(
synopsis=[
"$ gas branch tb:<dataset_name> [--verbose] # List branches.",
"$ gas branch tb:<dataset_name>[@<branch_name>] <branch_name> # Create a branch.",
"$ gas branch --delete tb:<dataset_name>@<branch_name> # Delete a branch.",
]
)
@click.argument("tbrn", type=str)
@click.argument("name", type=str, default="")
@click.option("-v", "--verbose", is_flag=True, help="Show short commit id and commit message.")
@click.option("-d", "--delete", "is_delete", is_flag=True, help="Delete the branch")
@click.pass_obj
def branch(obj: Dict[str, str], tbrn: str, name: str, verbose: bool, is_delete: bool) -> None:
"""Work with branch.\f
"""List, create or delete branches.\f
Arguments:
obj: A dict contains config information.
Expand All @@ -342,13 +451,19 @@ def branch(obj: Dict[str, str], tbrn: str, name: str, verbose: bool, is_delete:
_implement_branch(obj, tbrn, name, verbose, is_delete)


@cli.command()
@command(
synopsis=[
"$ gas tag tb:<dataset_name> # List tags.",
"$ gas tag tb:<dataset_name>[@<revision>] <tag_name> # Create a tag.",
"$ gas tag -d tb:<dataset_name>@<tag_name> # Delete a tag.",
]
)
@click.argument("tbrn", type=str)
@click.argument("name", type=str, default="")
@click.option("-d", "--delete", "is_delete", is_flag=True, help="Delete the tag.")
@click.pass_obj
def tag(obj: Dict[str, str], tbrn: str, name: str, is_delete: bool) -> None:
"""Work with tag.\f
"""List, create or delete tags.\f
Arguments:
obj: A dict contains config information.
Expand All @@ -362,7 +477,14 @@ def tag(obj: Dict[str, str], tbrn: str, name: str, is_delete: bool) -> None:
_implement_tag(obj, tbrn, name, is_delete)


@cli.command()
@command(
synopsis=[
"$ gas log tb:<dataset_name>[@<branch_name>] # Show commit logs.",
"$ gas log -n <number> tb:<dataset_name>[@<branch_name>] "
"# Show up to <number> commit logs.",
"$ gas log --oneline tb:<dataset_name>[@<branch_name>] # Show oneline commit logs.",
]
)
@click.argument("tbrn", type=str)
@click.option(
"-n", "--max-count", type=int, default=None, help="Limit the max number of commits to be showed"
Expand All @@ -389,7 +511,14 @@ def log(
_implement_log(obj, tbrn, max_count, oneline)


@cli.command()
@command(
synopsis=[
"$ gas auth # Interactive authentication.",
"$ gas auth <AccessKey> # Authenticate with AccessKey.",
"$ gas auth --get [--all] # Get the authentication info.",
"$ gas auth --unset [--all] # Unset the authentication info.",
]
)
@click.argument("arg1", type=str, default="", metavar="accessKey")
@click.argument("arg2", type=str, default="", metavar="")
@click.option("-g", "--get", is_flag=True, help="Get the accesskey of the profile")
Expand Down

0 comments on commit c30d0c1

Please sign in to comment.