Add multi selecto to SelectionAction and SelectFileAction, Allow IOActions to receive no input, Rename subpackage falyx.parsers -> falyx.parser, Add default_text to UserInputAction
This commit is contained in:
@ -6,11 +6,12 @@ from falyx.action.types import FileReturnType
|
|||||||
|
|
||||||
sf = SelectFileAction(
|
sf = SelectFileAction(
|
||||||
name="select_file",
|
name="select_file",
|
||||||
suffix_filter=".py",
|
suffix_filter=".yaml",
|
||||||
title="Select a YAML file",
|
title="Select a YAML file",
|
||||||
prompt_message="Choose > ",
|
prompt_message="Choose 2 > ",
|
||||||
return_type=FileReturnType.TEXT,
|
return_type=FileReturnType.TEXT,
|
||||||
columns=3,
|
columns=3,
|
||||||
|
number_selections=2,
|
||||||
)
|
)
|
||||||
|
|
||||||
flx = Falyx()
|
flx = Falyx()
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from falyx import Falyx
|
||||||
from falyx.action import SelectionAction
|
from falyx.action import SelectionAction
|
||||||
from falyx.selection import SelectionOption
|
from falyx.selection import SelectionOption
|
||||||
from falyx.signals import CancelSignal
|
from falyx.signals import CancelSignal
|
||||||
@ -24,7 +26,45 @@ select = SelectionAction(
|
|||||||
show_table=True,
|
show_table=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
list_selections = [uuid4() for _ in range(10)]
|
||||||
print(asyncio.run(select()))
|
|
||||||
except CancelSignal:
|
list_select = SelectionAction(
|
||||||
print("Selection was cancelled.")
|
name="Select Deployments",
|
||||||
|
selections=list_selections,
|
||||||
|
title="Select Deployments",
|
||||||
|
columns=3,
|
||||||
|
prompt_message="Select 3 Deployments > ",
|
||||||
|
return_type="value",
|
||||||
|
show_table=True,
|
||||||
|
number_selections=3,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
flx = Falyx()
|
||||||
|
|
||||||
|
flx.add_command(
|
||||||
|
key="S",
|
||||||
|
description="Select a deployment",
|
||||||
|
action=select,
|
||||||
|
help_text="Select a deployment from the list",
|
||||||
|
)
|
||||||
|
flx.add_command(
|
||||||
|
key="L",
|
||||||
|
description="Select deployments",
|
||||||
|
action=list_select,
|
||||||
|
help_text="Select multiple deployments from the list",
|
||||||
|
)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(asyncio.run(select()))
|
||||||
|
except CancelSignal:
|
||||||
|
print("Selection was cancelled.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(asyncio.run(list_select()))
|
||||||
|
except CancelSignal:
|
||||||
|
print("Selection was cancelled.")
|
||||||
|
|
||||||
|
asyncio.run(flx.run())
|
||||||
|
@ -2,7 +2,7 @@ import asyncio
|
|||||||
from uuid import UUID, uuid4
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
from falyx import Falyx
|
from falyx import Falyx
|
||||||
from falyx.parsers import CommandArgumentParser
|
from falyx.parser import CommandArgumentParser
|
||||||
|
|
||||||
flx = Falyx("Test Type Validation")
|
flx = Falyx("Test Type Validation")
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ from typing import Any
|
|||||||
|
|
||||||
from falyx.config import loader
|
from falyx.config import loader
|
||||||
from falyx.falyx import Falyx
|
from falyx.falyx import Falyx
|
||||||
from falyx.parsers import CommandArgumentParser, get_root_parser, get_subparsers
|
from falyx.parser import CommandArgumentParser, get_root_parser, get_subparsers
|
||||||
|
|
||||||
|
|
||||||
def find_falyx_config() -> Path | None:
|
def find_falyx_config() -> Path | None:
|
||||||
|
@ -157,6 +157,6 @@ class Action(BaseAction):
|
|||||||
return (
|
return (
|
||||||
f"Action(name={self.name!r}, action="
|
f"Action(name={self.name!r}, action="
|
||||||
f"{getattr(self._action, '__name__', repr(self._action))}, "
|
f"{getattr(self._action, '__name__', repr(self._action))}, "
|
||||||
f"args={self.args!r}, kwargs={self.kwargs!r}, "
|
f"retry={self.retry_policy.enabled}, "
|
||||||
f"retry={self.retry_policy.enabled})"
|
f"rollback={self.rollback is not None})"
|
||||||
)
|
)
|
||||||
|
@ -11,7 +11,7 @@ from falyx.context import ExecutionContext, SharedContext
|
|||||||
from falyx.execution_registry import ExecutionRegistry as er
|
from falyx.execution_registry import ExecutionRegistry as er
|
||||||
from falyx.hook_manager import Hook, HookManager, HookType
|
from falyx.hook_manager import Hook, HookManager, HookType
|
||||||
from falyx.logger import logger
|
from falyx.logger import logger
|
||||||
from falyx.parsers.utils import same_argument_definitions
|
from falyx.parser.utils import same_argument_definitions
|
||||||
from falyx.themes.colors import OneColors
|
from falyx.themes.colors import OneColors
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,10 +93,7 @@ class BaseIOAction(BaseAction):
|
|||||||
if self.inject_last_result and self.shared_context:
|
if self.inject_last_result and self.shared_context:
|
||||||
return self.shared_context.last_result()
|
return self.shared_context.last_result()
|
||||||
|
|
||||||
logger.debug(
|
return ""
|
||||||
"[%s] No input provided and no last result found for injection.", self.name
|
|
||||||
)
|
|
||||||
raise FalyxError("No input provided and no last result to inject.")
|
|
||||||
|
|
||||||
def get_infer_target(self) -> tuple[Callable[..., Any] | None, dict[str, Any] | None]:
|
def get_infer_target(self) -> tuple[Callable[..., Any] | None, dict[str, Any] | None]:
|
||||||
return None, None
|
return None, None
|
||||||
|
@ -111,7 +111,7 @@ class MenuAction(BaseAction):
|
|||||||
key = effective_default
|
key = effective_default
|
||||||
if not self.never_prompt:
|
if not self.never_prompt:
|
||||||
table = self._build_table()
|
table = self._build_table()
|
||||||
key = await prompt_for_selection(
|
key_ = await prompt_for_selection(
|
||||||
self.menu_options.keys(),
|
self.menu_options.keys(),
|
||||||
table,
|
table,
|
||||||
default_selection=self.default_selection,
|
default_selection=self.default_selection,
|
||||||
@ -120,6 +120,10 @@ class MenuAction(BaseAction):
|
|||||||
prompt_message=self.prompt_message,
|
prompt_message=self.prompt_message,
|
||||||
show_table=self.show_table,
|
show_table=self.show_table,
|
||||||
)
|
)
|
||||||
|
if isinstance(key_, str):
|
||||||
|
key = key_
|
||||||
|
else:
|
||||||
|
assert False, "Unreachable, MenuAction only supports single selection"
|
||||||
option = self.menu_options[key]
|
option = self.menu_options[key]
|
||||||
result = await option.action(*args, **kwargs)
|
result = await option.action(*args, **kwargs)
|
||||||
context.result = result
|
context.result = result
|
||||||
|
@ -14,7 +14,7 @@ from falyx.context import ExecutionContext, SharedContext
|
|||||||
from falyx.execution_registry import ExecutionRegistry as er
|
from falyx.execution_registry import ExecutionRegistry as er
|
||||||
from falyx.hook_manager import HookManager, HookType
|
from falyx.hook_manager import HookManager, HookType
|
||||||
from falyx.logger import logger
|
from falyx.logger import logger
|
||||||
from falyx.parsers.utils import same_argument_definitions
|
from falyx.parser.utils import same_argument_definitions
|
||||||
from falyx.themes import OneColors
|
from falyx.themes import OneColors
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,6 +66,9 @@ class SelectFileAction(BaseAction):
|
|||||||
style: str = OneColors.WHITE,
|
style: str = OneColors.WHITE,
|
||||||
suffix_filter: str | None = None,
|
suffix_filter: str | None = None,
|
||||||
return_type: FileReturnType | str = FileReturnType.PATH,
|
return_type: FileReturnType | str = FileReturnType.PATH,
|
||||||
|
number_selections: int | str = 1,
|
||||||
|
separator: str = ",",
|
||||||
|
allow_duplicates: bool = False,
|
||||||
console: Console | None = None,
|
console: Console | None = None,
|
||||||
prompt_session: PromptSession | None = None,
|
prompt_session: PromptSession | None = None,
|
||||||
):
|
):
|
||||||
@ -76,6 +79,9 @@ class SelectFileAction(BaseAction):
|
|||||||
self.prompt_message = prompt_message
|
self.prompt_message = prompt_message
|
||||||
self.suffix_filter = suffix_filter
|
self.suffix_filter = suffix_filter
|
||||||
self.style = style
|
self.style = style
|
||||||
|
self.number_selections = number_selections
|
||||||
|
self.separator = separator
|
||||||
|
self.allow_duplicates = allow_duplicates
|
||||||
if isinstance(console, Console):
|
if isinstance(console, Console):
|
||||||
self.console = console
|
self.console = console
|
||||||
elif console:
|
elif console:
|
||||||
@ -83,6 +89,21 @@ class SelectFileAction(BaseAction):
|
|||||||
self.prompt_session = prompt_session or PromptSession()
|
self.prompt_session = prompt_session or PromptSession()
|
||||||
self.return_type = self._coerce_return_type(return_type)
|
self.return_type = self._coerce_return_type(return_type)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def number_selections(self) -> int | str:
|
||||||
|
return self._number_selections
|
||||||
|
|
||||||
|
@number_selections.setter
|
||||||
|
def number_selections(self, value: int | str):
|
||||||
|
if isinstance(value, int) and value > 0:
|
||||||
|
self._number_selections: int | str = value
|
||||||
|
elif isinstance(value, str):
|
||||||
|
if value not in ("*"):
|
||||||
|
raise ValueError("number_selections string must be one of '*'")
|
||||||
|
self._number_selections = value
|
||||||
|
else:
|
||||||
|
raise ValueError("number_selections must be a positive integer or one of '*'")
|
||||||
|
|
||||||
def _coerce_return_type(self, return_type: FileReturnType | str) -> FileReturnType:
|
def _coerce_return_type(self, return_type: FileReturnType | str) -> FileReturnType:
|
||||||
if isinstance(return_type, FileReturnType):
|
if isinstance(return_type, FileReturnType):
|
||||||
return return_type
|
return return_type
|
||||||
@ -163,18 +184,25 @@ class SelectFileAction(BaseAction):
|
|||||||
title=self.title, selections=options | cancel_option, columns=self.columns
|
title=self.title, selections=options | cancel_option, columns=self.columns
|
||||||
)
|
)
|
||||||
|
|
||||||
key = await prompt_for_selection(
|
keys = await prompt_for_selection(
|
||||||
(options | cancel_option).keys(),
|
(options | cancel_option).keys(),
|
||||||
table,
|
table,
|
||||||
console=self.console,
|
console=self.console,
|
||||||
prompt_session=self.prompt_session,
|
prompt_session=self.prompt_session,
|
||||||
prompt_message=self.prompt_message,
|
prompt_message=self.prompt_message,
|
||||||
|
number_selections=self.number_selections,
|
||||||
|
separator=self.separator,
|
||||||
|
allow_duplicates=self.allow_duplicates,
|
||||||
|
cancel_key=cancel_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
if key == cancel_key:
|
if isinstance(keys, str):
|
||||||
raise CancelSignal("User canceled the selection.")
|
if keys == cancel_key:
|
||||||
|
raise CancelSignal("User canceled the selection.")
|
||||||
|
result = options[keys].value
|
||||||
|
elif isinstance(keys, list):
|
||||||
|
result = [options[key].value for key in keys]
|
||||||
|
|
||||||
result = options[key].value
|
|
||||||
context.result = result
|
context.result = result
|
||||||
await self.hooks.trigger(HookType.ON_SUCCESS, context)
|
await self.hooks.trigger(HookType.ON_SUCCESS, context)
|
||||||
return result
|
return result
|
||||||
|
@ -48,6 +48,9 @@ class SelectionAction(BaseAction):
|
|||||||
columns: int = 5,
|
columns: int = 5,
|
||||||
prompt_message: str = "Select > ",
|
prompt_message: str = "Select > ",
|
||||||
default_selection: str = "",
|
default_selection: str = "",
|
||||||
|
number_selections: int | str = 1,
|
||||||
|
separator: str = ",",
|
||||||
|
allow_duplicates: bool = False,
|
||||||
inject_last_result: bool = False,
|
inject_last_result: bool = False,
|
||||||
inject_into: str = "last_result",
|
inject_into: str = "last_result",
|
||||||
return_type: SelectionReturnType | str = "value",
|
return_type: SelectionReturnType | str = "value",
|
||||||
@ -73,9 +76,26 @@ class SelectionAction(BaseAction):
|
|||||||
raise ValueError("`console` must be an instance of `rich.console.Console`")
|
raise ValueError("`console` must be an instance of `rich.console.Console`")
|
||||||
self.prompt_session = prompt_session or PromptSession()
|
self.prompt_session = prompt_session or PromptSession()
|
||||||
self.default_selection = default_selection
|
self.default_selection = default_selection
|
||||||
|
self.number_selections = number_selections
|
||||||
|
self.separator = separator
|
||||||
|
self.allow_duplicates = allow_duplicates
|
||||||
self.prompt_message = prompt_message
|
self.prompt_message = prompt_message
|
||||||
self.show_table = show_table
|
self.show_table = show_table
|
||||||
self.cancel_key = self._find_cancel_key()
|
|
||||||
|
@property
|
||||||
|
def number_selections(self) -> int | str:
|
||||||
|
return self._number_selections
|
||||||
|
|
||||||
|
@number_selections.setter
|
||||||
|
def number_selections(self, value: int | str):
|
||||||
|
if isinstance(value, int) and value > 0:
|
||||||
|
self._number_selections: int | str = value
|
||||||
|
elif isinstance(value, str):
|
||||||
|
if value not in ("*"):
|
||||||
|
raise ValueError("number_selections string must be '*'")
|
||||||
|
self._number_selections = value
|
||||||
|
else:
|
||||||
|
raise ValueError("number_selections must be a positive integer or '*'")
|
||||||
|
|
||||||
def _coerce_return_type(
|
def _coerce_return_type(
|
||||||
self, return_type: SelectionReturnType | str
|
self, return_type: SelectionReturnType | str
|
||||||
@ -156,6 +176,38 @@ class SelectionAction(BaseAction):
|
|||||||
def get_infer_target(self) -> tuple[None, None]:
|
def get_infer_target(self) -> tuple[None, None]:
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
def _get_result_from_keys(self, keys: str | list[str]) -> Any:
|
||||||
|
if not isinstance(self.selections, dict):
|
||||||
|
raise TypeError("Selections must be a dictionary to get result by keys.")
|
||||||
|
if self.return_type == SelectionReturnType.KEY:
|
||||||
|
result: Any = keys
|
||||||
|
elif self.return_type == SelectionReturnType.VALUE:
|
||||||
|
if isinstance(keys, list):
|
||||||
|
result = [self.selections[key].value for key in keys]
|
||||||
|
elif isinstance(keys, str):
|
||||||
|
result = self.selections[keys].value
|
||||||
|
elif self.return_type == SelectionReturnType.ITEMS:
|
||||||
|
if isinstance(keys, list):
|
||||||
|
result = {key: self.selections[key] for key in keys}
|
||||||
|
elif isinstance(keys, str):
|
||||||
|
result = {keys: self.selections[keys]}
|
||||||
|
elif self.return_type == SelectionReturnType.DESCRIPTION:
|
||||||
|
if isinstance(keys, list):
|
||||||
|
result = [self.selections[key].description for key in keys]
|
||||||
|
elif isinstance(keys, str):
|
||||||
|
result = self.selections[keys].description
|
||||||
|
elif self.return_type == SelectionReturnType.DESCRIPTION_VALUE:
|
||||||
|
if isinstance(keys, list):
|
||||||
|
result = {
|
||||||
|
self.selections[key].description: self.selections[key].value
|
||||||
|
for key in keys
|
||||||
|
}
|
||||||
|
elif isinstance(keys, str):
|
||||||
|
result = {self.selections[keys].description: self.selections[keys].value}
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unsupported return type: {self.return_type}")
|
||||||
|
return result
|
||||||
|
|
||||||
async def _run(self, *args, **kwargs) -> Any:
|
async def _run(self, *args, **kwargs) -> Any:
|
||||||
kwargs = self._maybe_inject_last_result(kwargs)
|
kwargs = self._maybe_inject_last_result(kwargs)
|
||||||
context = ExecutionContext(
|
context = ExecutionContext(
|
||||||
@ -191,7 +243,7 @@ class SelectionAction(BaseAction):
|
|||||||
if self.never_prompt and not effective_default:
|
if self.never_prompt and not effective_default:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"[{self.name}] 'never_prompt' is True but no valid default_selection "
|
f"[{self.name}] 'never_prompt' is True but no valid default_selection "
|
||||||
"was provided."
|
"or usable last_result was available."
|
||||||
)
|
)
|
||||||
|
|
||||||
context.start_timer()
|
context.start_timer()
|
||||||
@ -206,7 +258,7 @@ class SelectionAction(BaseAction):
|
|||||||
formatter=self.cancel_formatter,
|
formatter=self.cancel_formatter,
|
||||||
)
|
)
|
||||||
if not self.never_prompt:
|
if not self.never_prompt:
|
||||||
index: int | str = await prompt_for_index(
|
indices: int | list[int] = await prompt_for_index(
|
||||||
len(self.selections),
|
len(self.selections),
|
||||||
table,
|
table,
|
||||||
default_selection=effective_default,
|
default_selection=effective_default,
|
||||||
@ -214,12 +266,30 @@ class SelectionAction(BaseAction):
|
|||||||
prompt_session=self.prompt_session,
|
prompt_session=self.prompt_session,
|
||||||
prompt_message=self.prompt_message,
|
prompt_message=self.prompt_message,
|
||||||
show_table=self.show_table,
|
show_table=self.show_table,
|
||||||
|
number_selections=self.number_selections,
|
||||||
|
separator=self.separator,
|
||||||
|
allow_duplicates=self.allow_duplicates,
|
||||||
|
cancel_key=self.cancel_key,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
index = effective_default
|
if effective_default:
|
||||||
if int(index) == int(self.cancel_key):
|
indices = int(effective_default)
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"[{self.name}] 'never_prompt' is True but no valid "
|
||||||
|
"default_selection was provided."
|
||||||
|
)
|
||||||
|
|
||||||
|
if indices == int(self.cancel_key):
|
||||||
raise CancelSignal("User cancelled the selection.")
|
raise CancelSignal("User cancelled the selection.")
|
||||||
result: Any = self.selections[int(index)]
|
if isinstance(indices, list):
|
||||||
|
result: str | list[str] = [
|
||||||
|
self.selections[index] for index in indices
|
||||||
|
]
|
||||||
|
elif isinstance(indices, int):
|
||||||
|
result = self.selections[indices]
|
||||||
|
else:
|
||||||
|
assert False, "unreachable"
|
||||||
elif isinstance(self.selections, dict):
|
elif isinstance(self.selections, dict):
|
||||||
cancel_option = {
|
cancel_option = {
|
||||||
self.cancel_key: SelectionOption(
|
self.cancel_key: SelectionOption(
|
||||||
@ -232,7 +302,7 @@ class SelectionAction(BaseAction):
|
|||||||
columns=self.columns,
|
columns=self.columns,
|
||||||
)
|
)
|
||||||
if not self.never_prompt:
|
if not self.never_prompt:
|
||||||
key = await prompt_for_selection(
|
keys = await prompt_for_selection(
|
||||||
(self.selections | cancel_option).keys(),
|
(self.selections | cancel_option).keys(),
|
||||||
table,
|
table,
|
||||||
default_selection=effective_default,
|
default_selection=effective_default,
|
||||||
@ -240,25 +310,17 @@ class SelectionAction(BaseAction):
|
|||||||
prompt_session=self.prompt_session,
|
prompt_session=self.prompt_session,
|
||||||
prompt_message=self.prompt_message,
|
prompt_message=self.prompt_message,
|
||||||
show_table=self.show_table,
|
show_table=self.show_table,
|
||||||
|
number_selections=self.number_selections,
|
||||||
|
separator=self.separator,
|
||||||
|
allow_duplicates=self.allow_duplicates,
|
||||||
|
cancel_key=self.cancel_key,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
key = effective_default
|
keys = effective_default
|
||||||
if key == self.cancel_key:
|
if keys == self.cancel_key:
|
||||||
raise CancelSignal("User cancelled the selection.")
|
raise CancelSignal("User cancelled the selection.")
|
||||||
if self.return_type == SelectionReturnType.KEY:
|
|
||||||
result = key
|
result = self._get_result_from_keys(keys)
|
||||||
elif self.return_type == SelectionReturnType.VALUE:
|
|
||||||
result = self.selections[key].value
|
|
||||||
elif self.return_type == SelectionReturnType.ITEMS:
|
|
||||||
result = {key: self.selections[key]}
|
|
||||||
elif self.return_type == SelectionReturnType.DESCRIPTION:
|
|
||||||
result = self.selections[key].description
|
|
||||||
elif self.return_type == SelectionReturnType.DESCRIPTION_VALUE:
|
|
||||||
result = {
|
|
||||||
self.selections[key].description: self.selections[key].value
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Unsupported return type: {self.return_type}")
|
|
||||||
else:
|
else:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"'selections' must be a list[str] or dict[str, Any], "
|
"'selections' must be a list[str] or dict[str, Any], "
|
||||||
|
@ -29,6 +29,7 @@ class UserInputAction(BaseAction):
|
|||||||
name: str,
|
name: str,
|
||||||
*,
|
*,
|
||||||
prompt_text: str = "Input > ",
|
prompt_text: str = "Input > ",
|
||||||
|
default_text: str = "",
|
||||||
validator: Validator | None = None,
|
validator: Validator | None = None,
|
||||||
console: Console | None = None,
|
console: Console | None = None,
|
||||||
prompt_session: PromptSession | None = None,
|
prompt_session: PromptSession | None = None,
|
||||||
@ -45,6 +46,7 @@ class UserInputAction(BaseAction):
|
|||||||
elif console:
|
elif console:
|
||||||
raise ValueError("`console` must be an instance of `rich.console.Console`")
|
raise ValueError("`console` must be an instance of `rich.console.Console`")
|
||||||
self.prompt_session = prompt_session or PromptSession()
|
self.prompt_session = prompt_session or PromptSession()
|
||||||
|
self.default_text = default_text
|
||||||
|
|
||||||
def get_infer_target(self) -> tuple[None, None]:
|
def get_infer_target(self) -> tuple[None, None]:
|
||||||
return None, None
|
return None, None
|
||||||
@ -67,6 +69,7 @@ class UserInputAction(BaseAction):
|
|||||||
answer = await self.prompt_session.prompt_async(
|
answer = await self.prompt_session.prompt_async(
|
||||||
prompt_text,
|
prompt_text,
|
||||||
validator=self.validator,
|
validator=self.validator,
|
||||||
|
default=kwargs.get("default_text", self.default_text),
|
||||||
)
|
)
|
||||||
context.result = answer
|
context.result = answer
|
||||||
await self.hooks.trigger(HookType.ON_SUCCESS, context)
|
await self.hooks.trigger(HookType.ON_SUCCESS, context)
|
||||||
|
@ -34,8 +34,8 @@ from falyx.execution_registry import ExecutionRegistry as er
|
|||||||
from falyx.hook_manager import HookManager, HookType
|
from falyx.hook_manager import HookManager, HookType
|
||||||
from falyx.logger import logger
|
from falyx.logger import logger
|
||||||
from falyx.options_manager import OptionsManager
|
from falyx.options_manager import OptionsManager
|
||||||
from falyx.parsers.argparse import CommandArgumentParser
|
from falyx.parser.argparse import CommandArgumentParser
|
||||||
from falyx.parsers.signature import infer_args_from_func
|
from falyx.parser.signature import infer_args_from_func
|
||||||
from falyx.prompt_utils import confirm_async, should_prompt_user
|
from falyx.prompt_utils import confirm_async, should_prompt_user
|
||||||
from falyx.protocols import ArgParserProtocol
|
from falyx.protocols import ArgParserProtocol
|
||||||
from falyx.retry import RetryPolicy
|
from falyx.retry import RetryPolicy
|
||||||
|
@ -129,7 +129,7 @@ class ExecutionContext(BaseModel):
|
|||||||
args = ", ".join(map(repr, self.args))
|
args = ", ".join(map(repr, self.args))
|
||||||
kwargs = ", ".join(f"{key}={value!r}" for key, value in self.kwargs.items())
|
kwargs = ", ".join(f"{key}={value!r}" for key, value in self.kwargs.items())
|
||||||
signature = ", ".join(filter(None, [args, kwargs]))
|
signature = ", ".join(filter(None, [args, kwargs]))
|
||||||
return f"{self.name} ({signature})"
|
return f"{self.action} ({signature})"
|
||||||
|
|
||||||
def as_dict(self) -> dict:
|
def as_dict(self) -> dict:
|
||||||
return {
|
return {
|
||||||
|
@ -112,7 +112,7 @@ class ExecutionRegistry:
|
|||||||
cls,
|
cls,
|
||||||
name: str = "",
|
name: str = "",
|
||||||
index: int | None = None,
|
index: int | None = None,
|
||||||
result: int | None = None,
|
result_index: int | None = None,
|
||||||
clear: bool = False,
|
clear: bool = False,
|
||||||
last_result: bool = False,
|
last_result: bool = False,
|
||||||
status: Literal["all", "success", "error"] = "all",
|
status: Literal["all", "success", "error"] = "all",
|
||||||
@ -138,12 +138,12 @@ class ExecutionRegistry:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if result is not None and result >= 0:
|
if result_index is not None and result_index >= 0:
|
||||||
try:
|
try:
|
||||||
result_context = cls._store_by_index[result]
|
result_context = cls._store_by_index[result_index]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
cls._console.print(
|
cls._console.print(
|
||||||
f"[{OneColors.DARK_RED}]❌ No execution found for index {index}."
|
f"[{OneColors.DARK_RED}]❌ No execution found for index {result_index}."
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
cls._console.print(f"{result_context.signature}:")
|
cls._console.print(f"{result_context.signature}:")
|
||||||
|
@ -59,7 +59,7 @@ from falyx.execution_registry import ExecutionRegistry as er
|
|||||||
from falyx.hook_manager import Hook, HookManager, HookType
|
from falyx.hook_manager import Hook, HookManager, HookType
|
||||||
from falyx.logger import logger
|
from falyx.logger import logger
|
||||||
from falyx.options_manager import OptionsManager
|
from falyx.options_manager import OptionsManager
|
||||||
from falyx.parsers import CommandArgumentParser, FalyxParsers, get_arg_parsers
|
from falyx.parser import CommandArgumentParser, FalyxParsers, get_arg_parsers
|
||||||
from falyx.protocols import ArgParserProtocol
|
from falyx.protocols import ArgParserProtocol
|
||||||
from falyx.retry import RetryPolicy
|
from falyx.retry import RetryPolicy
|
||||||
from falyx.signals import BackSignal, CancelSignal, HelpSignal, QuitSignal
|
from falyx.signals import BackSignal, CancelSignal, HelpSignal, QuitSignal
|
||||||
@ -330,7 +330,13 @@ class Falyx:
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Clear the Execution History.",
|
help="Clear the Execution History.",
|
||||||
)
|
)
|
||||||
parser.add_argument("-r", "--result", type=int, help="Get the result by index")
|
parser.add_argument(
|
||||||
|
"-r",
|
||||||
|
"--result",
|
||||||
|
type=int,
|
||||||
|
dest="result_index",
|
||||||
|
help="Get the result by index",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-l", "--last-result", action="store_true", help="Get the last result"
|
"-l", "--last-result", action="store_true", help="Get the last result"
|
||||||
)
|
)
|
||||||
|
@ -12,7 +12,7 @@ from rich.text import Text
|
|||||||
|
|
||||||
from falyx.action.base import BaseAction
|
from falyx.action.base import BaseAction
|
||||||
from falyx.exceptions import CommandArgumentError
|
from falyx.exceptions import CommandArgumentError
|
||||||
from falyx.parsers.utils import coerce_value
|
from falyx.parser.utils import coerce_value
|
||||||
from falyx.signals import HelpSignal
|
from falyx.signals import HelpSignal
|
||||||
|
|
||||||
|
|
@ -7,7 +7,7 @@ from dateutil import parser as date_parser
|
|||||||
|
|
||||||
from falyx.action.base import BaseAction
|
from falyx.action.base import BaseAction
|
||||||
from falyx.logger import logger
|
from falyx.logger import logger
|
||||||
from falyx.parsers.signature import infer_args_from_func
|
from falyx.parser.signature import infer_args_from_func
|
||||||
|
|
||||||
|
|
||||||
def coerce_bool(value: str) -> bool:
|
def coerce_bool(value: str) -> bool:
|
@ -11,7 +11,7 @@ from rich.table import Table
|
|||||||
|
|
||||||
from falyx.themes import OneColors
|
from falyx.themes import OneColors
|
||||||
from falyx.utils import CaseInsensitiveDict, chunks
|
from falyx.utils import CaseInsensitiveDict, chunks
|
||||||
from falyx.validators import int_range_validator, key_validator
|
from falyx.validators import MultiIndexValidator, MultiKeyValidator
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -271,7 +271,11 @@ async def prompt_for_index(
|
|||||||
prompt_session: PromptSession | None = None,
|
prompt_session: PromptSession | None = None,
|
||||||
prompt_message: str = "Select an option > ",
|
prompt_message: str = "Select an option > ",
|
||||||
show_table: bool = True,
|
show_table: bool = True,
|
||||||
) -> int:
|
number_selections: int | str = 1,
|
||||||
|
separator: str = ",",
|
||||||
|
allow_duplicates: bool = False,
|
||||||
|
cancel_key: str = "",
|
||||||
|
) -> int | list[int]:
|
||||||
prompt_session = prompt_session or PromptSession()
|
prompt_session = prompt_session or PromptSession()
|
||||||
console = console or Console(color_system="truecolor")
|
console = console or Console(color_system="truecolor")
|
||||||
|
|
||||||
@ -280,10 +284,22 @@ async def prompt_for_index(
|
|||||||
|
|
||||||
selection = await prompt_session.prompt_async(
|
selection = await prompt_session.prompt_async(
|
||||||
message=prompt_message,
|
message=prompt_message,
|
||||||
validator=int_range_validator(min_index, max_index),
|
validator=MultiIndexValidator(
|
||||||
|
min_index,
|
||||||
|
max_index,
|
||||||
|
number_selections,
|
||||||
|
separator,
|
||||||
|
allow_duplicates,
|
||||||
|
cancel_key,
|
||||||
|
),
|
||||||
default=default_selection,
|
default=default_selection,
|
||||||
)
|
)
|
||||||
return int(selection)
|
|
||||||
|
if selection.strip() == cancel_key:
|
||||||
|
return int(cancel_key)
|
||||||
|
if isinstance(number_selections, int) and number_selections == 1:
|
||||||
|
return int(selection.strip())
|
||||||
|
return [int(index.strip()) for index in selection.strip().split(separator)]
|
||||||
|
|
||||||
|
|
||||||
async def prompt_for_selection(
|
async def prompt_for_selection(
|
||||||
@ -295,7 +311,11 @@ async def prompt_for_selection(
|
|||||||
prompt_session: PromptSession | None = None,
|
prompt_session: PromptSession | None = None,
|
||||||
prompt_message: str = "Select an option > ",
|
prompt_message: str = "Select an option > ",
|
||||||
show_table: bool = True,
|
show_table: bool = True,
|
||||||
) -> str:
|
number_selections: int | str = 1,
|
||||||
|
separator: str = ",",
|
||||||
|
allow_duplicates: bool = False,
|
||||||
|
cancel_key: str = "",
|
||||||
|
) -> str | list[str]:
|
||||||
"""Prompt the user to select a key from a set of options. Return the selected key."""
|
"""Prompt the user to select a key from a set of options. Return the selected key."""
|
||||||
prompt_session = prompt_session or PromptSession()
|
prompt_session = prompt_session or PromptSession()
|
||||||
console = console or Console(color_system="truecolor")
|
console = console or Console(color_system="truecolor")
|
||||||
@ -305,11 +325,17 @@ async def prompt_for_selection(
|
|||||||
|
|
||||||
selected = await prompt_session.prompt_async(
|
selected = await prompt_session.prompt_async(
|
||||||
message=prompt_message,
|
message=prompt_message,
|
||||||
validator=key_validator(keys),
|
validator=MultiKeyValidator(
|
||||||
|
keys, number_selections, separator, allow_duplicates, cancel_key
|
||||||
|
),
|
||||||
default=default_selection,
|
default=default_selection,
|
||||||
)
|
)
|
||||||
|
|
||||||
return selected
|
if selected.strip() == cancel_key:
|
||||||
|
return cancel_key
|
||||||
|
if isinstance(number_selections, int) and number_selections == 1:
|
||||||
|
return selected.strip()
|
||||||
|
return [key.strip() for key in selected.strip().split(separator)]
|
||||||
|
|
||||||
|
|
||||||
async def select_value_from_list(
|
async def select_value_from_list(
|
||||||
@ -320,6 +346,10 @@ async def select_value_from_list(
|
|||||||
prompt_session: PromptSession | None = None,
|
prompt_session: PromptSession | None = None,
|
||||||
prompt_message: str = "Select an option > ",
|
prompt_message: str = "Select an option > ",
|
||||||
default_selection: str = "",
|
default_selection: str = "",
|
||||||
|
number_selections: int | str = 1,
|
||||||
|
separator: str = ",",
|
||||||
|
allow_duplicates: bool = False,
|
||||||
|
cancel_key: str = "",
|
||||||
columns: int = 4,
|
columns: int = 4,
|
||||||
caption: str = "",
|
caption: str = "",
|
||||||
box_style: box.Box = box.SIMPLE,
|
box_style: box.Box = box.SIMPLE,
|
||||||
@ -332,7 +362,7 @@ async def select_value_from_list(
|
|||||||
title_style: str = "",
|
title_style: str = "",
|
||||||
caption_style: str = "",
|
caption_style: str = "",
|
||||||
highlight: bool = False,
|
highlight: bool = False,
|
||||||
):
|
) -> str | list[str]:
|
||||||
"""Prompt for a selection. Return the selected item."""
|
"""Prompt for a selection. Return the selected item."""
|
||||||
table = render_selection_indexed_table(
|
table = render_selection_indexed_table(
|
||||||
title=title,
|
title=title,
|
||||||
@ -360,8 +390,14 @@ async def select_value_from_list(
|
|||||||
console=console,
|
console=console,
|
||||||
prompt_session=prompt_session,
|
prompt_session=prompt_session,
|
||||||
prompt_message=prompt_message,
|
prompt_message=prompt_message,
|
||||||
|
number_selections=number_selections,
|
||||||
|
separator=separator,
|
||||||
|
allow_duplicates=allow_duplicates,
|
||||||
|
cancel_key=cancel_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if isinstance(selection_index, list):
|
||||||
|
return [selections[i] for i in selection_index]
|
||||||
return selections[selection_index]
|
return selections[selection_index]
|
||||||
|
|
||||||
|
|
||||||
@ -373,7 +409,11 @@ async def select_key_from_dict(
|
|||||||
prompt_session: PromptSession | None = None,
|
prompt_session: PromptSession | None = None,
|
||||||
prompt_message: str = "Select an option > ",
|
prompt_message: str = "Select an option > ",
|
||||||
default_selection: str = "",
|
default_selection: str = "",
|
||||||
) -> Any:
|
number_selections: int | str = 1,
|
||||||
|
separator: str = ",",
|
||||||
|
allow_duplicates: bool = False,
|
||||||
|
cancel_key: str = "",
|
||||||
|
) -> str | list[str]:
|
||||||
"""Prompt for a key from a dict, returns the key."""
|
"""Prompt for a key from a dict, returns the key."""
|
||||||
prompt_session = prompt_session or PromptSession()
|
prompt_session = prompt_session or PromptSession()
|
||||||
console = console or Console(color_system="truecolor")
|
console = console or Console(color_system="truecolor")
|
||||||
@ -387,6 +427,10 @@ async def select_key_from_dict(
|
|||||||
console=console,
|
console=console,
|
||||||
prompt_session=prompt_session,
|
prompt_session=prompt_session,
|
||||||
prompt_message=prompt_message,
|
prompt_message=prompt_message,
|
||||||
|
number_selections=number_selections,
|
||||||
|
separator=separator,
|
||||||
|
allow_duplicates=allow_duplicates,
|
||||||
|
cancel_key=cancel_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -398,7 +442,11 @@ async def select_value_from_dict(
|
|||||||
prompt_session: PromptSession | None = None,
|
prompt_session: PromptSession | None = None,
|
||||||
prompt_message: str = "Select an option > ",
|
prompt_message: str = "Select an option > ",
|
||||||
default_selection: str = "",
|
default_selection: str = "",
|
||||||
) -> Any:
|
number_selections: int | str = 1,
|
||||||
|
separator: str = ",",
|
||||||
|
allow_duplicates: bool = False,
|
||||||
|
cancel_key: str = "",
|
||||||
|
) -> Any | list[Any]:
|
||||||
"""Prompt for a key from a dict, but return the value."""
|
"""Prompt for a key from a dict, but return the value."""
|
||||||
prompt_session = prompt_session or PromptSession()
|
prompt_session = prompt_session or PromptSession()
|
||||||
console = console or Console(color_system="truecolor")
|
console = console or Console(color_system="truecolor")
|
||||||
@ -412,8 +460,14 @@ async def select_value_from_dict(
|
|||||||
console=console,
|
console=console,
|
||||||
prompt_session=prompt_session,
|
prompt_session=prompt_session,
|
||||||
prompt_message=prompt_message,
|
prompt_message=prompt_message,
|
||||||
|
number_selections=number_selections,
|
||||||
|
separator=separator,
|
||||||
|
allow_duplicates=allow_duplicates,
|
||||||
|
cancel_key=cancel_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if isinstance(selection_key, list):
|
||||||
|
return [selections[key].value for key in selection_key]
|
||||||
return selections[selection_key].value
|
return selections[selection_key].value
|
||||||
|
|
||||||
|
|
||||||
@ -425,7 +479,11 @@ async def get_selection_from_dict_menu(
|
|||||||
prompt_session: PromptSession | None = None,
|
prompt_session: PromptSession | None = None,
|
||||||
prompt_message: str = "Select an option > ",
|
prompt_message: str = "Select an option > ",
|
||||||
default_selection: str = "",
|
default_selection: str = "",
|
||||||
):
|
number_selections: int | str = 1,
|
||||||
|
separator: str = ",",
|
||||||
|
allow_duplicates: bool = False,
|
||||||
|
cancel_key: str = "",
|
||||||
|
) -> Any | list[Any]:
|
||||||
"""Prompt for a key from a dict, but return the value."""
|
"""Prompt for a key from a dict, but return the value."""
|
||||||
table = render_selection_dict_table(
|
table = render_selection_dict_table(
|
||||||
title,
|
title,
|
||||||
@ -439,4 +497,8 @@ async def get_selection_from_dict_menu(
|
|||||||
prompt_session=prompt_session,
|
prompt_session=prompt_session,
|
||||||
prompt_message=prompt_message,
|
prompt_message=prompt_message,
|
||||||
default_selection=default_selection,
|
default_selection=default_selection,
|
||||||
|
number_selections=number_selections,
|
||||||
|
separator=separator,
|
||||||
|
allow_duplicates=allow_duplicates,
|
||||||
|
cancel_key=cancel_key,
|
||||||
)
|
)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"""validators.py"""
|
"""validators.py"""
|
||||||
from typing import KeysView, Sequence
|
from typing import KeysView, Sequence
|
||||||
|
|
||||||
from prompt_toolkit.validation import Validator
|
from prompt_toolkit.validation import ValidationError, Validator
|
||||||
|
|
||||||
|
|
||||||
def int_range_validator(minimum: int, maximum: int) -> Validator:
|
def int_range_validator(minimum: int, maximum: int) -> Validator:
|
||||||
@ -45,3 +45,91 @@ def yes_no_validator() -> Validator:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
return Validator.from_callable(validate, error_message="Enter 'Y' or 'n'.")
|
return Validator.from_callable(validate, error_message="Enter 'Y' or 'n'.")
|
||||||
|
|
||||||
|
|
||||||
|
class MultiIndexValidator(Validator):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
minimum: int,
|
||||||
|
maximum: int,
|
||||||
|
number_selections: int | str,
|
||||||
|
separator: str,
|
||||||
|
allow_duplicates: bool,
|
||||||
|
cancel_key: str,
|
||||||
|
) -> None:
|
||||||
|
self.minimum = minimum
|
||||||
|
self.maximum = maximum
|
||||||
|
self.number_selections = number_selections
|
||||||
|
self.separator = separator
|
||||||
|
self.allow_duplicates = allow_duplicates
|
||||||
|
self.cancel_key = cancel_key
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def validate(self, document):
|
||||||
|
selections = [
|
||||||
|
index.strip() for index in document.text.strip().split(self.separator)
|
||||||
|
]
|
||||||
|
if not selections or selections == [""]:
|
||||||
|
raise ValidationError(message="Select at least 1 item.")
|
||||||
|
if self.cancel_key in selections and len(selections) == 1:
|
||||||
|
return
|
||||||
|
elif self.cancel_key in selections:
|
||||||
|
raise ValidationError(message="Cancel key must be selected alone.")
|
||||||
|
for selection in selections:
|
||||||
|
try:
|
||||||
|
index = int(selection)
|
||||||
|
if not self.minimum <= index <= self.maximum:
|
||||||
|
raise ValidationError(
|
||||||
|
message=f"Invalid selection: {selection}. Select a number between {self.minimum} and {self.maximum}."
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
raise ValidationError(
|
||||||
|
message=f"Invalid selection: {selection}. Select a number between {self.minimum} and {self.maximum}."
|
||||||
|
)
|
||||||
|
if not self.allow_duplicates and selections.count(selection) > 1:
|
||||||
|
raise ValidationError(message=f"Duplicate selection: {selection}")
|
||||||
|
if isinstance(self.number_selections, int):
|
||||||
|
if self.number_selections == 1 and len(selections) > 1:
|
||||||
|
raise ValidationError(message="Invalid selection. Select only 1 item.")
|
||||||
|
if len(selections) != self.number_selections:
|
||||||
|
raise ValidationError(
|
||||||
|
message=f"Select exactly {self.number_selections} items separated by '{self.separator}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MultiKeyValidator(Validator):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
keys: Sequence[str] | KeysView[str],
|
||||||
|
number_selections: int | str,
|
||||||
|
separator: str,
|
||||||
|
allow_duplicates: bool,
|
||||||
|
cancel_key: str,
|
||||||
|
) -> None:
|
||||||
|
self.keys = keys
|
||||||
|
self.separator = separator
|
||||||
|
self.number_selections = number_selections
|
||||||
|
self.allow_duplicates = allow_duplicates
|
||||||
|
self.cancel_key = cancel_key
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def validate(self, document):
|
||||||
|
selections = [key.strip() for key in document.text.strip().split(self.separator)]
|
||||||
|
if not selections or selections == [""]:
|
||||||
|
raise ValidationError(message="Select at least 1 item.")
|
||||||
|
if self.cancel_key in selections and len(selections) == 1:
|
||||||
|
return
|
||||||
|
elif self.cancel_key in selections:
|
||||||
|
raise ValidationError(message="Cancel key must be selected alone.")
|
||||||
|
for selection in selections:
|
||||||
|
if selection.upper() not in [key.upper() for key in self.keys]:
|
||||||
|
raise ValidationError(message=f"Invalid selection: {selection}")
|
||||||
|
if not self.allow_duplicates and selections.count(selection) > 1:
|
||||||
|
raise ValidationError(message=f"Duplicate selection: {selection}")
|
||||||
|
if isinstance(self.number_selections, int):
|
||||||
|
if self.number_selections == 1 and len(selections) > 1:
|
||||||
|
raise ValidationError(message="Invalid selection. Select only 1 item.")
|
||||||
|
if len(selections) != self.number_selections:
|
||||||
|
raise ValidationError(
|
||||||
|
message=f"Select exactly {self.number_selections} items separated by '{self.separator}'"
|
||||||
|
)
|
||||||
|
@ -1 +1 @@
|
|||||||
__version__ = "0.1.50"
|
__version__ = "0.1.51"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "falyx"
|
name = "falyx"
|
||||||
version = "0.1.50"
|
version = "0.1.51"
|
||||||
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"
|
||||||
|
@ -38,13 +38,14 @@ async def test_action_async_callable():
|
|||||||
action = Action("test_action", async_callable)
|
action = Action("test_action", async_callable)
|
||||||
result = await action()
|
result = await action()
|
||||||
assert result == "Hello, World!"
|
assert result == "Hello, World!"
|
||||||
|
print(action)
|
||||||
assert (
|
assert (
|
||||||
str(action)
|
str(action)
|
||||||
== "Action(name='test_action', action=async_callable, args=(), kwargs={}, retry=False)"
|
== "Action(name='test_action', action=async_callable, retry=False, rollback=False)"
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
repr(action)
|
repr(action)
|
||||||
== "Action(name='test_action', action=async_callable, args=(), kwargs={}, retry=False)"
|
== "Action(name='test_action', action=async_callable, retry=False, rollback=False)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,9 +50,10 @@ def test_command_str():
|
|||||||
"""Test if Command string representation is correct."""
|
"""Test if Command string representation is correct."""
|
||||||
action = Action("test_action", dummy_action)
|
action = Action("test_action", dummy_action)
|
||||||
cmd = Command(key="TEST", description="Test Command", action=action)
|
cmd = Command(key="TEST", description="Test Command", action=action)
|
||||||
|
print(cmd)
|
||||||
assert (
|
assert (
|
||||||
str(cmd)
|
str(cmd)
|
||||||
== "Command(key='TEST', description='Test Command' action='Action(name='test_action', action=dummy_action, args=(), kwargs={}, retry=False)')"
|
== "Command(key='TEST', description='Test Command' action='Action(name='test_action', action=dummy_action, retry=False, rollback=False)')"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from falyx.exceptions import CommandArgumentError
|
from falyx.exceptions import CommandArgumentError
|
||||||
from falyx.parsers import ArgumentAction, CommandArgumentParser
|
from falyx.parser import ArgumentAction, CommandArgumentParser
|
||||||
from falyx.signals import HelpSignal
|
from falyx.signals import HelpSignal
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import pytest
|
|||||||
|
|
||||||
from falyx.action import Action, SelectionAction
|
from falyx.action import Action, SelectionAction
|
||||||
from falyx.exceptions import CommandArgumentError
|
from falyx.exceptions import CommandArgumentError
|
||||||
from falyx.parsers import ArgumentAction, CommandArgumentParser
|
from falyx.parser import ArgumentAction, CommandArgumentParser
|
||||||
|
|
||||||
|
|
||||||
def test_add_argument():
|
def test_add_argument():
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from falyx.parsers import Argument, ArgumentAction
|
from falyx.parser import Argument, ArgumentAction
|
||||||
|
|
||||||
|
|
||||||
def test_positional_text_with_choices():
|
def test_positional_text_with_choices():
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from falyx.parsers import ArgumentAction
|
from falyx.parser import ArgumentAction
|
||||||
|
|
||||||
|
|
||||||
def test_argument_action():
|
def test_argument_action():
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from falyx.exceptions import CommandArgumentError
|
from falyx.exceptions import CommandArgumentError
|
||||||
from falyx.parsers import CommandArgumentParser
|
from falyx.parser import CommandArgumentParser
|
||||||
|
|
||||||
|
|
||||||
def test_str():
|
def test_str():
|
||||||
|
@ -5,7 +5,7 @@ from typing import Literal
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from falyx.parsers.utils import coerce_value
|
from falyx.parser.utils import coerce_value
|
||||||
|
|
||||||
|
|
||||||
# --- Tests ---
|
# --- Tests ---
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from falyx.exceptions import CommandArgumentError
|
from falyx.exceptions import CommandArgumentError
|
||||||
from falyx.parsers import ArgumentAction, CommandArgumentParser
|
from falyx.parser import ArgumentAction, CommandArgumentParser
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from falyx.exceptions import CommandArgumentError
|
from falyx.exceptions import CommandArgumentError
|
||||||
from falyx.parsers import ArgumentAction, CommandArgumentParser
|
from falyx.parser import ArgumentAction, CommandArgumentParser
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
Reference in New Issue
Block a user