falyx 0.1.37__py3-none-any.whl → 0.1.39__py3-none-any.whl
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.
- falyx/__init__.py +0 -11
- falyx/action/__init__.py +9 -9
- falyx/action/action.py +4 -724
- falyx/action/action_factory.py +1 -1
- falyx/action/action_group.py +170 -0
- falyx/action/base.py +156 -0
- falyx/action/chained_action.py +208 -0
- falyx/action/fallback_action.py +49 -0
- falyx/action/io_action.py +1 -1
- falyx/action/literal_input_action.py +47 -0
- falyx/action/menu_action.py +1 -1
- falyx/action/mixins.py +33 -0
- falyx/action/process_action.py +128 -0
- falyx/action/process_pool_action.py +166 -0
- falyx/action/prompt_menu_action.py +1 -1
- falyx/action/select_file_action.py +1 -1
- falyx/action/selection_action.py +1 -2
- falyx/action/user_input_action.py +1 -1
- falyx/command.py +2 -1
- falyx/config.py +2 -1
- falyx/falyx.py +21 -13
- falyx/menu.py +1 -1
- falyx/parsers/argparse.py +152 -53
- falyx/parsers/utils.py +1 -1
- falyx/protocols.py +1 -1
- falyx/retry_utils.py +2 -1
- falyx/utils.py +1 -1
- falyx/version.py +1 -1
- {falyx-0.1.37.dist-info → falyx-0.1.39.dist-info}/METADATA +6 -5
- falyx-0.1.39.dist-info/RECORD +61 -0
- falyx-0.1.37.dist-info/RECORD +0 -53
- {falyx-0.1.37.dist-info → falyx-0.1.39.dist-info}/LICENSE +0 -0
- {falyx-0.1.37.dist-info → falyx-0.1.39.dist-info}/WHEEL +0 -0
- {falyx-0.1.37.dist-info → falyx-0.1.39.dist-info}/entry_points.txt +0 -0
falyx/action/mixins.py
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
from falyx.action.base import BaseAction
|
2
|
+
|
3
|
+
|
4
|
+
class ActionListMixin:
|
5
|
+
"""Mixin for managing a list of actions."""
|
6
|
+
|
7
|
+
def __init__(self) -> None:
|
8
|
+
self.actions: list[BaseAction] = []
|
9
|
+
|
10
|
+
def set_actions(self, actions: list[BaseAction]) -> None:
|
11
|
+
"""Replaces the current action list with a new one."""
|
12
|
+
self.actions.clear()
|
13
|
+
for action in actions:
|
14
|
+
self.add_action(action)
|
15
|
+
|
16
|
+
def add_action(self, action: BaseAction) -> None:
|
17
|
+
"""Adds an action to the list."""
|
18
|
+
self.actions.append(action)
|
19
|
+
|
20
|
+
def remove_action(self, name: str) -> None:
|
21
|
+
"""Removes an action by name."""
|
22
|
+
self.actions = [action for action in self.actions if action.name != name]
|
23
|
+
|
24
|
+
def has_action(self, name: str) -> bool:
|
25
|
+
"""Checks if an action with the given name exists."""
|
26
|
+
return any(action.name == name for action in self.actions)
|
27
|
+
|
28
|
+
def get_action(self, name: str) -> BaseAction | None:
|
29
|
+
"""Retrieves an action by name."""
|
30
|
+
for action in self.actions:
|
31
|
+
if action.name == name:
|
32
|
+
return action
|
33
|
+
return None
|
@@ -0,0 +1,128 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
from concurrent.futures import ProcessPoolExecutor
|
5
|
+
from functools import partial
|
6
|
+
from typing import Any, Callable
|
7
|
+
|
8
|
+
from rich.tree import Tree
|
9
|
+
|
10
|
+
from falyx.action.base import BaseAction
|
11
|
+
from falyx.context import ExecutionContext
|
12
|
+
from falyx.execution_registry import ExecutionRegistry as er
|
13
|
+
from falyx.hook_manager import HookManager, HookType
|
14
|
+
from falyx.themes import OneColors
|
15
|
+
|
16
|
+
|
17
|
+
class ProcessAction(BaseAction):
|
18
|
+
"""
|
19
|
+
ProcessAction runs a function in a separate process using ProcessPoolExecutor.
|
20
|
+
|
21
|
+
Features:
|
22
|
+
- Executes CPU-bound or blocking tasks without blocking the main event loop.
|
23
|
+
- Supports last_result injection into the subprocess.
|
24
|
+
- Validates that last_result is pickleable when injection is enabled.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
name (str): Name of the action.
|
28
|
+
func (Callable): Function to execute in a new process.
|
29
|
+
args (tuple, optional): Positional arguments.
|
30
|
+
kwargs (dict, optional): Keyword arguments.
|
31
|
+
hooks (HookManager, optional): Hook manager for lifecycle events.
|
32
|
+
executor (ProcessPoolExecutor, optional): Custom executor if desired.
|
33
|
+
inject_last_result (bool, optional): Inject last result into the function.
|
34
|
+
inject_into (str, optional): Name of the injected key.
|
35
|
+
"""
|
36
|
+
|
37
|
+
def __init__(
|
38
|
+
self,
|
39
|
+
name: str,
|
40
|
+
action: Callable[..., Any],
|
41
|
+
*,
|
42
|
+
args: tuple = (),
|
43
|
+
kwargs: dict[str, Any] | None = None,
|
44
|
+
hooks: HookManager | None = None,
|
45
|
+
executor: ProcessPoolExecutor | None = None,
|
46
|
+
inject_last_result: bool = False,
|
47
|
+
inject_into: str = "last_result",
|
48
|
+
):
|
49
|
+
super().__init__(
|
50
|
+
name,
|
51
|
+
hooks=hooks,
|
52
|
+
inject_last_result=inject_last_result,
|
53
|
+
inject_into=inject_into,
|
54
|
+
)
|
55
|
+
self.action = action
|
56
|
+
self.args = args
|
57
|
+
self.kwargs = kwargs or {}
|
58
|
+
self.executor = executor or ProcessPoolExecutor()
|
59
|
+
self.is_retryable = True
|
60
|
+
|
61
|
+
def get_infer_target(self) -> tuple[Callable[..., Any] | None, None]:
|
62
|
+
return self.action, None
|
63
|
+
|
64
|
+
async def _run(self, *args, **kwargs) -> Any:
|
65
|
+
if self.inject_last_result and self.shared_context:
|
66
|
+
last_result = self.shared_context.last_result()
|
67
|
+
if not self._validate_pickleable(last_result):
|
68
|
+
raise ValueError(
|
69
|
+
f"Cannot inject last result into {self.name}: "
|
70
|
+
f"last result is not pickleable."
|
71
|
+
)
|
72
|
+
combined_args = args + self.args
|
73
|
+
combined_kwargs = self._maybe_inject_last_result({**self.kwargs, **kwargs})
|
74
|
+
context = ExecutionContext(
|
75
|
+
name=self.name,
|
76
|
+
args=combined_args,
|
77
|
+
kwargs=combined_kwargs,
|
78
|
+
action=self,
|
79
|
+
)
|
80
|
+
loop = asyncio.get_running_loop()
|
81
|
+
|
82
|
+
context.start_timer()
|
83
|
+
try:
|
84
|
+
await self.hooks.trigger(HookType.BEFORE, context)
|
85
|
+
result = await loop.run_in_executor(
|
86
|
+
self.executor, partial(self.action, *combined_args, **combined_kwargs)
|
87
|
+
)
|
88
|
+
context.result = result
|
89
|
+
await self.hooks.trigger(HookType.ON_SUCCESS, context)
|
90
|
+
return result
|
91
|
+
except Exception as error:
|
92
|
+
context.exception = error
|
93
|
+
await self.hooks.trigger(HookType.ON_ERROR, context)
|
94
|
+
if context.result is not None:
|
95
|
+
return context.result
|
96
|
+
raise
|
97
|
+
finally:
|
98
|
+
context.stop_timer()
|
99
|
+
await self.hooks.trigger(HookType.AFTER, context)
|
100
|
+
await self.hooks.trigger(HookType.ON_TEARDOWN, context)
|
101
|
+
er.record(context)
|
102
|
+
|
103
|
+
def _validate_pickleable(self, obj: Any) -> bool:
|
104
|
+
try:
|
105
|
+
import pickle
|
106
|
+
|
107
|
+
pickle.dumps(obj)
|
108
|
+
return True
|
109
|
+
except (pickle.PicklingError, TypeError):
|
110
|
+
return False
|
111
|
+
|
112
|
+
async def preview(self, parent: Tree | None = None):
|
113
|
+
label = [
|
114
|
+
f"[{OneColors.DARK_YELLOW_b}]🧠 ProcessAction (new process)[/] '{self.name}'"
|
115
|
+
]
|
116
|
+
if self.inject_last_result:
|
117
|
+
label.append(f" [dim](injects '{self.inject_into}')[/dim]")
|
118
|
+
if parent:
|
119
|
+
parent.add("".join(label))
|
120
|
+
else:
|
121
|
+
self.console.print(Tree("".join(label)))
|
122
|
+
|
123
|
+
def __str__(self) -> str:
|
124
|
+
return (
|
125
|
+
f"ProcessAction(name={self.name!r}, "
|
126
|
+
f"action={getattr(self.action, '__name__', repr(self.action))}, "
|
127
|
+
f"args={self.args!r}, kwargs={self.kwargs!r})"
|
128
|
+
)
|
@@ -0,0 +1,166 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
import random
|
5
|
+
from concurrent.futures import ProcessPoolExecutor
|
6
|
+
from dataclasses import dataclass, field
|
7
|
+
from functools import partial
|
8
|
+
from typing import Any, Callable
|
9
|
+
|
10
|
+
from rich.tree import Tree
|
11
|
+
|
12
|
+
from falyx.action.base import BaseAction
|
13
|
+
from falyx.context import ExecutionContext, SharedContext
|
14
|
+
from falyx.execution_registry import ExecutionRegistry as er
|
15
|
+
from falyx.hook_manager import HookManager, HookType
|
16
|
+
from falyx.logger import logger
|
17
|
+
from falyx.parsers.utils import same_argument_definitions
|
18
|
+
from falyx.themes import OneColors
|
19
|
+
|
20
|
+
|
21
|
+
@dataclass
|
22
|
+
class ProcessTask:
|
23
|
+
task: Callable[..., Any]
|
24
|
+
args: tuple = ()
|
25
|
+
kwargs: dict[str, Any] = field(default_factory=dict)
|
26
|
+
|
27
|
+
def __post_init__(self):
|
28
|
+
if not callable(self.task):
|
29
|
+
raise TypeError(f"Expected a callable task, got {type(self.task).__name__}")
|
30
|
+
|
31
|
+
|
32
|
+
class ProcessPoolAction(BaseAction):
|
33
|
+
""" """
|
34
|
+
|
35
|
+
def __init__(
|
36
|
+
self,
|
37
|
+
name: str,
|
38
|
+
actions: list[ProcessTask] | None = None,
|
39
|
+
*,
|
40
|
+
hooks: HookManager | None = None,
|
41
|
+
executor: ProcessPoolExecutor | None = None,
|
42
|
+
inject_last_result: bool = False,
|
43
|
+
inject_into: str = "last_result",
|
44
|
+
):
|
45
|
+
super().__init__(
|
46
|
+
name,
|
47
|
+
hooks=hooks,
|
48
|
+
inject_last_result=inject_last_result,
|
49
|
+
inject_into=inject_into,
|
50
|
+
)
|
51
|
+
self.executor = executor or ProcessPoolExecutor()
|
52
|
+
self.is_retryable = True
|
53
|
+
self.actions: list[ProcessTask] = []
|
54
|
+
if actions:
|
55
|
+
self.set_actions(actions)
|
56
|
+
|
57
|
+
def set_actions(self, actions: list[ProcessTask]) -> None:
|
58
|
+
"""Replaces the current action list with a new one."""
|
59
|
+
self.actions.clear()
|
60
|
+
for action in actions:
|
61
|
+
self.add_action(action)
|
62
|
+
|
63
|
+
def add_action(self, action: ProcessTask) -> None:
|
64
|
+
if not isinstance(action, ProcessTask):
|
65
|
+
raise TypeError(f"Expected a ProcessTask, got {type(action).__name__}")
|
66
|
+
self.actions.append(action)
|
67
|
+
|
68
|
+
def get_infer_target(self) -> tuple[Callable[..., Any] | None, None]:
|
69
|
+
arg_defs = same_argument_definitions([action.task for action in self.actions])
|
70
|
+
if arg_defs:
|
71
|
+
return self.actions[0].task, None
|
72
|
+
logger.debug(
|
73
|
+
"[%s] auto_args disabled: mismatched ProcessPoolAction arguments",
|
74
|
+
self.name,
|
75
|
+
)
|
76
|
+
return None, None
|
77
|
+
|
78
|
+
async def _run(self, *args, **kwargs) -> Any:
|
79
|
+
shared_context = SharedContext(name=self.name, action=self, is_parallel=True)
|
80
|
+
if self.shared_context:
|
81
|
+
shared_context.set_shared_result(self.shared_context.last_result())
|
82
|
+
if self.inject_last_result and self.shared_context:
|
83
|
+
last_result = self.shared_context.last_result()
|
84
|
+
if not self._validate_pickleable(last_result):
|
85
|
+
raise ValueError(
|
86
|
+
f"Cannot inject last result into {self.name}: "
|
87
|
+
f"last result is not pickleable."
|
88
|
+
)
|
89
|
+
print(kwargs)
|
90
|
+
updated_kwargs = self._maybe_inject_last_result(kwargs)
|
91
|
+
print(updated_kwargs)
|
92
|
+
context = ExecutionContext(
|
93
|
+
name=self.name,
|
94
|
+
args=args,
|
95
|
+
kwargs=updated_kwargs,
|
96
|
+
action=self,
|
97
|
+
)
|
98
|
+
loop = asyncio.get_running_loop()
|
99
|
+
|
100
|
+
context.start_timer()
|
101
|
+
try:
|
102
|
+
await self.hooks.trigger(HookType.BEFORE, context)
|
103
|
+
futures = [
|
104
|
+
loop.run_in_executor(
|
105
|
+
self.executor,
|
106
|
+
partial(
|
107
|
+
task.task,
|
108
|
+
*(*args, *task.args),
|
109
|
+
**{**updated_kwargs, **task.kwargs},
|
110
|
+
),
|
111
|
+
)
|
112
|
+
for task in self.actions
|
113
|
+
]
|
114
|
+
results = await asyncio.gather(*futures, return_exceptions=True)
|
115
|
+
context.result = results
|
116
|
+
await self.hooks.trigger(HookType.ON_SUCCESS, context)
|
117
|
+
return results
|
118
|
+
except Exception as error:
|
119
|
+
context.exception = error
|
120
|
+
await self.hooks.trigger(HookType.ON_ERROR, context)
|
121
|
+
if context.result is not None:
|
122
|
+
return context.result
|
123
|
+
raise
|
124
|
+
finally:
|
125
|
+
context.stop_timer()
|
126
|
+
await self.hooks.trigger(HookType.AFTER, context)
|
127
|
+
await self.hooks.trigger(HookType.ON_TEARDOWN, context)
|
128
|
+
er.record(context)
|
129
|
+
|
130
|
+
def _validate_pickleable(self, obj: Any) -> bool:
|
131
|
+
try:
|
132
|
+
import pickle
|
133
|
+
|
134
|
+
pickle.dumps(obj)
|
135
|
+
return True
|
136
|
+
except (pickle.PicklingError, TypeError):
|
137
|
+
return False
|
138
|
+
|
139
|
+
async def preview(self, parent: Tree | None = None):
|
140
|
+
label = [f"[{OneColors.DARK_YELLOW_b}]🧠 ProcessPoolAction[/] '{self.name}'"]
|
141
|
+
if self.inject_last_result:
|
142
|
+
label.append(f" [dim](receives '{self.inject_into}')[/dim]")
|
143
|
+
tree = parent.add("".join(label)) if parent else Tree("".join(label))
|
144
|
+
actions = self.actions.copy()
|
145
|
+
random.shuffle(actions)
|
146
|
+
for action in actions:
|
147
|
+
label = [
|
148
|
+
f"[{OneColors.DARK_YELLOW_b}] - {getattr(action.task, '__name__', repr(action.task))}[/] "
|
149
|
+
f"[dim]({', '.join(map(repr, action.args))})[/]"
|
150
|
+
]
|
151
|
+
if action.kwargs:
|
152
|
+
label.append(
|
153
|
+
f" [dim]({', '.join(f'{k}={v!r}' for k, v in action.kwargs.items())})[/]"
|
154
|
+
)
|
155
|
+
tree.add("".join(label))
|
156
|
+
|
157
|
+
if not parent:
|
158
|
+
self.console.print(tree)
|
159
|
+
|
160
|
+
def __str__(self) -> str:
|
161
|
+
return (
|
162
|
+
f"ProcessPoolAction(name={self.name!r}, "
|
163
|
+
f"actions={[getattr(action.task, '__name__', repr(action.task)) for action in self.actions]}, "
|
164
|
+
f"inject_last_result={self.inject_last_result}, "
|
165
|
+
f"inject_into={self.inject_into!r})"
|
166
|
+
)
|
@@ -7,7 +7,7 @@ from prompt_toolkit.formatted_text import FormattedText, merge_formatted_text
|
|
7
7
|
from rich.console import Console
|
8
8
|
from rich.tree import Tree
|
9
9
|
|
10
|
-
from falyx.action.
|
10
|
+
from falyx.action.base import BaseAction
|
11
11
|
from falyx.context import ExecutionContext
|
12
12
|
from falyx.execution_registry import ExecutionRegistry as er
|
13
13
|
from falyx.hook_manager import HookType
|
@@ -14,7 +14,7 @@ from prompt_toolkit import PromptSession
|
|
14
14
|
from rich.console import Console
|
15
15
|
from rich.tree import Tree
|
16
16
|
|
17
|
-
from falyx.action.
|
17
|
+
from falyx.action.base import BaseAction
|
18
18
|
from falyx.action.types import FileReturnType
|
19
19
|
from falyx.context import ExecutionContext
|
20
20
|
from falyx.execution_registry import ExecutionRegistry as er
|
falyx/action/selection_action.py
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
# Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed
|
2
2
|
"""selection_action.py"""
|
3
|
-
from copy import copy
|
4
3
|
from typing import Any
|
5
4
|
|
6
5
|
from prompt_toolkit import PromptSession
|
7
6
|
from rich.console import Console
|
8
7
|
from rich.tree import Tree
|
9
8
|
|
10
|
-
from falyx.action.
|
9
|
+
from falyx.action.base import BaseAction
|
11
10
|
from falyx.action.types import SelectionReturnType
|
12
11
|
from falyx.context import ExecutionContext
|
13
12
|
from falyx.execution_registry import ExecutionRegistry as er
|
@@ -3,7 +3,7 @@ from prompt_toolkit.validation import Validator
|
|
3
3
|
from rich.console import Console
|
4
4
|
from rich.tree import Tree
|
5
5
|
|
6
|
-
from falyx.action import BaseAction
|
6
|
+
from falyx.action.base import BaseAction
|
7
7
|
from falyx.context import ExecutionContext
|
8
8
|
from falyx.execution_registry import ExecutionRegistry as er
|
9
9
|
from falyx.hook_manager import HookType
|
falyx/command.py
CHANGED
@@ -26,7 +26,8 @@ from pydantic import BaseModel, ConfigDict, Field, PrivateAttr, field_validator
|
|
26
26
|
from rich.console import Console
|
27
27
|
from rich.tree import Tree
|
28
28
|
|
29
|
-
from falyx.action.action import Action
|
29
|
+
from falyx.action.action import Action
|
30
|
+
from falyx.action.base import BaseAction
|
30
31
|
from falyx.context import ExecutionContext
|
31
32
|
from falyx.debug import register_debug_hooks
|
32
33
|
from falyx.execution_registry import ExecutionRegistry as er
|
falyx/config.py
CHANGED
@@ -13,7 +13,8 @@ import yaml
|
|
13
13
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
14
14
|
from rich.console import Console
|
15
15
|
|
16
|
-
from falyx.action.action import Action
|
16
|
+
from falyx.action.action import Action
|
17
|
+
from falyx.action.base import BaseAction
|
17
18
|
from falyx.command import Command
|
18
19
|
from falyx.falyx import Falyx
|
19
20
|
from falyx.logger import logger
|
falyx/falyx.py
CHANGED
@@ -42,7 +42,8 @@ from rich.console import Console
|
|
42
42
|
from rich.markdown import Markdown
|
43
43
|
from rich.table import Table
|
44
44
|
|
45
|
-
from falyx.action.action import Action
|
45
|
+
from falyx.action.action import Action
|
46
|
+
from falyx.action.base import BaseAction
|
46
47
|
from falyx.bottom_bar import BottomBar
|
47
48
|
from falyx.command import Command
|
48
49
|
from falyx.context import ExecutionContext
|
@@ -82,7 +83,7 @@ class CommandValidator(Validator):
|
|
82
83
|
self.falyx = falyx
|
83
84
|
self.error_message = error_message
|
84
85
|
|
85
|
-
def validate(self,
|
86
|
+
def validate(self, _) -> None:
|
86
87
|
pass
|
87
88
|
|
88
89
|
async def validate_async(self, document) -> None:
|
@@ -449,7 +450,7 @@ class Falyx:
|
|
449
450
|
validator=CommandValidator(self, self._get_validator_error_message()),
|
450
451
|
bottom_toolbar=self._get_bottom_bar_render(),
|
451
452
|
key_bindings=self.key_bindings,
|
452
|
-
validate_while_typing=
|
453
|
+
validate_while_typing=True,
|
453
454
|
)
|
454
455
|
return self._prompt_session
|
455
456
|
|
@@ -761,7 +762,7 @@ class Falyx:
|
|
761
762
|
is_preview = False
|
762
763
|
choice = "?"
|
763
764
|
elif is_preview and not choice:
|
764
|
-
# No help command enabled
|
765
|
+
# No help (list) command enabled
|
765
766
|
if not from_validate:
|
766
767
|
self.console.print(
|
767
768
|
f"[{OneColors.DARK_RED}]❌ You must enter a command for preview mode."
|
@@ -781,12 +782,9 @@ class Falyx:
|
|
781
782
|
)
|
782
783
|
except CommandArgumentError as error:
|
783
784
|
if not from_validate:
|
784
|
-
if not name_map[choice].show_help():
|
785
|
-
self.console.print(
|
786
|
-
f"[{OneColors.DARK_RED}]❌ Invalid arguments for '{choice}': {error}"
|
787
|
-
)
|
788
|
-
else:
|
789
785
|
name_map[choice].show_help()
|
786
|
+
self.console.print(f"[{OneColors.DARK_RED}]❌ [{choice}]: {error}")
|
787
|
+
else:
|
790
788
|
raise ValidationError(
|
791
789
|
message=str(error), cursor_position=len(raw_choices)
|
792
790
|
)
|
@@ -806,14 +804,24 @@ class Falyx:
|
|
806
804
|
f"[{OneColors.LIGHT_YELLOW}]⚠️ Unknown command '{choice}'. "
|
807
805
|
"Did you mean:"
|
808
806
|
)
|
809
|
-
|
810
|
-
|
811
|
-
|
807
|
+
for match in fuzzy_matches:
|
808
|
+
cmd = name_map[match]
|
809
|
+
self.console.print(f" • [bold]{match}[/] → {cmd.description}")
|
810
|
+
else:
|
811
|
+
raise ValidationError(
|
812
|
+
message=f"Unknown command '{choice}'. Did you mean: "
|
813
|
+
f"{', '.join(fuzzy_matches)}?",
|
814
|
+
cursor_position=len(raw_choices),
|
815
|
+
)
|
812
816
|
else:
|
813
817
|
if not from_validate:
|
814
818
|
self.console.print(
|
815
819
|
f"[{OneColors.LIGHT_YELLOW}]⚠️ Unknown command '{choice}'[/]"
|
816
820
|
)
|
821
|
+
raise ValidationError(
|
822
|
+
message=f"Unknown command '{choice}'.",
|
823
|
+
cursor_position=len(raw_choices),
|
824
|
+
)
|
817
825
|
return is_preview, None, args, kwargs
|
818
826
|
|
819
827
|
def _create_context(self, selected_command: Command) -> ExecutionContext:
|
@@ -974,7 +982,7 @@ class Falyx:
|
|
974
982
|
|
975
983
|
async def menu(self) -> None:
|
976
984
|
"""Runs the menu and handles user input."""
|
977
|
-
logger.info("
|
985
|
+
logger.info("Starting menu: %s", self.get_title())
|
978
986
|
self.debug_hooks()
|
979
987
|
if self.welcome_message:
|
980
988
|
self.print_message(self.welcome_message)
|
falyx/menu.py
CHANGED
@@ -4,7 +4,7 @@ from dataclasses import dataclass
|
|
4
4
|
|
5
5
|
from prompt_toolkit.formatted_text import FormattedText
|
6
6
|
|
7
|
-
from falyx.action import BaseAction
|
7
|
+
from falyx.action.base import BaseAction
|
8
8
|
from falyx.signals import BackSignal, QuitSignal
|
9
9
|
from falyx.themes import OneColors
|
10
10
|
from falyx.utils import CaseInsensitiveDict
|