Skip to content

Commit

Permalink
feat: Add support for interactive args (#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
abhijeetSaroha authored Sep 24, 2024
1 parent af80420 commit f422e30
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 20 deletions.
26 changes: 26 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,31 @@ 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
RESULT=$(echo mycity | makim $VERBOSE_FLAG --file $MAKIM_FILE weather.forecast --country mycountry)
STRING_VALIDATION="Fetching weather forecast for mycity, mycountry..."
# Check if RESULT contains SUBSTRING
if [[ "$RESULT" != *"$STRING_VALIDATION"* ]]; then
echo "STRING_VALIDATION not found in RESULT."
exit 1
fi
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!"

0 comments on commit f422e30

Please sign in to comment.