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

feat: add support for interactive args #112

Merged
merged 12 commits into from
Sep 24, 2024
18 changes: 18 additions & 0 deletions .makim.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ groups:
- task: smoke-tests.dir-absolute-path
- task: smoke-tests.dir-no-path
- task: smoke-tests.dir-relative-path
- task: smoke-tests.interactive-args

ci:
help: Run all tasks used on CI
Expand Down Expand Up @@ -346,6 +347,23 @@ groups:
makim $VERBOSE_FLAG --file $MAKIM_FILE group-relative.task-absolute
makim $VERBOSE_FLAG --file $MAKIM_FILE group-relative.task-relative

interactive-args:
help: Test makim with interactive-args
args:
verbose-mode:
help: Run the all the tests in verbose mode
type: bool
action: store_true
env:
MAKIM_FILE: tests/smoke/.makim-interactive-args.yaml
shell: bash
run: |
export VERBOSE_FLAG='${{ "--verbose" if args.verbose_mode else "" }}'
makim $VERBOSE_FLAG --file $MAKIM_FILE --help
makim $VERBOSE_FLAG --file $MAKIM_FILE --version
makim $VERBOSE_FLAG --file $MAKIM_FILE user.create --username johndoe --email johndoe@gmail.com --password johndoe
makim $VERBOSE_FLAG --file $MAKIM_FILE weather.forecast --city Delhi --country India
xmnlab marked this conversation as resolved.
Show resolved Hide resolved

error:
help: This group helps tests failure tasks
tasks:
Expand Down
42 changes: 23 additions & 19 deletions src/makim/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def create_args_string(args: Dict[str, str]) -> str:
args_rendered = []

arg_template = (
'{arg_name}: {arg_type} = typer.Option('
'{arg_name}: Optional[{arg_type}] = typer.Option('
'{default_value}, '
'"--{name_flag}", '
'help="{help_text}"'
Expand All @@ -184,9 +184,11 @@ def create_args_string(args: Dict[str, str]) -> str:
name_clean = name.replace('-', '_')
arg_type = normalize_string_type(spec.get('type', 'str'))
help_text = spec.get('help', '')
default_value = '...'
default_value = 'None'

if not spec.get('required', False):
if not spec.get('required', False) and not spec.get(
'interactive', False
):
default_value = spec.get('default', '')
default_value = get_default_value_str(arg_type, default_value)

Expand All @@ -206,7 +208,7 @@ def create_args_string(args: Dict[str, str]) -> str:


def apply_click_options(
command_function: Callable, options: Dict[str, str]
command_function: Callable, options: Dict[str, Any]
) -> Callable:
"""
Apply Click options to a Typer command function.
Expand Down Expand Up @@ -235,7 +237,9 @@ def apply_click_options(

opt_args.update(
{
'default': opt_default,
'default': None
if opt_data.get('interactive', False)
else opt_default,
'type': map_type_from_string(opt_type_str),
'help': opt_data.get('help', ''),
'show_default': True,
Expand Down Expand Up @@ -265,9 +269,9 @@ def create_dynamic_command(name: str, args: Dict[str, str]) -> None:
args_str = create_args_string(args)
args_param_list = [f'"task": "{name}"']

args_data = cast(Dict[str, str], args.get('args', {}))
args_data = cast(Dict[str, Dict[str, str]], args.get('args', {}))

for arg in list(args_data.keys()):
for arg, arg_details in args_data.items():
arg_clean = arg.replace('-', '_')
args_param_list.append(f'"--{arg}": {arg_clean}')

Expand All @@ -280,24 +284,24 @@ def create_dynamic_command(name: str, args: Dict[str, str]) -> None:
rich_help_panel=group_name,
)

function_code = (
f'def dynamic_command({args_str}):\n'
f' makim.run({args_param_str})\n'
'\n'
)
function_code = f'def dynamic_command({args_str}):\n'

# handle interactive prompts
for arg, arg_details in args_data.items():
arg_clean = arg.replace('-', '_')
if arg_details.get('interactive', False):
function_code += f' if {arg_clean} is None:\n'
function_code += f" {arg_clean} = click.prompt('{arg}')\n"

function_code += f' makim.run({args_param_str})\n'

local_vars: Dict[str, Any] = {}
try:
exec(function_code, globals(), local_vars)
except Exception as e:
# breakpoint()
print(e)
print(function_code)
exec(function_code, globals(), local_vars)
dynamic_command = decorator(local_vars['dynamic_command'])

# Apply Click options to the Typer command
if 'args' in args:
options_data = cast(Dict[str, str], args.get('args', {}))
options_data = cast(Dict[str, Dict[str, Any]], args.get('args', {}))
dynamic_command = apply_click_options(dynamic_command, options_data)


Expand Down
2 changes: 1 addition & 1 deletion src/makim/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ def _run_command(self, args: dict):
args_input[k_clean] = default

input_flag = f'--{k}'
if args.get(input_flag):
if args.get(input_flag) is not None:
if action == 'store_true':
args_input[k_clean] = (
True if args[input_flag] is None else args[input_flag]
Expand Down
39 changes: 39 additions & 0 deletions tests/smoke/.makim-interactive-args.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
groups:
user:
tasks:
create:
help: Create a new user
args:
username:
type: str
help: The username for the new user
email:
type: str
help: The email address for the new user
interactive: true
password:
type: str
help: The password for the new user
interactive: true
run: |
echo "Creating user:"
echo "Username: ${{ args.username }}"
echo "Email: ${{ args.email }}"
echo "Password: ${{ args.password }}"

weather:
tasks:
forecast:
help: Get the weather forecast for a location
args:
city:
type: str
help: Enter the city for weather forecast
interactive: true
country:
type: str
help: Enter the country for weather forecast
interactive: true
run: |
echo "Fetching weather forecast for ${{ args.city }}, ${{ args.country }}..."
echo "The weather in ${{ args.city }} is sunny with a high of 25°C today!"
Loading