falyx 0.1.26__tar.gz → 0.1.28__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {falyx-0.1.26 → falyx-0.1.28}/PKG-INFO +1 -1
  2. {falyx-0.1.26 → falyx-0.1.28}/falyx/action/__init__.py +2 -0
  3. {falyx-0.1.26 → falyx-0.1.28}/falyx/action/action_factory.py +11 -2
  4. {falyx-0.1.26 → falyx-0.1.28}/falyx/action/menu_action.py +4 -0
  5. falyx-0.1.28/falyx/action/user_input_action.py +94 -0
  6. falyx-0.1.28/falyx/argparse.py +596 -0
  7. {falyx-0.1.26 → falyx-0.1.28}/falyx/command.py +35 -4
  8. {falyx-0.1.26 → falyx-0.1.28}/falyx/exceptions.py +4 -0
  9. {falyx-0.1.26 → falyx-0.1.28}/falyx/execution_registry.py +1 -1
  10. {falyx-0.1.26 → falyx-0.1.28}/falyx/falyx.py +95 -43
  11. {falyx-0.1.26 → falyx-0.1.28}/falyx/parsers.py +7 -1
  12. falyx-0.1.28/falyx/protocols.py +17 -0
  13. {falyx-0.1.26 → falyx-0.1.28}/falyx/signals.py +14 -0
  14. {falyx-0.1.26 → falyx-0.1.28}/falyx/utils.py +1 -0
  15. falyx-0.1.28/falyx/version.py +1 -0
  16. {falyx-0.1.26 → falyx-0.1.28}/pyproject.toml +1 -1
  17. falyx-0.1.26/falyx/protocols.py +0 -11
  18. falyx-0.1.26/falyx/version.py +0 -1
  19. {falyx-0.1.26 → falyx-0.1.28}/LICENSE +0 -0
  20. {falyx-0.1.26 → falyx-0.1.28}/README.md +0 -0
  21. {falyx-0.1.26 → falyx-0.1.28}/falyx/.pytyped +0 -0
  22. {falyx-0.1.26 → falyx-0.1.28}/falyx/__init__.py +0 -0
  23. {falyx-0.1.26 → falyx-0.1.28}/falyx/__main__.py +0 -0
  24. {falyx-0.1.26 → falyx-0.1.28}/falyx/action/action.py +0 -0
  25. {falyx-0.1.26 → falyx-0.1.28}/falyx/action/http_action.py +0 -0
  26. {falyx-0.1.26 → falyx-0.1.28}/falyx/action/io_action.py +0 -0
  27. {falyx-0.1.26 → falyx-0.1.28}/falyx/action/select_file_action.py +0 -0
  28. {falyx-0.1.26 → falyx-0.1.28}/falyx/action/selection_action.py +0 -0
  29. {falyx-0.1.26 → falyx-0.1.28}/falyx/action/signal_action.py +0 -0
  30. {falyx-0.1.26 → falyx-0.1.28}/falyx/action/types.py +0 -0
  31. {falyx-0.1.26 → falyx-0.1.28}/falyx/bottom_bar.py +0 -0
  32. {falyx-0.1.26 → falyx-0.1.28}/falyx/config.py +0 -0
  33. {falyx-0.1.26 → falyx-0.1.28}/falyx/config_schema.py +0 -0
  34. {falyx-0.1.26 → falyx-0.1.28}/falyx/context.py +0 -0
  35. {falyx-0.1.26 → falyx-0.1.28}/falyx/debug.py +0 -0
  36. {falyx-0.1.26 → falyx-0.1.28}/falyx/hook_manager.py +0 -0
  37. {falyx-0.1.26 → falyx-0.1.28}/falyx/hooks.py +0 -0
  38. {falyx-0.1.26 → falyx-0.1.28}/falyx/init.py +0 -0
  39. {falyx-0.1.26 → falyx-0.1.28}/falyx/logger.py +0 -0
  40. {falyx-0.1.26 → falyx-0.1.28}/falyx/menu.py +0 -0
  41. {falyx-0.1.26 → falyx-0.1.28}/falyx/options_manager.py +0 -0
  42. {falyx-0.1.26 → falyx-0.1.28}/falyx/prompt_utils.py +0 -0
  43. {falyx-0.1.26 → falyx-0.1.28}/falyx/retry.py +0 -0
  44. {falyx-0.1.26 → falyx-0.1.28}/falyx/retry_utils.py +0 -0
  45. {falyx-0.1.26 → falyx-0.1.28}/falyx/selection.py +0 -0
  46. {falyx-0.1.26 → falyx-0.1.28}/falyx/tagged_table.py +0 -0
  47. {falyx-0.1.26 → falyx-0.1.28}/falyx/themes/__init__.py +0 -0
  48. {falyx-0.1.26 → falyx-0.1.28}/falyx/themes/colors.py +0 -0
  49. {falyx-0.1.26 → falyx-0.1.28}/falyx/validators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: falyx
3
- Version: 0.1.26
3
+ Version: 0.1.28
4
4
  Summary: Reliable and introspectable async CLI action framework.
5
5
  License: MIT
6
6
  Author: Roland Thomas Jr
@@ -21,6 +21,7 @@ from .menu_action import MenuAction
21
21
  from .select_file_action import SelectFileAction
22
22
  from .selection_action import SelectionAction
23
23
  from .signal_action import SignalAction
24
+ from .user_input_action import UserInputAction
24
25
 
25
26
  __all__ = [
26
27
  "Action",
@@ -38,4 +39,5 @@ __all__ = [
38
39
  "SignalAction",
39
40
  "FallbackAction",
40
41
  "LiteralInputAction",
42
+ "UserInputAction",
41
43
  ]
@@ -11,6 +11,7 @@ from falyx.hook_manager import HookType
11
11
  from falyx.logger import logger
12
12
  from falyx.protocols import ActionFactoryProtocol
13
13
  from falyx.themes import OneColors
14
+ from falyx.utils import ensure_async
14
15
 
15
16
 
16
17
  class ActionFactoryAction(BaseAction):
@@ -46,6 +47,14 @@ class ActionFactoryAction(BaseAction):
46
47
  self.preview_args = preview_args
47
48
  self.preview_kwargs = preview_kwargs or {}
48
49
 
50
+ @property
51
+ def factory(self) -> ActionFactoryProtocol:
52
+ return self._factory # type: ignore[return-value]
53
+
54
+ @factory.setter
55
+ def factory(self, value: ActionFactoryProtocol):
56
+ self._factory = ensure_async(value)
57
+
49
58
  async def _run(self, *args, **kwargs) -> Any:
50
59
  updated_kwargs = self._maybe_inject_last_result(kwargs)
51
60
  context = ExecutionContext(
@@ -57,7 +66,7 @@ class ActionFactoryAction(BaseAction):
57
66
  context.start_timer()
58
67
  try:
59
68
  await self.hooks.trigger(HookType.BEFORE, context)
60
- generated_action = self.factory(*args, **updated_kwargs)
69
+ generated_action = await self.factory(*args, **updated_kwargs)
61
70
  if not isinstance(generated_action, BaseAction):
62
71
  raise TypeError(
63
72
  f"[{self.name}] Factory must return a BaseAction, got "
@@ -94,7 +103,7 @@ class ActionFactoryAction(BaseAction):
94
103
  tree = parent.add(label) if parent else Tree(label)
95
104
 
96
105
  try:
97
- generated = self.factory(*self.preview_args, **self.preview_kwargs)
106
+ generated = await self.factory(*self.preview_args, **self.preview_kwargs)
98
107
  if isinstance(generated, BaseAction):
99
108
  await generated.preview(parent=tree)
100
109
  else:
@@ -38,6 +38,7 @@ class MenuAction(BaseAction):
38
38
  never_prompt: bool = False,
39
39
  include_reserved: bool = True,
40
40
  show_table: bool = True,
41
+ custom_table: Table | None = None,
41
42
  ):
42
43
  super().__init__(
43
44
  name,
@@ -54,8 +55,11 @@ class MenuAction(BaseAction):
54
55
  self.prompt_session = prompt_session or PromptSession()
55
56
  self.include_reserved = include_reserved
56
57
  self.show_table = show_table
58
+ self.custom_table = custom_table
57
59
 
58
60
  def _build_table(self) -> Table:
61
+ if self.custom_table:
62
+ return self.custom_table
59
63
  table = render_table_base(
60
64
  title=self.title,
61
65
  columns=self.columns,
@@ -0,0 +1,94 @@
1
+ from prompt_toolkit import PromptSession
2
+ from prompt_toolkit.validation import Validator
3
+ from rich.console import Console
4
+ from rich.tree import Tree
5
+
6
+ from falyx.action import BaseAction
7
+ from falyx.context import ExecutionContext
8
+ from falyx.execution_registry import ExecutionRegistry as er
9
+ from falyx.hook_manager import HookType
10
+ from falyx.themes.colors import OneColors
11
+
12
+
13
+ class UserInputAction(BaseAction):
14
+ """
15
+ Prompts the user for input via PromptSession and returns the result.
16
+
17
+ Args:
18
+ name (str): Action name.
19
+ prompt_text (str): Prompt text (can include '{last_result}' for interpolation).
20
+ validator (Validator, optional): Prompt Toolkit validator.
21
+ console (Console, optional): Rich console for rendering.
22
+ prompt_session (PromptSession, optional): Reusable prompt session.
23
+ inject_last_result (bool): Whether to inject last_result into prompt.
24
+ inject_into (str): Key to use for injection (default: 'last_result').
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ name: str,
30
+ *,
31
+ prompt_text: str = "Input > ",
32
+ validator: Validator | None = None,
33
+ console: Console | None = None,
34
+ prompt_session: PromptSession | None = None,
35
+ inject_last_result: bool = False,
36
+ ):
37
+ super().__init__(
38
+ name=name,
39
+ inject_last_result=inject_last_result,
40
+ )
41
+ self.prompt_text = prompt_text
42
+ self.validator = validator
43
+ self.console = console or Console(color_system="auto")
44
+ self.prompt_session = prompt_session or PromptSession()
45
+
46
+ async def _run(self, *args, **kwargs) -> str:
47
+ context = ExecutionContext(
48
+ name=self.name,
49
+ args=args,
50
+ kwargs=kwargs,
51
+ action=self,
52
+ )
53
+ context.start_timer()
54
+ try:
55
+ await self.hooks.trigger(HookType.BEFORE, context)
56
+
57
+ prompt_text = self.prompt_text
58
+ if self.inject_last_result and self.last_result:
59
+ prompt_text = prompt_text.format(last_result=self.last_result)
60
+
61
+ answer = await self.prompt_session.prompt_async(
62
+ prompt_text,
63
+ validator=self.validator,
64
+ )
65
+ context.result = answer
66
+ await self.hooks.trigger(HookType.ON_SUCCESS, context)
67
+ return answer
68
+ except Exception as error:
69
+ context.exception = error
70
+ await self.hooks.trigger(HookType.ON_ERROR, context)
71
+ raise
72
+ finally:
73
+ context.stop_timer()
74
+ await self.hooks.trigger(HookType.AFTER, context)
75
+ await self.hooks.trigger(HookType.ON_TEARDOWN, context)
76
+ er.record(context)
77
+
78
+ async def preview(self, parent: Tree | None = None):
79
+ label = f"[{OneColors.MAGENTA}]⌨ UserInputAction[/] '{self.name}'"
80
+ tree = parent.add(label) if parent else Tree(label)
81
+
82
+ prompt_text = (
83
+ self.prompt_text.replace("{last_result}", "<last_result>")
84
+ if "{last_result}" in self.prompt_text
85
+ else self.prompt_text
86
+ )
87
+ tree.add(f"[dim]Prompt:[/] {prompt_text}")
88
+ if self.validator:
89
+ tree.add("[dim]Validator:[/] Yes")
90
+ if not parent:
91
+ self.console.print(tree)
92
+
93
+ def __str__(self):
94
+ return f"UserInputAction(name={self.name!r}, prompt={self.prompt!r})"