ConfirmAction message formatting
This commit is contained in:
@ -71,7 +71,7 @@ async def build_chain(dogs: list[Dog]) -> ChainedAction:
|
|||||||
ConfirmAction(
|
ConfirmAction(
|
||||||
name="test_confirm",
|
name="test_confirm",
|
||||||
message="Do you want to process the dogs?",
|
message="Do you want to process the dogs?",
|
||||||
confirm_type="yes_no",
|
confirm_type="yes_no_cancel",
|
||||||
return_last_result=True,
|
return_last_result=True,
|
||||||
inject_into="dogs",
|
inject_into="dogs",
|
||||||
),
|
),
|
||||||
@ -88,6 +88,7 @@ async def build_chain(dogs: list[Dog]) -> ChainedAction:
|
|||||||
factory = ActionFactory(
|
factory = ActionFactory(
|
||||||
name="Dog Post Factory",
|
name="Dog Post Factory",
|
||||||
factory=build_chain,
|
factory=build_chain,
|
||||||
|
preview_kwargs={"dogs": ["Buddy", "Max"]},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,13 +56,14 @@ class ConfirmAction(BaseAction):
|
|||||||
prompt_session (PromptSession | None): The session to use for input.
|
prompt_session (PromptSession | None): The session to use for input.
|
||||||
confirm (bool): Whether to prompt the user for confirmation.
|
confirm (bool): Whether to prompt the user for confirmation.
|
||||||
word (str): The word to type for TYPE_WORD confirmation.
|
word (str): The word to type for TYPE_WORD confirmation.
|
||||||
return_last_result (bool): Whether to return the last result of the action.
|
return_last_result (bool): Whether to return the last result of the action
|
||||||
|
instead of a boolean.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
message: str = "Continue",
|
message: str = "Confirm?",
|
||||||
confirm_type: ConfirmType | str = ConfirmType.YES_NO,
|
confirm_type: ConfirmType | str = ConfirmType.YES_NO,
|
||||||
prompt_session: PromptSession | None = None,
|
prompt_session: PromptSession | None = None,
|
||||||
confirm: bool = True,
|
confirm: bool = True,
|
||||||
@ -114,16 +115,19 @@ class ConfirmAction(BaseAction):
|
|||||||
session=self.prompt_session,
|
session=self.prompt_session,
|
||||||
)
|
)
|
||||||
case ConfirmType.YES_NO_CANCEL:
|
case ConfirmType.YES_NO_CANCEL:
|
||||||
|
error_message = "Enter 'Y', 'y' to confirm, 'N', 'n' to decline, or 'C', 'c' to abort."
|
||||||
answer = await self.prompt_session.prompt_async(
|
answer = await self.prompt_session.prompt_async(
|
||||||
f"❓ {self.message} ([Y]es, [N]o, or [C]ancel to abort): ",
|
f"❓ {self.message} [Y]es, [N]o, or [C]ancel to abort > ",
|
||||||
validator=words_validator(["Y", "N", "C"]),
|
validator=words_validator(
|
||||||
|
["Y", "N", "C"], error_message=error_message
|
||||||
|
),
|
||||||
)
|
)
|
||||||
if answer.upper() == "C":
|
if answer.upper() == "C":
|
||||||
raise CancelSignal(f"Action '{self.name}' was cancelled by the user.")
|
raise CancelSignal(f"Action '{self.name}' was cancelled by the user.")
|
||||||
return answer.upper() == "Y"
|
return answer.upper() == "Y"
|
||||||
case ConfirmType.TYPE_WORD:
|
case ConfirmType.TYPE_WORD:
|
||||||
answer = await self.prompt_session.prompt_async(
|
answer = await self.prompt_session.prompt_async(
|
||||||
f"❓ {self.message} (type '{self.word}' to confirm or N/n): ",
|
f"❓ {self.message} [{self.word}] to confirm or [N/n] > ",
|
||||||
validator=word_validator(self.word),
|
validator=word_validator(self.word),
|
||||||
)
|
)
|
||||||
return answer.upper().strip() != "N"
|
return answer.upper().strip() != "N"
|
||||||
@ -138,9 +142,10 @@ class ConfirmAction(BaseAction):
|
|||||||
raise CancelSignal(f"Action '{self.name}' was cancelled by the user.")
|
raise CancelSignal(f"Action '{self.name}' was cancelled by the user.")
|
||||||
return answer
|
return answer
|
||||||
case ConfirmType.OK_CANCEL:
|
case ConfirmType.OK_CANCEL:
|
||||||
|
error_message = "Enter 'O', 'o' to confirm or 'C', 'c' to abort."
|
||||||
answer = await self.prompt_session.prompt_async(
|
answer = await self.prompt_session.prompt_async(
|
||||||
f"❓ {self.message} ([O]k to continue, [C]ancel to abort): ",
|
f"❓ {self.message} [O]k to confirm, [C]ancel to abort > ",
|
||||||
validator=words_validator(["O", "C"]),
|
validator=words_validator(["O", "C"], error_message=error_message),
|
||||||
)
|
)
|
||||||
if answer.upper() == "C":
|
if answer.upper() == "C":
|
||||||
raise CancelSignal(f"Action '{self.name}' was cancelled by the user.")
|
raise CancelSignal(f"Action '{self.name}' was cancelled by the user.")
|
||||||
@ -213,5 +218,5 @@ class ConfirmAction(BaseAction):
|
|||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f"ConfirmAction(name={self.name}, message={self.message}, "
|
f"ConfirmAction(name={self.name}, message={self.message}, "
|
||||||
f"confirm_type={self.confirm_type})"
|
f"confirm_type={self.confirm_type}, return_last_result={self.return_last_result})"
|
||||||
)
|
)
|
||||||
|
@ -14,7 +14,7 @@ class SignalAction(Action):
|
|||||||
Useful for exiting a menu, going back, or halting execution gracefully.
|
Useful for exiting a menu, going back, or halting execution gracefully.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name: str, signal: Exception):
|
def __init__(self, name: str, signal: FlowSignal):
|
||||||
self.signal = signal
|
self.signal = signal
|
||||||
super().__init__(name, action=self.raise_signal)
|
super().__init__(name, action=self.raise_signal)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ from typing import Any, Callable
|
|||||||
|
|
||||||
from prompt_toolkit.formatted_text import HTML, merge_formatted_text
|
from prompt_toolkit.formatted_text import HTML, merge_formatted_text
|
||||||
from prompt_toolkit.key_binding import KeyBindings
|
from prompt_toolkit.key_binding import KeyBindings
|
||||||
|
from rich.console import Console
|
||||||
|
|
||||||
from falyx.console import console
|
from falyx.console import console
|
||||||
from falyx.options_manager import OptionsManager
|
from falyx.options_manager import OptionsManager
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
# Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed
|
# Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed
|
||||||
"""falyx.py
|
"""Main class for constructing and running Falyx CLI menus.
|
||||||
|
|
||||||
Main class for constructing and running Falyx CLI menus.
|
|
||||||
|
|
||||||
Falyx provides a structured, customizable interactive menu system
|
Falyx provides a structured, customizable interactive menu system
|
||||||
for running commands, actions, and workflows. It supports:
|
for running commands, actions, and workflows. It supports:
|
||||||
|
@ -56,6 +56,39 @@ def get_root_parser(
|
|||||||
allow_abbrev: bool = True,
|
allow_abbrev: bool = True,
|
||||||
exit_on_error: bool = True,
|
exit_on_error: bool = True,
|
||||||
) -> ArgumentParser:
|
) -> ArgumentParser:
|
||||||
|
"""
|
||||||
|
Construct the root-level ArgumentParser for the Falyx CLI.
|
||||||
|
|
||||||
|
This parser handles global arguments shared across subcommands and can serve
|
||||||
|
as the base parser for the Falyx CLI or standalone applications. It includes
|
||||||
|
options for verbosity, debug logging, and version output.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prog (str | None): Name of the program (e.g., 'falyx').
|
||||||
|
usage (str | None): Optional custom usage string.
|
||||||
|
description (str | None): Description shown in the CLI help.
|
||||||
|
epilog (str | None): Message displayed at the end of help output.
|
||||||
|
parents (Sequence[ArgumentParser] | None): Optional parent parsers.
|
||||||
|
prefix_chars (str): Characters to denote optional arguments (default: "-").
|
||||||
|
fromfile_prefix_chars (str | None): Prefix to indicate argument file input.
|
||||||
|
argument_default (Any): Global default value for arguments.
|
||||||
|
conflict_handler (str): Strategy to resolve conflicting argument names.
|
||||||
|
add_help (bool): Whether to include help (`-h/--help`) in this parser.
|
||||||
|
allow_abbrev (bool): Allow abbreviated long options.
|
||||||
|
exit_on_error (bool): Exit immediately on error or raise an exception.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ArgumentParser: The root parser with global options attached.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
```
|
||||||
|
Includes the following arguments:
|
||||||
|
--never-prompt : Run in non-interactive mode.
|
||||||
|
-v / --verbose : Enable debug logging.
|
||||||
|
--debug-hooks : Enable hook lifecycle debug logs.
|
||||||
|
--version : Print the Falyx version.
|
||||||
|
```
|
||||||
|
"""
|
||||||
parser = ArgumentParser(
|
parser = ArgumentParser(
|
||||||
prog=prog,
|
prog=prog,
|
||||||
usage=usage,
|
usage=usage,
|
||||||
@ -92,7 +125,30 @@ def get_subparsers(
|
|||||||
title: str = "Falyx Commands",
|
title: str = "Falyx Commands",
|
||||||
description: str | None = "Available commands for the Falyx CLI.",
|
description: str | None = "Available commands for the Falyx CLI.",
|
||||||
) -> _SubParsersAction:
|
) -> _SubParsersAction:
|
||||||
"""Create and return a subparsers action for the given parser."""
|
"""
|
||||||
|
Create and return a subparsers object for registering Falyx CLI subcommands.
|
||||||
|
|
||||||
|
This function adds a `subparsers` block to the given root parser, enabling
|
||||||
|
structured subcommands such as `run`, `run-all`, `preview`, etc.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
parser (ArgumentParser): The root parser to attach the subparsers to.
|
||||||
|
title (str): Title used in help output to group subcommands.
|
||||||
|
description (str | None): Optional text describing the group of subcommands.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
_SubParsersAction: The subparsers object that can be used to add new CLI subcommands.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError: If `parser` is not an instance of `ArgumentParser`.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```python
|
||||||
|
>>> parser = get_root_parser()
|
||||||
|
>>> subparsers = get_subparsers(parser, title="Available Commands")
|
||||||
|
>>> subparsers.add_parser("run", help="Run a Falyx command")
|
||||||
|
```
|
||||||
|
"""
|
||||||
if not isinstance(parser, ArgumentParser):
|
if not isinstance(parser, ArgumentParser):
|
||||||
raise TypeError("parser must be an instance of ArgumentParser")
|
raise TypeError("parser must be an instance of ArgumentParser")
|
||||||
subparsers = parser.add_subparsers(
|
subparsers = parser.add_subparsers(
|
||||||
@ -122,7 +178,54 @@ def get_arg_parsers(
|
|||||||
root_parser: ArgumentParser | None = None,
|
root_parser: ArgumentParser | None = None,
|
||||||
subparsers: _SubParsersAction | None = None,
|
subparsers: _SubParsersAction | None = None,
|
||||||
) -> FalyxParsers:
|
) -> FalyxParsers:
|
||||||
"""Returns the argument parser for the CLI."""
|
"""
|
||||||
|
Create and return the full suite of argument parsers used by the Falyx CLI.
|
||||||
|
|
||||||
|
This function builds the root parser and all subcommand parsers used for structured
|
||||||
|
CLI workflows in Falyx. It supports standard subcommands including `run`, `run-all`,
|
||||||
|
`preview`, `list`, and `version`, and integrates with registered `Command` objects
|
||||||
|
to populate dynamic help and usage documentation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prog (str | None): Program name to display in help and usage messages.
|
||||||
|
usage (str | None): Optional usage message to override the default.
|
||||||
|
description (str | None): Description for the CLI root parser.
|
||||||
|
epilog (str | None): Epilog message shown after the help text.
|
||||||
|
parents (Sequence[ArgumentParser] | None): Optional parent parsers.
|
||||||
|
prefix_chars (str): Characters that prefix optional arguments.
|
||||||
|
fromfile_prefix_chars (str | None): Prefix character for reading args from file.
|
||||||
|
argument_default (Any): Default value for arguments if not specified.
|
||||||
|
conflict_handler (str): Strategy for resolving conflicting arguments.
|
||||||
|
add_help (bool): Whether to add the `-h/--help` option to the root parser.
|
||||||
|
allow_abbrev (bool): Whether to allow abbreviated long options.
|
||||||
|
exit_on_error (bool): Whether the parser exits on error or raises.
|
||||||
|
commands (dict[str, Command] | None): Optional dictionary of registered commands
|
||||||
|
to populate help and subcommand descriptions dynamically.
|
||||||
|
root_parser (ArgumentParser | None): Custom root parser to use instead of building one.
|
||||||
|
subparsers (_SubParsersAction | None): Optional existing subparser object to extend.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
FalyxParsers: A structured container of all parsers, including `run`, `run-all`,
|
||||||
|
`preview`, `list`, `version`, and the root parser.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError: If `root_parser` is not an instance of ArgumentParser or
|
||||||
|
`subparsers` is not an instance of _SubParsersAction.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```python
|
||||||
|
>>> parsers = get_arg_parsers(commands=my_command_dict)
|
||||||
|
>>> args = parsers.root.parse_args()
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- This function integrates dynamic command usage and descriptions if the
|
||||||
|
`commands` argument is provided.
|
||||||
|
- The `run` parser supports additional options for retry logic and confirmation
|
||||||
|
prompts.
|
||||||
|
- The `run-all` parser executes all commands matching a tag.
|
||||||
|
- Use `falyx run ?[COMMAND]` from the CLI to preview a command.
|
||||||
|
"""
|
||||||
if epilog is None:
|
if epilog is None:
|
||||||
epilog = f"Tip: Use '{prog} run ?[COMMAND]' to preview any command from the CLI."
|
epilog = f"Tip: Use '{prog} run ?[COMMAND]' to preview any command from the CLI."
|
||||||
if root_parser is None:
|
if root_parser is None:
|
||||||
|
@ -44,10 +44,12 @@ def yes_no_validator() -> Validator:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return Validator.from_callable(validate, error_message="Enter 'Y' or 'n'.")
|
return Validator.from_callable(validate, error_message="Enter 'Y', 'y' or 'N', 'n'.")
|
||||||
|
|
||||||
|
|
||||||
def words_validator(keys: Sequence[str] | KeysView[str]) -> Validator:
|
def words_validator(
|
||||||
|
keys: Sequence[str] | KeysView[str], error_message: str | None = None
|
||||||
|
) -> Validator:
|
||||||
"""Validator for specific word inputs."""
|
"""Validator for specific word inputs."""
|
||||||
|
|
||||||
def validate(text: str) -> bool:
|
def validate(text: str) -> bool:
|
||||||
@ -55,9 +57,10 @@ def words_validator(keys: Sequence[str] | KeysView[str]) -> Validator:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return Validator.from_callable(
|
if error_message is None:
|
||||||
validate, error_message=f"Invalid input. Choices: {{{', '.join(keys)}}}."
|
error_message = f"Invalid input. Choices: {{{', '.join(keys)}}}."
|
||||||
)
|
|
||||||
|
return Validator.from_callable(validate, error_message=error_message)
|
||||||
|
|
||||||
|
|
||||||
def word_validator(word: str) -> Validator:
|
def word_validator(word: str) -> Validator:
|
||||||
@ -68,7 +71,7 @@ def word_validator(word: str) -> Validator:
|
|||||||
return True
|
return True
|
||||||
return text.upper().strip() == word.upper()
|
return text.upper().strip() == word.upper()
|
||||||
|
|
||||||
return Validator.from_callable(validate, error_message=f"Enter '{word}' or 'N'.")
|
return Validator.from_callable(validate, error_message=f"Enter '{word}' or 'N', 'n'.")
|
||||||
|
|
||||||
|
|
||||||
class MultiIndexValidator(Validator):
|
class MultiIndexValidator(Validator):
|
||||||
|
@ -1 +1 @@
|
|||||||
__version__ = "0.1.58"
|
__version__ = "0.1.59"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "falyx"
|
name = "falyx"
|
||||||
version = "0.1.58"
|
version = "0.1.59"
|
||||||
description = "Reliable and introspectable async CLI action framework."
|
description = "Reliable and introspectable async CLI action framework."
|
||||||
authors = ["Roland Thomas Jr <roland@rtj.dev>"]
|
authors = ["Roland Thomas Jr <roland@rtj.dev>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
Reference in New Issue
Block a user