falyx 0.1.49__py3-none-any.whl → 0.1.51__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/__main__.py +1 -1
- falyx/action/action.py +2 -2
- falyx/action/action_group.py +1 -1
- falyx/action/io_action.py +1 -4
- falyx/action/menu_action.py +5 -1
- falyx/action/process_pool_action.py +1 -1
- falyx/action/select_file_action.py +32 -4
- falyx/action/selection_action.py +85 -23
- falyx/action/user_input_action.py +3 -0
- falyx/command.py +2 -2
- falyx/context.py +12 -1
- falyx/debug.py +1 -1
- falyx/execution_registry.py +13 -8
- falyx/falyx.py +41 -22
- falyx/{parsers → parser}/argparse.py +5 -2
- falyx/{parsers → parser}/utils.py +1 -1
- falyx/selection.py +73 -11
- falyx/validators.py +89 -1
- falyx/version.py +1 -1
- {falyx-0.1.49.dist-info → falyx-0.1.51.dist-info}/METADATA +1 -1
- {falyx-0.1.49.dist-info → falyx-0.1.51.dist-info}/RECORD +28 -28
- /falyx/{parsers → parser}/.pytyped +0 -0
- /falyx/{parsers → parser}/__init__.py +0 -0
- /falyx/{parsers → parser}/parsers.py +0 -0
- /falyx/{parsers → parser}/signature.py +0 -0
- {falyx-0.1.49.dist-info → falyx-0.1.51.dist-info}/LICENSE +0 -0
- {falyx-0.1.49.dist-info → falyx-0.1.51.dist-info}/WHEEL +0 -0
- {falyx-0.1.49.dist-info → falyx-0.1.51.dist-info}/entry_points.txt +0 -0
falyx/__main__.py
CHANGED
@@ -14,7 +14,7 @@ from typing import Any
|
|
14
14
|
|
15
15
|
from falyx.config import loader
|
16
16
|
from falyx.falyx import Falyx
|
17
|
-
from falyx.
|
17
|
+
from falyx.parser import CommandArgumentParser, get_root_parser, get_subparsers
|
18
18
|
|
19
19
|
|
20
20
|
def find_falyx_config() -> Path | None:
|
falyx/action/action.py
CHANGED
@@ -157,6 +157,6 @@ class Action(BaseAction):
|
|
157
157
|
return (
|
158
158
|
f"Action(name={self.name!r}, action="
|
159
159
|
f"{getattr(self._action, '__name__', repr(self._action))}, "
|
160
|
-
f"
|
161
|
-
f"
|
160
|
+
f"retry={self.retry_policy.enabled}, "
|
161
|
+
f"rollback={self.rollback is not None})"
|
162
162
|
)
|
falyx/action/action_group.py
CHANGED
@@ -11,7 +11,7 @@ from falyx.context import ExecutionContext, SharedContext
|
|
11
11
|
from falyx.execution_registry import ExecutionRegistry as er
|
12
12
|
from falyx.hook_manager import Hook, HookManager, HookType
|
13
13
|
from falyx.logger import logger
|
14
|
-
from falyx.
|
14
|
+
from falyx.parser.utils import same_argument_definitions
|
15
15
|
from falyx.themes.colors import OneColors
|
16
16
|
|
17
17
|
|
falyx/action/io_action.py
CHANGED
@@ -93,10 +93,7 @@ class BaseIOAction(BaseAction):
|
|
93
93
|
if self.inject_last_result and self.shared_context:
|
94
94
|
return self.shared_context.last_result()
|
95
95
|
|
96
|
-
|
97
|
-
"[%s] No input provided and no last result found for injection.", self.name
|
98
|
-
)
|
99
|
-
raise FalyxError("No input provided and no last result to inject.")
|
96
|
+
return ""
|
100
97
|
|
101
98
|
def get_infer_target(self) -> tuple[Callable[..., Any] | None, dict[str, Any] | None]:
|
102
99
|
return None, None
|
falyx/action/menu_action.py
CHANGED
@@ -111,7 +111,7 @@ class MenuAction(BaseAction):
|
|
111
111
|
key = effective_default
|
112
112
|
if not self.never_prompt:
|
113
113
|
table = self._build_table()
|
114
|
-
|
114
|
+
key_ = await prompt_for_selection(
|
115
115
|
self.menu_options.keys(),
|
116
116
|
table,
|
117
117
|
default_selection=self.default_selection,
|
@@ -120,6 +120,10 @@ class MenuAction(BaseAction):
|
|
120
120
|
prompt_message=self.prompt_message,
|
121
121
|
show_table=self.show_table,
|
122
122
|
)
|
123
|
+
if isinstance(key_, str):
|
124
|
+
key = key_
|
125
|
+
else:
|
126
|
+
assert False, "Unreachable, MenuAction only supports single selection"
|
123
127
|
option = self.menu_options[key]
|
124
128
|
result = await option.action(*args, **kwargs)
|
125
129
|
context.result = result
|
@@ -14,7 +14,7 @@ from falyx.context import ExecutionContext, SharedContext
|
|
14
14
|
from falyx.execution_registry import ExecutionRegistry as er
|
15
15
|
from falyx.hook_manager import HookManager, HookType
|
16
16
|
from falyx.logger import logger
|
17
|
-
from falyx.
|
17
|
+
from falyx.parser.utils import same_argument_definitions
|
18
18
|
from falyx.themes import OneColors
|
19
19
|
|
20
20
|
|
@@ -66,6 +66,9 @@ class SelectFileAction(BaseAction):
|
|
66
66
|
style: str = OneColors.WHITE,
|
67
67
|
suffix_filter: str | None = None,
|
68
68
|
return_type: FileReturnType | str = FileReturnType.PATH,
|
69
|
+
number_selections: int | str = 1,
|
70
|
+
separator: str = ",",
|
71
|
+
allow_duplicates: bool = False,
|
69
72
|
console: Console | None = None,
|
70
73
|
prompt_session: PromptSession | None = None,
|
71
74
|
):
|
@@ -76,6 +79,9 @@ class SelectFileAction(BaseAction):
|
|
76
79
|
self.prompt_message = prompt_message
|
77
80
|
self.suffix_filter = suffix_filter
|
78
81
|
self.style = style
|
82
|
+
self.number_selections = number_selections
|
83
|
+
self.separator = separator
|
84
|
+
self.allow_duplicates = allow_duplicates
|
79
85
|
if isinstance(console, Console):
|
80
86
|
self.console = console
|
81
87
|
elif console:
|
@@ -83,6 +89,21 @@ class SelectFileAction(BaseAction):
|
|
83
89
|
self.prompt_session = prompt_session or PromptSession()
|
84
90
|
self.return_type = self._coerce_return_type(return_type)
|
85
91
|
|
92
|
+
@property
|
93
|
+
def number_selections(self) -> int | str:
|
94
|
+
return self._number_selections
|
95
|
+
|
96
|
+
@number_selections.setter
|
97
|
+
def number_selections(self, value: int | str):
|
98
|
+
if isinstance(value, int) and value > 0:
|
99
|
+
self._number_selections: int | str = value
|
100
|
+
elif isinstance(value, str):
|
101
|
+
if value not in ("*"):
|
102
|
+
raise ValueError("number_selections string must be one of '*'")
|
103
|
+
self._number_selections = value
|
104
|
+
else:
|
105
|
+
raise ValueError("number_selections must be a positive integer or one of '*'")
|
106
|
+
|
86
107
|
def _coerce_return_type(self, return_type: FileReturnType | str) -> FileReturnType:
|
87
108
|
if isinstance(return_type, FileReturnType):
|
88
109
|
return return_type
|
@@ -163,18 +184,25 @@ class SelectFileAction(BaseAction):
|
|
163
184
|
title=self.title, selections=options | cancel_option, columns=self.columns
|
164
185
|
)
|
165
186
|
|
166
|
-
|
187
|
+
keys = await prompt_for_selection(
|
167
188
|
(options | cancel_option).keys(),
|
168
189
|
table,
|
169
190
|
console=self.console,
|
170
191
|
prompt_session=self.prompt_session,
|
171
192
|
prompt_message=self.prompt_message,
|
193
|
+
number_selections=self.number_selections,
|
194
|
+
separator=self.separator,
|
195
|
+
allow_duplicates=self.allow_duplicates,
|
196
|
+
cancel_key=cancel_key,
|
172
197
|
)
|
173
198
|
|
174
|
-
if
|
175
|
-
|
199
|
+
if isinstance(keys, str):
|
200
|
+
if keys == cancel_key:
|
201
|
+
raise CancelSignal("User canceled the selection.")
|
202
|
+
result = options[keys].value
|
203
|
+
elif isinstance(keys, list):
|
204
|
+
result = [options[key].value for key in keys]
|
176
205
|
|
177
|
-
result = options[key].value
|
178
206
|
context.result = result
|
179
207
|
await self.hooks.trigger(HookType.ON_SUCCESS, context)
|
180
208
|
return result
|
falyx/action/selection_action.py
CHANGED
@@ -48,6 +48,9 @@ class SelectionAction(BaseAction):
|
|
48
48
|
columns: int = 5,
|
49
49
|
prompt_message: str = "Select > ",
|
50
50
|
default_selection: str = "",
|
51
|
+
number_selections: int | str = 1,
|
52
|
+
separator: str = ",",
|
53
|
+
allow_duplicates: bool = False,
|
51
54
|
inject_last_result: bool = False,
|
52
55
|
inject_into: str = "last_result",
|
53
56
|
return_type: SelectionReturnType | str = "value",
|
@@ -73,9 +76,26 @@ class SelectionAction(BaseAction):
|
|
73
76
|
raise ValueError("`console` must be an instance of `rich.console.Console`")
|
74
77
|
self.prompt_session = prompt_session or PromptSession()
|
75
78
|
self.default_selection = default_selection
|
79
|
+
self.number_selections = number_selections
|
80
|
+
self.separator = separator
|
81
|
+
self.allow_duplicates = allow_duplicates
|
76
82
|
self.prompt_message = prompt_message
|
77
83
|
self.show_table = show_table
|
78
|
-
|
84
|
+
|
85
|
+
@property
|
86
|
+
def number_selections(self) -> int | str:
|
87
|
+
return self._number_selections
|
88
|
+
|
89
|
+
@number_selections.setter
|
90
|
+
def number_selections(self, value: int | str):
|
91
|
+
if isinstance(value, int) and value > 0:
|
92
|
+
self._number_selections: int | str = value
|
93
|
+
elif isinstance(value, str):
|
94
|
+
if value not in ("*"):
|
95
|
+
raise ValueError("number_selections string must be '*'")
|
96
|
+
self._number_selections = value
|
97
|
+
else:
|
98
|
+
raise ValueError("number_selections must be a positive integer or '*'")
|
79
99
|
|
80
100
|
def _coerce_return_type(
|
81
101
|
self, return_type: SelectionReturnType | str
|
@@ -156,6 +176,38 @@ class SelectionAction(BaseAction):
|
|
156
176
|
def get_infer_target(self) -> tuple[None, None]:
|
157
177
|
return None, None
|
158
178
|
|
179
|
+
def _get_result_from_keys(self, keys: str | list[str]) -> Any:
|
180
|
+
if not isinstance(self.selections, dict):
|
181
|
+
raise TypeError("Selections must be a dictionary to get result by keys.")
|
182
|
+
if self.return_type == SelectionReturnType.KEY:
|
183
|
+
result: Any = keys
|
184
|
+
elif self.return_type == SelectionReturnType.VALUE:
|
185
|
+
if isinstance(keys, list):
|
186
|
+
result = [self.selections[key].value for key in keys]
|
187
|
+
elif isinstance(keys, str):
|
188
|
+
result = self.selections[keys].value
|
189
|
+
elif self.return_type == SelectionReturnType.ITEMS:
|
190
|
+
if isinstance(keys, list):
|
191
|
+
result = {key: self.selections[key] for key in keys}
|
192
|
+
elif isinstance(keys, str):
|
193
|
+
result = {keys: self.selections[keys]}
|
194
|
+
elif self.return_type == SelectionReturnType.DESCRIPTION:
|
195
|
+
if isinstance(keys, list):
|
196
|
+
result = [self.selections[key].description for key in keys]
|
197
|
+
elif isinstance(keys, str):
|
198
|
+
result = self.selections[keys].description
|
199
|
+
elif self.return_type == SelectionReturnType.DESCRIPTION_VALUE:
|
200
|
+
if isinstance(keys, list):
|
201
|
+
result = {
|
202
|
+
self.selections[key].description: self.selections[key].value
|
203
|
+
for key in keys
|
204
|
+
}
|
205
|
+
elif isinstance(keys, str):
|
206
|
+
result = {self.selections[keys].description: self.selections[keys].value}
|
207
|
+
else:
|
208
|
+
raise ValueError(f"Unsupported return type: {self.return_type}")
|
209
|
+
return result
|
210
|
+
|
159
211
|
async def _run(self, *args, **kwargs) -> Any:
|
160
212
|
kwargs = self._maybe_inject_last_result(kwargs)
|
161
213
|
context = ExecutionContext(
|
@@ -191,7 +243,7 @@ class SelectionAction(BaseAction):
|
|
191
243
|
if self.never_prompt and not effective_default:
|
192
244
|
raise ValueError(
|
193
245
|
f"[{self.name}] 'never_prompt' is True but no valid default_selection "
|
194
|
-
"was
|
246
|
+
"or usable last_result was available."
|
195
247
|
)
|
196
248
|
|
197
249
|
context.start_timer()
|
@@ -206,7 +258,7 @@ class SelectionAction(BaseAction):
|
|
206
258
|
formatter=self.cancel_formatter,
|
207
259
|
)
|
208
260
|
if not self.never_prompt:
|
209
|
-
|
261
|
+
indices: int | list[int] = await prompt_for_index(
|
210
262
|
len(self.selections),
|
211
263
|
table,
|
212
264
|
default_selection=effective_default,
|
@@ -214,12 +266,30 @@ class SelectionAction(BaseAction):
|
|
214
266
|
prompt_session=self.prompt_session,
|
215
267
|
prompt_message=self.prompt_message,
|
216
268
|
show_table=self.show_table,
|
269
|
+
number_selections=self.number_selections,
|
270
|
+
separator=self.separator,
|
271
|
+
allow_duplicates=self.allow_duplicates,
|
272
|
+
cancel_key=self.cancel_key,
|
217
273
|
)
|
218
274
|
else:
|
219
|
-
|
220
|
-
|
275
|
+
if effective_default:
|
276
|
+
indices = int(effective_default)
|
277
|
+
else:
|
278
|
+
raise ValueError(
|
279
|
+
f"[{self.name}] 'never_prompt' is True but no valid "
|
280
|
+
"default_selection was provided."
|
281
|
+
)
|
282
|
+
|
283
|
+
if indices == int(self.cancel_key):
|
221
284
|
raise CancelSignal("User cancelled the selection.")
|
222
|
-
|
285
|
+
if isinstance(indices, list):
|
286
|
+
result: str | list[str] = [
|
287
|
+
self.selections[index] for index in indices
|
288
|
+
]
|
289
|
+
elif isinstance(indices, int):
|
290
|
+
result = self.selections[indices]
|
291
|
+
else:
|
292
|
+
assert False, "unreachable"
|
223
293
|
elif isinstance(self.selections, dict):
|
224
294
|
cancel_option = {
|
225
295
|
self.cancel_key: SelectionOption(
|
@@ -232,7 +302,7 @@ class SelectionAction(BaseAction):
|
|
232
302
|
columns=self.columns,
|
233
303
|
)
|
234
304
|
if not self.never_prompt:
|
235
|
-
|
305
|
+
keys = await prompt_for_selection(
|
236
306
|
(self.selections | cancel_option).keys(),
|
237
307
|
table,
|
238
308
|
default_selection=effective_default,
|
@@ -240,25 +310,17 @@ class SelectionAction(BaseAction):
|
|
240
310
|
prompt_session=self.prompt_session,
|
241
311
|
prompt_message=self.prompt_message,
|
242
312
|
show_table=self.show_table,
|
313
|
+
number_selections=self.number_selections,
|
314
|
+
separator=self.separator,
|
315
|
+
allow_duplicates=self.allow_duplicates,
|
316
|
+
cancel_key=self.cancel_key,
|
243
317
|
)
|
244
318
|
else:
|
245
|
-
|
246
|
-
if
|
319
|
+
keys = effective_default
|
320
|
+
if keys == self.cancel_key:
|
247
321
|
raise CancelSignal("User cancelled the selection.")
|
248
|
-
|
249
|
-
|
250
|
-
elif self.return_type == SelectionReturnType.VALUE:
|
251
|
-
result = self.selections[key].value
|
252
|
-
elif self.return_type == SelectionReturnType.ITEMS:
|
253
|
-
result = {key: self.selections[key]}
|
254
|
-
elif self.return_type == SelectionReturnType.DESCRIPTION:
|
255
|
-
result = self.selections[key].description
|
256
|
-
elif self.return_type == SelectionReturnType.DESCRIPTION_VALUE:
|
257
|
-
result = {
|
258
|
-
self.selections[key].description: self.selections[key].value
|
259
|
-
}
|
260
|
-
else:
|
261
|
-
raise ValueError(f"Unsupported return type: {self.return_type}")
|
322
|
+
|
323
|
+
result = self._get_result_from_keys(keys)
|
262
324
|
else:
|
263
325
|
raise TypeError(
|
264
326
|
"'selections' must be a list[str] or dict[str, Any], "
|
@@ -29,6 +29,7 @@ class UserInputAction(BaseAction):
|
|
29
29
|
name: str,
|
30
30
|
*,
|
31
31
|
prompt_text: str = "Input > ",
|
32
|
+
default_text: str = "",
|
32
33
|
validator: Validator | None = None,
|
33
34
|
console: Console | None = None,
|
34
35
|
prompt_session: PromptSession | None = None,
|
@@ -45,6 +46,7 @@ class UserInputAction(BaseAction):
|
|
45
46
|
elif console:
|
46
47
|
raise ValueError("`console` must be an instance of `rich.console.Console`")
|
47
48
|
self.prompt_session = prompt_session or PromptSession()
|
49
|
+
self.default_text = default_text
|
48
50
|
|
49
51
|
def get_infer_target(self) -> tuple[None, None]:
|
50
52
|
return None, None
|
@@ -67,6 +69,7 @@ class UserInputAction(BaseAction):
|
|
67
69
|
answer = await self.prompt_session.prompt_async(
|
68
70
|
prompt_text,
|
69
71
|
validator=self.validator,
|
72
|
+
default=kwargs.get("default_text", self.default_text),
|
70
73
|
)
|
71
74
|
context.result = answer
|
72
75
|
await self.hooks.trigger(HookType.ON_SUCCESS, context)
|
falyx/command.py
CHANGED
@@ -34,8 +34,8 @@ from falyx.execution_registry import ExecutionRegistry as er
|
|
34
34
|
from falyx.hook_manager import HookManager, HookType
|
35
35
|
from falyx.logger import logger
|
36
36
|
from falyx.options_manager import OptionsManager
|
37
|
-
from falyx.
|
38
|
-
from falyx.
|
37
|
+
from falyx.parser.argparse import CommandArgumentParser
|
38
|
+
from falyx.parser.signature import infer_args_from_func
|
39
39
|
from falyx.prompt_utils import confirm_async, should_prompt_user
|
40
40
|
from falyx.protocols import ArgParserProtocol
|
41
41
|
from falyx.retry import RetryPolicy
|
falyx/context.py
CHANGED
@@ -70,7 +70,7 @@ class ExecutionContext(BaseModel):
|
|
70
70
|
|
71
71
|
name: str
|
72
72
|
args: tuple = ()
|
73
|
-
kwargs: dict =
|
73
|
+
kwargs: dict = Field(default_factory=dict)
|
74
74
|
action: Any
|
75
75
|
result: Any | None = None
|
76
76
|
exception: Exception | None = None
|
@@ -120,6 +120,17 @@ class ExecutionContext(BaseModel):
|
|
120
120
|
def status(self) -> str:
|
121
121
|
return "OK" if self.success else "ERROR"
|
122
122
|
|
123
|
+
@property
|
124
|
+
def signature(self) -> str:
|
125
|
+
"""
|
126
|
+
Returns a string representation of the action signature, including
|
127
|
+
its name and arguments.
|
128
|
+
"""
|
129
|
+
args = ", ".join(map(repr, self.args))
|
130
|
+
kwargs = ", ".join(f"{key}={value!r}" for key, value in self.kwargs.items())
|
131
|
+
signature = ", ".join(filter(None, [args, kwargs]))
|
132
|
+
return f"{self.action} ({signature})"
|
133
|
+
|
123
134
|
def as_dict(self) -> dict:
|
124
135
|
return {
|
125
136
|
"name": self.name,
|
falyx/debug.py
CHANGED
@@ -8,7 +8,7 @@ from falyx.logger import logger
|
|
8
8
|
def log_before(context: ExecutionContext):
|
9
9
|
"""Log the start of an action."""
|
10
10
|
args = ", ".join(map(repr, context.args))
|
11
|
-
kwargs = ", ".join(f"{
|
11
|
+
kwargs = ", ".join(f"{key}={value!r}" for key, value in context.kwargs.items())
|
12
12
|
signature = ", ".join(filter(None, [args, kwargs]))
|
13
13
|
logger.info("[%s] Starting -> %s(%s)", context.name, context.action, signature)
|
14
14
|
|
falyx/execution_registry.py
CHANGED
@@ -30,7 +30,7 @@ from __future__ import annotations
|
|
30
30
|
from collections import defaultdict
|
31
31
|
from datetime import datetime
|
32
32
|
from threading import Lock
|
33
|
-
from typing import
|
33
|
+
from typing import Literal
|
34
34
|
|
35
35
|
from rich import box
|
36
36
|
from rich.console import Console
|
@@ -111,8 +111,8 @@ class ExecutionRegistry:
|
|
111
111
|
def summary(
|
112
112
|
cls,
|
113
113
|
name: str = "",
|
114
|
-
index: int =
|
115
|
-
|
114
|
+
index: int | None = None,
|
115
|
+
result_index: int | None = None,
|
116
116
|
clear: bool = False,
|
117
117
|
last_result: bool = False,
|
118
118
|
status: Literal["all", "success", "error"] = "all",
|
@@ -138,15 +138,19 @@ class ExecutionRegistry:
|
|
138
138
|
)
|
139
139
|
return
|
140
140
|
|
141
|
-
if
|
141
|
+
if result_index is not None and result_index >= 0:
|
142
142
|
try:
|
143
|
-
result_context = cls._store_by_index[
|
143
|
+
result_context = cls._store_by_index[result_index]
|
144
144
|
except KeyError:
|
145
145
|
cls._console.print(
|
146
|
-
f"[{OneColors.DARK_RED}]❌ No execution found for index {
|
146
|
+
f"[{OneColors.DARK_RED}]❌ No execution found for index {result_index}."
|
147
147
|
)
|
148
148
|
return
|
149
|
-
cls._console.print(result_context.
|
149
|
+
cls._console.print(f"{result_context.signature}:")
|
150
|
+
if result_context.exception:
|
151
|
+
cls._console.print(result_context.exception)
|
152
|
+
else:
|
153
|
+
cls._console.print(result_context.result)
|
150
154
|
return
|
151
155
|
|
152
156
|
if name:
|
@@ -157,9 +161,10 @@ class ExecutionRegistry:
|
|
157
161
|
)
|
158
162
|
return
|
159
163
|
title = f"📊 Execution History for '{contexts[0].name}'"
|
160
|
-
elif index and index >= 0:
|
164
|
+
elif index is not None and index >= 0:
|
161
165
|
try:
|
162
166
|
contexts = [cls._store_by_index[index]]
|
167
|
+
print(contexts)
|
163
168
|
except KeyError:
|
164
169
|
cls._console.print(
|
165
170
|
f"[{OneColors.DARK_RED}]❌ No execution found for index {index}."
|
falyx/falyx.py
CHANGED
@@ -59,7 +59,7 @@ from falyx.execution_registry import ExecutionRegistry as er
|
|
59
59
|
from falyx.hook_manager import Hook, HookManager, HookType
|
60
60
|
from falyx.logger import logger
|
61
61
|
from falyx.options_manager import OptionsManager
|
62
|
-
from falyx.
|
62
|
+
from falyx.parser import CommandArgumentParser, FalyxParsers, get_arg_parsers
|
63
63
|
from falyx.protocols import ArgParserProtocol
|
64
64
|
from falyx.retry import RetryPolicy
|
65
65
|
from falyx.signals import BackSignal, CancelSignal, HelpSignal, QuitSignal
|
@@ -330,7 +330,13 @@ class Falyx:
|
|
330
330
|
action="store_true",
|
331
331
|
help="Clear the Execution History.",
|
332
332
|
)
|
333
|
-
parser.add_argument(
|
333
|
+
parser.add_argument(
|
334
|
+
"-r",
|
335
|
+
"--result",
|
336
|
+
type=int,
|
337
|
+
dest="result_index",
|
338
|
+
help="Get the result by index",
|
339
|
+
)
|
334
340
|
parser.add_argument(
|
335
341
|
"-l", "--last-result", action="store_true", help="Get the last result"
|
336
342
|
)
|
@@ -796,7 +802,12 @@ class Falyx:
|
|
796
802
|
def table(self) -> Table:
|
797
803
|
"""Creates or returns a custom table to display the menu commands."""
|
798
804
|
if callable(self.custom_table):
|
799
|
-
|
805
|
+
custom_table = self.custom_table(self)
|
806
|
+
if not isinstance(custom_table, Table):
|
807
|
+
raise FalyxError(
|
808
|
+
"custom_table must return an instance of rich.table.Table."
|
809
|
+
)
|
810
|
+
return custom_table
|
800
811
|
elif isinstance(self.custom_table, Table):
|
801
812
|
return self.custom_table
|
802
813
|
else:
|
@@ -834,21 +845,31 @@ class Falyx:
|
|
834
845
|
|
835
846
|
choice = choice.upper()
|
836
847
|
name_map = self._name_map
|
848
|
+
run_command = None
|
837
849
|
if name_map.get(choice):
|
850
|
+
run_command = name_map[choice]
|
851
|
+
else:
|
852
|
+
prefix_matches = [
|
853
|
+
cmd for key, cmd in name_map.items() if key.startswith(choice)
|
854
|
+
]
|
855
|
+
if len(prefix_matches) == 1:
|
856
|
+
run_command = prefix_matches[0]
|
857
|
+
|
858
|
+
if run_command:
|
838
859
|
if not from_validate:
|
839
|
-
logger.info("Command '%s' selected.",
|
860
|
+
logger.info("Command '%s' selected.", run_command.key)
|
840
861
|
if is_preview:
|
841
|
-
return True,
|
862
|
+
return True, run_command, args, kwargs
|
842
863
|
elif self.mode in {FalyxMode.RUN, FalyxMode.RUN_ALL, FalyxMode.PREVIEW}:
|
843
|
-
return False,
|
864
|
+
return False, run_command, args, kwargs
|
844
865
|
try:
|
845
|
-
args, kwargs = await
|
846
|
-
input_args, from_validate
|
847
|
-
)
|
866
|
+
args, kwargs = await run_command.parse_args(input_args, from_validate)
|
848
867
|
except (CommandArgumentError, Exception) as error:
|
849
868
|
if not from_validate:
|
850
|
-
|
851
|
-
self.console.print(
|
869
|
+
run_command.show_help()
|
870
|
+
self.console.print(
|
871
|
+
f"[{OneColors.DARK_RED}]❌ [{run_command.key}]: {error}"
|
872
|
+
)
|
852
873
|
else:
|
853
874
|
raise ValidationError(
|
854
875
|
message=str(error), cursor_position=len(raw_choices)
|
@@ -856,11 +877,7 @@ class Falyx:
|
|
856
877
|
return is_preview, None, args, kwargs
|
857
878
|
except HelpSignal:
|
858
879
|
return True, None, args, kwargs
|
859
|
-
return is_preview,
|
860
|
-
|
861
|
-
prefix_matches = [cmd for key, cmd in name_map.items() if key.startswith(choice)]
|
862
|
-
if len(prefix_matches) == 1:
|
863
|
-
return is_preview, prefix_matches[0], args, kwargs
|
880
|
+
return is_preview, run_command, args, kwargs
|
864
881
|
|
865
882
|
fuzzy_matches = get_close_matches(choice, list(name_map.keys()), n=3, cutoff=0.7)
|
866
883
|
if fuzzy_matches:
|
@@ -890,12 +907,14 @@ class Falyx:
|
|
890
907
|
)
|
891
908
|
return is_preview, None, args, kwargs
|
892
909
|
|
893
|
-
def _create_context(
|
894
|
-
|
910
|
+
def _create_context(
|
911
|
+
self, selected_command: Command, args: tuple, kwargs: dict[str, Any]
|
912
|
+
) -> ExecutionContext:
|
913
|
+
"""Creates an ExecutionContext object for the selected command."""
|
895
914
|
return ExecutionContext(
|
896
915
|
name=selected_command.description,
|
897
|
-
args=
|
898
|
-
kwargs=
|
916
|
+
args=args,
|
917
|
+
kwargs=kwargs,
|
899
918
|
action=selected_command,
|
900
919
|
)
|
901
920
|
|
@@ -929,7 +948,7 @@ class Falyx:
|
|
929
948
|
logger.info("Back selected: exiting %s", self.get_title())
|
930
949
|
return False
|
931
950
|
|
932
|
-
context = self._create_context(selected_command)
|
951
|
+
context = self._create_context(selected_command, args, kwargs)
|
933
952
|
context.start_timer()
|
934
953
|
try:
|
935
954
|
await self.hooks.trigger(HookType.BEFORE, context)
|
@@ -974,7 +993,7 @@ class Falyx:
|
|
974
993
|
selected_command.description,
|
975
994
|
)
|
976
995
|
|
977
|
-
context = self._create_context(selected_command)
|
996
|
+
context = self._create_context(selected_command, args, kwargs)
|
978
997
|
context.start_timer()
|
979
998
|
try:
|
980
999
|
await self.hooks.trigger(HookType.BEFORE, context)
|
@@ -12,7 +12,7 @@ from rich.text import Text
|
|
12
12
|
|
13
13
|
from falyx.action.base import BaseAction
|
14
14
|
from falyx.exceptions import CommandArgumentError
|
15
|
-
from falyx.
|
15
|
+
from falyx.parser.utils import coerce_value
|
16
16
|
from falyx.signals import HelpSignal
|
17
17
|
|
18
18
|
|
@@ -629,7 +629,10 @@ class CommandArgumentParser:
|
|
629
629
|
consumed_positional_indicies.add(j)
|
630
630
|
|
631
631
|
if i < len(args):
|
632
|
-
|
632
|
+
plural = "s" if len(args[i:]) > 1 else ""
|
633
|
+
raise CommandArgumentError(
|
634
|
+
f"Unexpected positional argument{plural}: {', '.join(args[i:])}"
|
635
|
+
)
|
633
636
|
|
634
637
|
return i
|
635
638
|
|
@@ -7,7 +7,7 @@ from dateutil import parser as date_parser
|
|
7
7
|
|
8
8
|
from falyx.action.base import BaseAction
|
9
9
|
from falyx.logger import logger
|
10
|
-
from falyx.
|
10
|
+
from falyx.parser.signature import infer_args_from_func
|
11
11
|
|
12
12
|
|
13
13
|
def coerce_bool(value: str) -> bool:
|
falyx/selection.py
CHANGED
@@ -11,7 +11,7 @@ from rich.table import Table
|
|
11
11
|
|
12
12
|
from falyx.themes import OneColors
|
13
13
|
from falyx.utils import CaseInsensitiveDict, chunks
|
14
|
-
from falyx.validators import
|
14
|
+
from falyx.validators import MultiIndexValidator, MultiKeyValidator
|
15
15
|
|
16
16
|
|
17
17
|
@dataclass
|
@@ -271,7 +271,11 @@ async def prompt_for_index(
|
|
271
271
|
prompt_session: PromptSession | None = None,
|
272
272
|
prompt_message: str = "Select an option > ",
|
273
273
|
show_table: bool = True,
|
274
|
-
|
274
|
+
number_selections: int | str = 1,
|
275
|
+
separator: str = ",",
|
276
|
+
allow_duplicates: bool = False,
|
277
|
+
cancel_key: str = "",
|
278
|
+
) -> int | list[int]:
|
275
279
|
prompt_session = prompt_session or PromptSession()
|
276
280
|
console = console or Console(color_system="truecolor")
|
277
281
|
|
@@ -280,10 +284,22 @@ async def prompt_for_index(
|
|
280
284
|
|
281
285
|
selection = await prompt_session.prompt_async(
|
282
286
|
message=prompt_message,
|
283
|
-
validator=
|
287
|
+
validator=MultiIndexValidator(
|
288
|
+
min_index,
|
289
|
+
max_index,
|
290
|
+
number_selections,
|
291
|
+
separator,
|
292
|
+
allow_duplicates,
|
293
|
+
cancel_key,
|
294
|
+
),
|
284
295
|
default=default_selection,
|
285
296
|
)
|
286
|
-
|
297
|
+
|
298
|
+
if selection.strip() == cancel_key:
|
299
|
+
return int(cancel_key)
|
300
|
+
if isinstance(number_selections, int) and number_selections == 1:
|
301
|
+
return int(selection.strip())
|
302
|
+
return [int(index.strip()) for index in selection.strip().split(separator)]
|
287
303
|
|
288
304
|
|
289
305
|
async def prompt_for_selection(
|
@@ -295,7 +311,11 @@ async def prompt_for_selection(
|
|
295
311
|
prompt_session: PromptSession | None = None,
|
296
312
|
prompt_message: str = "Select an option > ",
|
297
313
|
show_table: bool = True,
|
298
|
-
|
314
|
+
number_selections: int | str = 1,
|
315
|
+
separator: str = ",",
|
316
|
+
allow_duplicates: bool = False,
|
317
|
+
cancel_key: str = "",
|
318
|
+
) -> str | list[str]:
|
299
319
|
"""Prompt the user to select a key from a set of options. Return the selected key."""
|
300
320
|
prompt_session = prompt_session or PromptSession()
|
301
321
|
console = console or Console(color_system="truecolor")
|
@@ -305,11 +325,17 @@ async def prompt_for_selection(
|
|
305
325
|
|
306
326
|
selected = await prompt_session.prompt_async(
|
307
327
|
message=prompt_message,
|
308
|
-
validator=
|
328
|
+
validator=MultiKeyValidator(
|
329
|
+
keys, number_selections, separator, allow_duplicates, cancel_key
|
330
|
+
),
|
309
331
|
default=default_selection,
|
310
332
|
)
|
311
333
|
|
312
|
-
|
334
|
+
if selected.strip() == cancel_key:
|
335
|
+
return cancel_key
|
336
|
+
if isinstance(number_selections, int) and number_selections == 1:
|
337
|
+
return selected.strip()
|
338
|
+
return [key.strip() for key in selected.strip().split(separator)]
|
313
339
|
|
314
340
|
|
315
341
|
async def select_value_from_list(
|
@@ -320,6 +346,10 @@ async def select_value_from_list(
|
|
320
346
|
prompt_session: PromptSession | None = None,
|
321
347
|
prompt_message: str = "Select an option > ",
|
322
348
|
default_selection: str = "",
|
349
|
+
number_selections: int | str = 1,
|
350
|
+
separator: str = ",",
|
351
|
+
allow_duplicates: bool = False,
|
352
|
+
cancel_key: str = "",
|
323
353
|
columns: int = 4,
|
324
354
|
caption: str = "",
|
325
355
|
box_style: box.Box = box.SIMPLE,
|
@@ -332,7 +362,7 @@ async def select_value_from_list(
|
|
332
362
|
title_style: str = "",
|
333
363
|
caption_style: str = "",
|
334
364
|
highlight: bool = False,
|
335
|
-
):
|
365
|
+
) -> str | list[str]:
|
336
366
|
"""Prompt for a selection. Return the selected item."""
|
337
367
|
table = render_selection_indexed_table(
|
338
368
|
title=title,
|
@@ -360,8 +390,14 @@ async def select_value_from_list(
|
|
360
390
|
console=console,
|
361
391
|
prompt_session=prompt_session,
|
362
392
|
prompt_message=prompt_message,
|
393
|
+
number_selections=number_selections,
|
394
|
+
separator=separator,
|
395
|
+
allow_duplicates=allow_duplicates,
|
396
|
+
cancel_key=cancel_key,
|
363
397
|
)
|
364
398
|
|
399
|
+
if isinstance(selection_index, list):
|
400
|
+
return [selections[i] for i in selection_index]
|
365
401
|
return selections[selection_index]
|
366
402
|
|
367
403
|
|
@@ -373,7 +409,11 @@ async def select_key_from_dict(
|
|
373
409
|
prompt_session: PromptSession | None = None,
|
374
410
|
prompt_message: str = "Select an option > ",
|
375
411
|
default_selection: str = "",
|
376
|
-
|
412
|
+
number_selections: int | str = 1,
|
413
|
+
separator: str = ",",
|
414
|
+
allow_duplicates: bool = False,
|
415
|
+
cancel_key: str = "",
|
416
|
+
) -> str | list[str]:
|
377
417
|
"""Prompt for a key from a dict, returns the key."""
|
378
418
|
prompt_session = prompt_session or PromptSession()
|
379
419
|
console = console or Console(color_system="truecolor")
|
@@ -387,6 +427,10 @@ async def select_key_from_dict(
|
|
387
427
|
console=console,
|
388
428
|
prompt_session=prompt_session,
|
389
429
|
prompt_message=prompt_message,
|
430
|
+
number_selections=number_selections,
|
431
|
+
separator=separator,
|
432
|
+
allow_duplicates=allow_duplicates,
|
433
|
+
cancel_key=cancel_key,
|
390
434
|
)
|
391
435
|
|
392
436
|
|
@@ -398,7 +442,11 @@ async def select_value_from_dict(
|
|
398
442
|
prompt_session: PromptSession | None = None,
|
399
443
|
prompt_message: str = "Select an option > ",
|
400
444
|
default_selection: str = "",
|
401
|
-
|
445
|
+
number_selections: int | str = 1,
|
446
|
+
separator: str = ",",
|
447
|
+
allow_duplicates: bool = False,
|
448
|
+
cancel_key: str = "",
|
449
|
+
) -> Any | list[Any]:
|
402
450
|
"""Prompt for a key from a dict, but return the value."""
|
403
451
|
prompt_session = prompt_session or PromptSession()
|
404
452
|
console = console or Console(color_system="truecolor")
|
@@ -412,8 +460,14 @@ async def select_value_from_dict(
|
|
412
460
|
console=console,
|
413
461
|
prompt_session=prompt_session,
|
414
462
|
prompt_message=prompt_message,
|
463
|
+
number_selections=number_selections,
|
464
|
+
separator=separator,
|
465
|
+
allow_duplicates=allow_duplicates,
|
466
|
+
cancel_key=cancel_key,
|
415
467
|
)
|
416
468
|
|
469
|
+
if isinstance(selection_key, list):
|
470
|
+
return [selections[key].value for key in selection_key]
|
417
471
|
return selections[selection_key].value
|
418
472
|
|
419
473
|
|
@@ -425,7 +479,11 @@ async def get_selection_from_dict_menu(
|
|
425
479
|
prompt_session: PromptSession | None = None,
|
426
480
|
prompt_message: str = "Select an option > ",
|
427
481
|
default_selection: str = "",
|
428
|
-
|
482
|
+
number_selections: int | str = 1,
|
483
|
+
separator: str = ",",
|
484
|
+
allow_duplicates: bool = False,
|
485
|
+
cancel_key: str = "",
|
486
|
+
) -> Any | list[Any]:
|
429
487
|
"""Prompt for a key from a dict, but return the value."""
|
430
488
|
table = render_selection_dict_table(
|
431
489
|
title,
|
@@ -439,4 +497,8 @@ async def get_selection_from_dict_menu(
|
|
439
497
|
prompt_session=prompt_session,
|
440
498
|
prompt_message=prompt_message,
|
441
499
|
default_selection=default_selection,
|
500
|
+
number_selections=number_selections,
|
501
|
+
separator=separator,
|
502
|
+
allow_duplicates=allow_duplicates,
|
503
|
+
cancel_key=cancel_key,
|
442
504
|
)
|
falyx/validators.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
"""validators.py"""
|
3
3
|
from typing import KeysView, Sequence
|
4
4
|
|
5
|
-
from prompt_toolkit.validation import Validator
|
5
|
+
from prompt_toolkit.validation import ValidationError, Validator
|
6
6
|
|
7
7
|
|
8
8
|
def int_range_validator(minimum: int, maximum: int) -> Validator:
|
@@ -45,3 +45,91 @@ def yes_no_validator() -> Validator:
|
|
45
45
|
return True
|
46
46
|
|
47
47
|
return Validator.from_callable(validate, error_message="Enter 'Y' or 'n'.")
|
48
|
+
|
49
|
+
|
50
|
+
class MultiIndexValidator(Validator):
|
51
|
+
def __init__(
|
52
|
+
self,
|
53
|
+
minimum: int,
|
54
|
+
maximum: int,
|
55
|
+
number_selections: int | str,
|
56
|
+
separator: str,
|
57
|
+
allow_duplicates: bool,
|
58
|
+
cancel_key: str,
|
59
|
+
) -> None:
|
60
|
+
self.minimum = minimum
|
61
|
+
self.maximum = maximum
|
62
|
+
self.number_selections = number_selections
|
63
|
+
self.separator = separator
|
64
|
+
self.allow_duplicates = allow_duplicates
|
65
|
+
self.cancel_key = cancel_key
|
66
|
+
super().__init__()
|
67
|
+
|
68
|
+
def validate(self, document):
|
69
|
+
selections = [
|
70
|
+
index.strip() for index in document.text.strip().split(self.separator)
|
71
|
+
]
|
72
|
+
if not selections or selections == [""]:
|
73
|
+
raise ValidationError(message="Select at least 1 item.")
|
74
|
+
if self.cancel_key in selections and len(selections) == 1:
|
75
|
+
return
|
76
|
+
elif self.cancel_key in selections:
|
77
|
+
raise ValidationError(message="Cancel key must be selected alone.")
|
78
|
+
for selection in selections:
|
79
|
+
try:
|
80
|
+
index = int(selection)
|
81
|
+
if not self.minimum <= index <= self.maximum:
|
82
|
+
raise ValidationError(
|
83
|
+
message=f"Invalid selection: {selection}. Select a number between {self.minimum} and {self.maximum}."
|
84
|
+
)
|
85
|
+
except ValueError:
|
86
|
+
raise ValidationError(
|
87
|
+
message=f"Invalid selection: {selection}. Select a number between {self.minimum} and {self.maximum}."
|
88
|
+
)
|
89
|
+
if not self.allow_duplicates and selections.count(selection) > 1:
|
90
|
+
raise ValidationError(message=f"Duplicate selection: {selection}")
|
91
|
+
if isinstance(self.number_selections, int):
|
92
|
+
if self.number_selections == 1 and len(selections) > 1:
|
93
|
+
raise ValidationError(message="Invalid selection. Select only 1 item.")
|
94
|
+
if len(selections) != self.number_selections:
|
95
|
+
raise ValidationError(
|
96
|
+
message=f"Select exactly {self.number_selections} items separated by '{self.separator}'"
|
97
|
+
)
|
98
|
+
|
99
|
+
|
100
|
+
class MultiKeyValidator(Validator):
|
101
|
+
def __init__(
|
102
|
+
self,
|
103
|
+
keys: Sequence[str] | KeysView[str],
|
104
|
+
number_selections: int | str,
|
105
|
+
separator: str,
|
106
|
+
allow_duplicates: bool,
|
107
|
+
cancel_key: str,
|
108
|
+
) -> None:
|
109
|
+
self.keys = keys
|
110
|
+
self.separator = separator
|
111
|
+
self.number_selections = number_selections
|
112
|
+
self.allow_duplicates = allow_duplicates
|
113
|
+
self.cancel_key = cancel_key
|
114
|
+
super().__init__()
|
115
|
+
|
116
|
+
def validate(self, document):
|
117
|
+
selections = [key.strip() for key in document.text.strip().split(self.separator)]
|
118
|
+
if not selections or selections == [""]:
|
119
|
+
raise ValidationError(message="Select at least 1 item.")
|
120
|
+
if self.cancel_key in selections and len(selections) == 1:
|
121
|
+
return
|
122
|
+
elif self.cancel_key in selections:
|
123
|
+
raise ValidationError(message="Cancel key must be selected alone.")
|
124
|
+
for selection in selections:
|
125
|
+
if selection.upper() not in [key.upper() for key in self.keys]:
|
126
|
+
raise ValidationError(message=f"Invalid selection: {selection}")
|
127
|
+
if not self.allow_duplicates and selections.count(selection) > 1:
|
128
|
+
raise ValidationError(message=f"Duplicate selection: {selection}")
|
129
|
+
if isinstance(self.number_selections, int):
|
130
|
+
if self.number_selections == 1 and len(selections) > 1:
|
131
|
+
raise ValidationError(message="Invalid selection. Select only 1 item.")
|
132
|
+
if len(selections) != self.number_selections:
|
133
|
+
raise ValidationError(
|
134
|
+
message=f"Select exactly {self.number_selections} items separated by '{self.separator}'"
|
135
|
+
)
|
falyx/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.1.
|
1
|
+
__version__ = "0.1.51"
|
@@ -1,61 +1,61 @@
|
|
1
1
|
falyx/.pytyped,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
2
|
falyx/__init__.py,sha256=Gh88lQ5pbD7xbGWrBgslE2kSTZKY9TkvKSa53rZ3l8U,305
|
3
|
-
falyx/__main__.py,sha256=
|
3
|
+
falyx/__main__.py,sha256=xHO4pB45rccixo-ougF84QJeB36ef8mEZXWVK_CJL9M,3420
|
4
4
|
falyx/action/.pytyped,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
falyx/action/__init__.py,sha256=4E3Rb0GgGcmggrPJh0YFiwbVgN_PQjIzL06-Z3qMReo,1247
|
6
|
-
falyx/action/action.py,sha256=
|
6
|
+
falyx/action/action.py,sha256=j9ANO0xRfWoiNK6z-46T04EKAJ7GhjVrzb8_U8afEAA,5753
|
7
7
|
falyx/action/action_factory.py,sha256=br-P7Oip-4tZkO8qVT_ECwLe6idYjJa_GuBi5QR7vS4,4832
|
8
|
-
falyx/action/action_group.py,sha256=
|
8
|
+
falyx/action/action_group.py,sha256=_u64s81kgLAQFlTic3OJJoa8DwYJ2kqBh2U-_mpuPhs,6834
|
9
9
|
falyx/action/base.py,sha256=B7mt66oznmhv2qpSOwOuScgMckVXrxjRMU2buzZkRD8,5866
|
10
10
|
falyx/action/chained_action.py,sha256=aV_plUdDVdc1o-oU57anbWkw33jgRIh4W29QwEA_1Mw,8501
|
11
11
|
falyx/action/fallback_action.py,sha256=0z5l0s_LKnhIwgMdykm8lJqs246DKSpyYs-p7PnsKok,1619
|
12
12
|
falyx/action/http_action.py,sha256=DNeSBWh58UTFGlfFyTk2GnhS54hpLAJLC0QNbq2cYic,5799
|
13
|
-
falyx/action/io_action.py,sha256=
|
13
|
+
falyx/action/io_action.py,sha256=9vx3mX9lBUBmwl1Xtzr5-idEwlMKlK1ZQuZkzTmORJc,9881
|
14
14
|
falyx/action/literal_input_action.py,sha256=7H2VX_L5VaytVdV2uis-VTGi782kQtwKTB8T04c7J1k,1293
|
15
|
-
falyx/action/menu_action.py,sha256=
|
15
|
+
falyx/action/menu_action.py,sha256=SLqwmQ1TOt8kl_cgIWogBYfx8lYPLZa4E-Yy6M2cX_w,6069
|
16
16
|
falyx/action/mixins.py,sha256=eni8_PwzMnuwh0ZqOdzCdAyWlOphoiqL7z27xnFsg5s,1117
|
17
17
|
falyx/action/process_action.py,sha256=HsDqlKy1PkG3HHC6mHa4O6ayY_oKVY2qj5nDRJuSn24,4571
|
18
|
-
falyx/action/process_pool_action.py,sha256=
|
18
|
+
falyx/action/process_pool_action.py,sha256=8RQSjJaU0nGY_ObpcO31uI-HfNY7krqMN2wSzTnJ8jw,5939
|
19
19
|
falyx/action/prompt_menu_action.py,sha256=corzjpPNVMYKncfueeRUWwklnlZHN-Fc61psOzbZELg,5286
|
20
|
-
falyx/action/select_file_action.py,sha256=
|
21
|
-
falyx/action/selection_action.py,sha256=
|
20
|
+
falyx/action/select_file_action.py,sha256=2T4I1CLvHLAAqNUD2rFBIpdi74BP5amU4yTHUOGnd64,9911
|
21
|
+
falyx/action/selection_action.py,sha256=Mav39iTkVIJPDvmDek8R2bSF18f-mII56l5sSzZSPII,15735
|
22
22
|
falyx/action/signal_action.py,sha256=5UMqvzy7fBnLANGwYUWoe1VRhrr7e-yOVeLdOnCBiJo,1350
|
23
23
|
falyx/action/types.py,sha256=NfZz1ufZuvCgp-he2JIItbnjX7LjOUadjtKbjpRlSIY,1399
|
24
|
-
falyx/action/user_input_action.py,sha256=
|
24
|
+
falyx/action/user_input_action.py,sha256=EnwSk-ZW0bqSlEnWpUE9_0jmoFCoTGMQc5PqP49cSyg,3750
|
25
25
|
falyx/bottom_bar.py,sha256=KPACb9VC0I3dv_pYZLqy7e4uA_KT5dSfwnvuknyV0FI,7388
|
26
|
-
falyx/command.py,sha256=
|
26
|
+
falyx/command.py,sha256=7BM4JPK36dsWm2JPmRwarLJiDRk1GgfX5HESbeysuMY,16417
|
27
27
|
falyx/config.py,sha256=Cm1F9SfNSbugPALxaEz7NRqp1wrk-g2jYq35bQzN2uE,9658
|
28
|
-
falyx/context.py,sha256=
|
29
|
-
falyx/debug.py,sha256=
|
28
|
+
falyx/context.py,sha256=b9PGkIfhc1BbFUmaqmr4AojzONfKG1c9WP2uixzCJGQ,10806
|
29
|
+
falyx/debug.py,sha256=pguI0XQcZ-7jte5YUPexAufa1oxxalYO1JgmO6GU3rI,1557
|
30
30
|
falyx/exceptions.py,sha256=kK9k1v7LVNjJSwYztRa9Krhr3ZOI-6Htq2ZjlYICPKg,922
|
31
|
-
falyx/execution_registry.py,sha256=
|
32
|
-
falyx/falyx.py,sha256=
|
31
|
+
falyx/execution_registry.py,sha256=7t_96-Q7R7MAJBvWwAt5IAERp0TjbGZPGeeJ1s24ey8,7628
|
32
|
+
falyx/falyx.py,sha256=zOAah7OYHZgMRI60zwXjNgfeEHs8y87SDs3Meslood0,49805
|
33
33
|
falyx/hook_manager.py,sha256=TFuHQnAncS_rk6vuw-VSx8bnAppLuHfrZCrzLwqcO9o,2979
|
34
34
|
falyx/hooks.py,sha256=xMfQROib0BNsaQF4AXJpmCiGePoE1f1xpcdibgnVZWM,2913
|
35
35
|
falyx/init.py,sha256=F9jg7mLPoBWXdJnc_fyWG7zVQSnrAO8ueDiP8AJxDWE,3331
|
36
36
|
falyx/logger.py,sha256=1Mfb_vJFJ1tQwziuyU2p-cSMi2Js8N2byniFEnI6vOQ,132
|
37
37
|
falyx/menu.py,sha256=E580qZsx08bnWcqRVjJuD2Fy8Zh_1zIexp5f0lC7L2c,3745
|
38
38
|
falyx/options_manager.py,sha256=dFAnQw543tQ6Xupvh1PwBrhiSWlSACHw8K-sHP_lUh4,2842
|
39
|
-
falyx/
|
40
|
-
falyx/
|
41
|
-
falyx/
|
42
|
-
falyx/
|
43
|
-
falyx/
|
44
|
-
falyx/
|
39
|
+
falyx/parser/.pytyped,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
40
|
+
falyx/parser/__init__.py,sha256=ZfPmbtEUechDvgl99-lWhTXmFnXS_FMXJ_xb8KGEJLo,448
|
41
|
+
falyx/parser/argparse.py,sha256=izJDlhHxyP1y0-NjiuLcNpHYMIO6zS4nY_-v-Xg-O94,37503
|
42
|
+
falyx/parser/parsers.py,sha256=MXWC8OQ3apDaeKfY0O4J8NnkxofWVOCRnKatC00lGm0,8796
|
43
|
+
falyx/parser/signature.py,sha256=cCa-yKUcbbET0Ho45oFZWWHFGCX5a_LaAOWRP7b87po,2465
|
44
|
+
falyx/parser/utils.py,sha256=VX4C58pJdHQihkaLIrYmcwqHJrFjbNjb5blEU3IqSAE,2892
|
45
45
|
falyx/prompt_utils.py,sha256=qgk0bXs7mwzflqzWyFhEOTpKQ_ZtMIqGhKeg-ocwNnE,1542
|
46
46
|
falyx/protocols.py,sha256=-9GbCBUzzsEgw2_KOCYqxxzWJuez0eHmwnZp_ShY0jc,493
|
47
47
|
falyx/retry.py,sha256=sGRE9QhdZK98M99G8F15WUsJ_fYLNyLlCgu3UANaSQs,3744
|
48
48
|
falyx/retry_utils.py,sha256=vwoZmFVCGVqZ13BX_xi3qZZVsmSxkp-jfaf6kJtBV9c,723
|
49
|
-
falyx/selection.py,sha256=
|
49
|
+
falyx/selection.py,sha256=TPSM_KKGHedJblWI0AzxTZR2haZjRF3k-gQoQeR3L28,15239
|
50
50
|
falyx/signals.py,sha256=Y_neFXpfHs7qY0syw9XcfR9WeAGRcRw1nG_2L1JJqKE,1083
|
51
51
|
falyx/tagged_table.py,sha256=4SV-SdXFrAhy1JNToeBCvyxT-iWVf6cWY7XETTys4n8,1067
|
52
52
|
falyx/themes/__init__.py,sha256=1CZhEUCin9cUk8IGYBUFkVvdHRNNJBEFXccHwpUKZCA,284
|
53
53
|
falyx/themes/colors.py,sha256=4aaeAHJetmeNInI0Zytg4E3YqKfPFelpf04vtjSvsS8,19776
|
54
54
|
falyx/utils.py,sha256=U45xnZFUdoFC4xiji_9S1jHS5V7MvxSDtufP8EgB0SM,6732
|
55
|
-
falyx/validators.py,sha256=
|
56
|
-
falyx/version.py,sha256=
|
57
|
-
falyx-0.1.
|
58
|
-
falyx-0.1.
|
59
|
-
falyx-0.1.
|
60
|
-
falyx-0.1.
|
61
|
-
falyx-0.1.
|
55
|
+
falyx/validators.py,sha256=Pbdxh5777Y03HxyArAh2ApeVSx23in4w4K38G43Vt98,5197
|
56
|
+
falyx/version.py,sha256=J5DqQAfJrmJsVwP_F1QEYvB7YXVaXR6y-BTblSWQs-k,23
|
57
|
+
falyx-0.1.51.dist-info/LICENSE,sha256=B0yqgaHuSdhN7T3OBmgQSiDTy8HqT5Oe_dLypRe4Ra4,1073
|
58
|
+
falyx-0.1.51.dist-info/METADATA,sha256=1pOPjynlq0g3vfgJaV2VqedsHH4O-3Egjo1WEengCns,5561
|
59
|
+
falyx-0.1.51.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
60
|
+
falyx-0.1.51.dist-info/entry_points.txt,sha256=j8owOSl2j1Ss8DtGMnKfgehKaolqnIPhVFHaUBLUnMs,45
|
61
|
+
falyx-0.1.51.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|