Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: change CLI backend to typer #78

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jinja2 = ">=2.0"
xonsh = ">=0.14.0"
python-dotenv = ">=0.21.1"
colorama = ">=0.4.6"
typer = ">=0.9.0"

[tool.poetry.group.dev.dependencies]
containers-sugar = "1.9.0"
Expand Down Expand Up @@ -66,7 +67,7 @@ ignore_missing_imports = true
line-length = 79
force-exclude = true
src = ["./src/makim", "./tests"]
ignore = ["RUF012"]
ignore = ["RUF012", "PLR0913"]
exclude = [
"docs",
]
Expand All @@ -84,6 +85,9 @@ select = [
"I002", # isort
]

[tool.ruff.lint.pylint]
max-args = 6

[tool.ruff.pydocstyle]
convention = "numpy"

Expand Down
269 changes: 110 additions & 159 deletions src/makim/cli.py
Original file line number Diff line number Diff line change
@@ -1,199 +1,150 @@
"""Cli functions to define the arguments and to call Makim."""
import argparse
"""
Makim CLI module.

This module defines the command-line interface for the Makim tool.
"""
import os
import sys

from pathlib import Path

from makim import Makim, __version__


class CustomHelpFormatter(argparse.RawTextHelpFormatter):
"""Formatter for generating usage messages and argument help strings.

Only the name of this class is considered a public API. All the methods
provided by the class are considered an implementation detail.
"""

def __init__(
self,
prog,
indent_increment=2,
max_help_position=4,
width=None,
**kwargs,
):
"""Define the parameters for the argparse help text."""
super().__init__(
prog,
indent_increment=indent_increment,
max_help_position=max_help_position,
width=width,
**kwargs,
)
from typer import Argument, Option, Typer, echo
from typing_extensions import Annotated

from makim import Makim, __version__

makim = Makim()


def _get_args():
"""
Define the arguments for the CLI.

note: when added new flags, update the list of flags to be
skipped at extract_makim_args function.
"""
makim_file_default = str(Path(os.getcwd()) / '.makim.yaml')

parser = argparse.ArgumentParser(
prog='Makim',
description=(
'Makim is a tool that helps you to organize '
'and simplify your helper commands.'
),
epilog=(
'If you have any problem, open an issue at: '
'https://github.com/osl-incubator/makim'
),
add_help=False,
formatter_class=CustomHelpFormatter,
)
parser.add_argument(
app = Typer(
name='Makim',
epilog=(
'If you have any problem, open an issue at: '
'https://github.com/osl-incubator/makim'
),
)
makim_file_default = str(Path(os.getcwd()) / '.makim.yaml')


@app.callback(invoke_without_command=True)
def main(
# Common Options
help: bool = Option(
None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you probably don't need to specify help manually.

'--help',
'-h',
action='store_true',
help='Show the help menu',
)

parser.add_argument(
is_flag=True,
help='Show the version and exit',
),
version: bool = Option(
None,
'--version',
action='store_true',
help='Show the version of the installed Makim tool.',
)

parser.add_argument(
'-v',
is_flag=True,
help='Show the version and exit',
),
verbose: bool = Option(
None,
'--verbose',
action='store_true',
is_flag=True,
help='Show the commands to be executed.',
)

parser.add_argument(
),
dry_run: bool = Option(
None,
'--dry-run',
action='store_true',
is_flag=True,
help="Show the commands but don't execute them.",
)
),
# Makim-specific Options
makim_file: Annotated[
str,
Option(
'--makim-file',
help='Specify a custom location for the makim file.',
is_flag=True,
),
] = makim_file_default,
target: Annotated[
str,
Argument(
...,
help='Specify the target command to be performed.',
),
] = '',
):
"""
Makim is a tool.

that helps you to organize and simplify your helper commands.
"""
if version:
return show_version()

if help or not target:
return create_help_text(makim_file)

args = {
'dry_run': dry_run,
'help': help,
'makim_file': makim_file,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you also probably don't need to use help here
let's have typer to handle the help automatically

'target': target,
'verbose': verbose,
'version': version,
}

makim.load(makim_file)
return makim.run(args)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if that is the write way to work with makim and typer together

in this main function you probably should allow makim-file, and version.

for the other commands you will need to implement (for each one) verbose, dry-run and makim-file

the target you can set manually for each one, because each command will trigger just one function

does it make sense?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, let me know if this one is too complex for you ... and I can work on this one.
this one is really tricky because it is not using the default workflow for typer

Copy link
Collaborator Author

@abhijeetSaroha abhijeetSaroha Jan 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me try one more time, then I will report to you.



def create_help_text(makim_file):
"""Display help text with details about Makim commands and options."""
usage = """makim [--help] [--version] [--verbose] [--dry-run] [--makim-file
MAKIM_FILE] [target]"""

parser.add_argument(
'--makim-file',
type=str,
default=makim_file_default,
help='Specify a custom location for the makim file.',
)
description = """Makim is a tool that helps you to organize and simplify
your helper commands."""

try:
idx = sys.argv.index('--makim-file')
makim_file = sys.argv[idx + 1]
except ValueError:
makim_file = makim_file_default
epilog = """If you have any problem, open an issue at:
https://github.com/osl-incubator/makim"""

makim.load(makim_file)
target_help = []

# Iterate through groups and targets to generate help text
groups = makim.global_data.get('groups', [])
for group in groups:
target_help.append('\n' + group + ':')
target_help.append('-' * (len(group) + 1))
target_help.append('\n' + group + ':' + '\n')
target_help.append('-' * (len(group) + 1) + '\n')
for target_name, target_data in groups[group]['targets'].items():
target_name_qualified = f'{group}.{target_name}'
help_text = target_data['help'] if 'help' in target_data else ''
target_help.append(f' {target_name_qualified} => {help_text}')
target_help.append(f' {target_name_qualified} => {help_text}\n')

if 'args' in target_data:
target_help.append(' ARGS:')
target_help.append(' ARGS:\n')

for arg_name, arg_data in target_data['args'].items():
target_help.append(
f' --{arg_name}: ({arg_data["type"]}) '
f'{arg_data["help"]}'
f'{arg_data["help"]}\n'
)

parser.add_argument(
'target',
nargs='?',
default=None,
help=(
'Specify the target command to be performed. Options are:\n'
+ '\n'.join(target_help)
),
)

return parser
help_text = format_help_text(description, usage, epilog, target_help)
echo(help_text)


def show_version():
"""Show version."""
print(__version__)


def extract_makim_args():
"""Extract makim arguments from the CLI call."""
makim_args = {}
index_to_remove = []
for ind, arg in enumerate(list(sys.argv)):
if arg in [
'--help',
'--version',
'--verbose',
'--makim-file',
'--dry-run',
]:
continue
def format_help_text(description, usage, epilog, target_help):
"""Format help text with usage, description, and target details."""
return f"""{usage}

if not arg.startswith('--'):
continue
{description}

index_to_remove.append(ind)
Positonal Arguments:
{"".join(target_help)}
{epilog}"""

arg_name = None
arg_value = None

next_ind = ind + 1

arg_name = sys.argv[ind]

if (
len(sys.argv) == next_ind
or len(sys.argv) > next_ind
and sys.argv[next_ind].startswith('--')
):
arg_value = True
else:
arg_value = sys.argv[next_ind]
index_to_remove.append(next_ind)

makim_args[arg_name] = arg_value

# remove exclusive makim flags from original sys.argv
for ind in sorted(index_to_remove, reverse=True):
sys.argv.pop(ind)

return makim_args


def app():
"""Call the makim program with the arguments defined by the user."""
makim_args = extract_makim_args()
args_parser = _get_args()
args = args_parser.parse_args()

if args.version:
return show_version()

if not args.target or args.help:
return args_parser.print_help()
def show_version():
"""Show version."""
echo(__version__)

if args.help:
return args_parser.print_help()

makim.load(args.makim_file)
makim_args.update(dict(args._get_kwargs()))
return makim.run(makim_args)
if __name__ == '__main__':
app()
Loading