diff --git a/falyx/action/action_types.py b/falyx/action/action_types.py index 7ebdc02..ce855a4 100644 --- a/falyx/action/action_types.py +++ b/falyx/action/action_types.py @@ -52,3 +52,31 @@ class SelectionReturnType(Enum): def _missing_(cls, value: object) -> SelectionReturnType: valid = ", ".join(member.value for member in cls) raise ValueError(f"Invalid DictReturnType: '{value}'. Must be one of: {valid}") + + +class ConfirmType(Enum): + """Enum for different confirmation types.""" + + YES_NO = "yes_no" + YES_CANCEL = "yes_cancel" + YES_NO_CANCEL = "yes_no_cancel" + TYPE_WORD = "type_word" + OK_CANCEL = "ok_cancel" + + @classmethod + def choices(cls) -> list[ConfirmType]: + """Return a list of all hook type choices.""" + return list(cls) + + def __str__(self) -> str: + """Return the string representation of the confirm type.""" + return self.value + + @classmethod + def _missing_(cls, value: object) -> ConfirmType: + if isinstance(value, str): + for member in cls: + if member.value == value.lower(): + return member + valid = ", ".join(member.value for member in cls) + raise ValueError(f"Invalid ConfirmType: '{value}'. Must be one of: {valid}") diff --git a/falyx/action/confirm_action.py b/falyx/action/confirm_action.py index 83ce112..428e9e7 100644 --- a/falyx/action/confirm_action.py +++ b/falyx/action/confirm_action.py @@ -1,11 +1,11 @@ from __future__ import annotations -from enum import Enum from typing import Any from prompt_toolkit import PromptSession from rich.tree import Tree +from falyx.action.action_types import ConfirmType from falyx.action.base_action import BaseAction from falyx.context import ExecutionContext from falyx.execution_registry import ExecutionRegistry as er @@ -17,25 +17,6 @@ from falyx.themes import OneColors from falyx.validators import word_validator, words_validator -class ConfirmType(Enum): - """Enum for different confirmation types.""" - - YES_NO = "yes_no" - YES_CANCEL = "yes_cancel" - YES_NO_CANCEL = "yes_no_cancel" - TYPE_WORD = "type_word" - OK_CANCEL = "ok_cancel" - - @classmethod - def choices(cls) -> list[ConfirmType]: - """Return a list of all hook type choices.""" - return list(cls) - - def __str__(self) -> str: - """Return the string representation of the confirm type.""" - return self.value - - class ConfirmAction(BaseAction): """ Action to confirm an operation with the user. @@ -66,7 +47,7 @@ class ConfirmAction(BaseAction): message: str = "Confirm?", confirm_type: ConfirmType | str = ConfirmType.YES_NO, prompt_session: PromptSession | None = None, - confirm: bool = True, + never_prompt: bool = False, word: str = "CONFIRM", return_last_result: bool = False, inject_last_result: bool = True, @@ -88,11 +69,11 @@ class ConfirmAction(BaseAction): name=name, inject_last_result=inject_last_result, inject_into=inject_into, + never_prompt=never_prompt, ) self.message = message self.confirm_type = self._coerce_confirm_type(confirm_type) self.prompt_session = prompt_session or PromptSession() - self.confirm = confirm self.word = word self.return_last_result = return_last_result @@ -165,11 +146,9 @@ class ConfirmAction(BaseAction): try: await self.hooks.trigger(HookType.BEFORE, context) if ( - not self.confirm + self.never_prompt or self.options_manager - and not should_prompt_user( - confirm=self.confirm, options=self.options_manager - ) + and not should_prompt_user(confirm=True, options=self.options_manager) ): logger.debug( "Skipping confirmation for action '%s' as 'confirm' is False or options manager indicates no prompt.", @@ -209,7 +188,7 @@ class ConfirmAction(BaseAction): ) tree.add(f"[bold]Message:[/] {self.message}") tree.add(f"[bold]Type:[/] {self.confirm_type.value}") - tree.add(f"[bold]Prompt Required:[/] {'Yes' if self.confirm else 'No'}") + tree.add(f"[bold]Prompt Required:[/] {'No' if self.never_prompt else 'Yes'}") if self.confirm_type == ConfirmType.TYPE_WORD: tree.add(f"[bold]Confirmation Word:[/] {self.word}") if parent is None: diff --git a/falyx/parser/command_argument_parser.py b/falyx/parser/command_argument_parser.py index dc73849..dce3be7 100644 --- a/falyx/parser/command_argument_parser.py +++ b/falyx/parser/command_argument_parser.py @@ -395,7 +395,7 @@ class CommandArgumentParser: help: str = "", dest: str | None = None, resolver: BaseAction | None = None, - lazy_resolver: bool = False, + lazy_resolver: bool = True, ) -> None: """Add an argument to the parser. For `ArgumentAction.ACTION`, `nargs` and `type` determine how many and what kind @@ -852,6 +852,10 @@ class CommandArgumentParser: and spec.lazy_resolver and from_validate ): + if not args: + raise CommandArgumentError( + f"Missing required argument '{spec.dest}': {spec.get_choice_text()}{help_text}" + ) continue # Lazy resolvers are not validated here raise CommandArgumentError( f"Missing required argument '{spec.dest}': {spec.get_choice_text()}{help_text}" diff --git a/falyx/version.py b/falyx/version.py index aa5efd5..ccfe745 100644 --- a/falyx/version.py +++ b/falyx/version.py @@ -1 +1 @@ -__version__ = "0.1.60" +__version__ = "0.1.61" diff --git a/pyproject.toml b/pyproject.toml index eec446f..b6400c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "falyx" -version = "0.1.60" +version = "0.1.61" description = "Reliable and introspectable async CLI action framework." authors = ["Roland Thomas Jr "] license = "MIT"