falyx 0.1.25__tar.gz → 0.1.26__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.
- {falyx-0.1.25 → falyx-0.1.26}/PKG-INFO +1 -1
- {falyx-0.1.25 → falyx-0.1.26}/falyx/config.py +73 -11
- {falyx-0.1.25 → falyx-0.1.26}/falyx/falyx.py +5 -2
- falyx-0.1.26/falyx/version.py +1 -0
- {falyx-0.1.25 → falyx-0.1.26}/pyproject.toml +1 -1
- falyx-0.1.25/falyx/version.py +0 -1
- {falyx-0.1.25 → falyx-0.1.26}/LICENSE +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/README.md +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/.pytyped +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/__init__.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/__main__.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/action/__init__.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/action/action.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/action/action_factory.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/action/http_action.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/action/io_action.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/action/menu_action.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/action/select_file_action.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/action/selection_action.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/action/signal_action.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/action/types.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/bottom_bar.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/command.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/config_schema.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/context.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/debug.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/exceptions.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/execution_registry.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/hook_manager.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/hooks.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/init.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/logger.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/menu.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/options_manager.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/parsers.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/prompt_utils.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/protocols.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/retry.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/retry_utils.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/selection.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/signals.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/tagged_table.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/themes/__init__.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/themes/colors.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/utils.py +0 -0
- {falyx-0.1.25 → falyx-0.1.26}/falyx/validators.py +0 -0
@@ -72,10 +72,10 @@ class RawCommand(BaseModel):
|
|
72
72
|
description: str
|
73
73
|
action: str
|
74
74
|
|
75
|
-
args: tuple[Any, ...] = ()
|
76
|
-
kwargs: dict[str, Any] =
|
77
|
-
aliases: list[str] =
|
78
|
-
tags: list[str] =
|
75
|
+
args: tuple[Any, ...] = Field(default_factory=tuple)
|
76
|
+
kwargs: dict[str, Any] = Field(default_factory=dict)
|
77
|
+
aliases: list[str] = Field(default_factory=list)
|
78
|
+
tags: list[str] = Field(default_factory=list)
|
79
79
|
style: str = OneColors.WHITE
|
80
80
|
|
81
81
|
confirm: bool = False
|
@@ -86,13 +86,13 @@ class RawCommand(BaseModel):
|
|
86
86
|
spinner_message: str = "Processing..."
|
87
87
|
spinner_type: str = "dots"
|
88
88
|
spinner_style: str = OneColors.CYAN
|
89
|
-
spinner_kwargs: dict[str, Any] =
|
89
|
+
spinner_kwargs: dict[str, Any] = Field(default_factory=dict)
|
90
90
|
|
91
|
-
before_hooks: list[Callable] =
|
92
|
-
success_hooks: list[Callable] =
|
93
|
-
error_hooks: list[Callable] =
|
94
|
-
after_hooks: list[Callable] =
|
95
|
-
teardown_hooks: list[Callable] =
|
91
|
+
before_hooks: list[Callable] = Field(default_factory=list)
|
92
|
+
success_hooks: list[Callable] = Field(default_factory=list)
|
93
|
+
error_hooks: list[Callable] = Field(default_factory=list)
|
94
|
+
after_hooks: list[Callable] = Field(default_factory=list)
|
95
|
+
teardown_hooks: list[Callable] = Field(default_factory=list)
|
96
96
|
|
97
97
|
logging_hooks: bool = False
|
98
98
|
retry: bool = False
|
@@ -129,6 +129,60 @@ def convert_commands(raw_commands: list[dict[str, Any]]) -> list[Command]:
|
|
129
129
|
return commands
|
130
130
|
|
131
131
|
|
132
|
+
def convert_submenus(
|
133
|
+
raw_submenus: list[dict[str, Any]], *, parent_path: Path | None = None, depth: int = 0
|
134
|
+
) -> list[dict[str, Any]]:
|
135
|
+
submenus: list[dict[str, Any]] = []
|
136
|
+
for raw_submenu in raw_submenus:
|
137
|
+
if raw_submenu.get("config"):
|
138
|
+
config_path = Path(raw_submenu["config"])
|
139
|
+
if parent_path:
|
140
|
+
config_path = (parent_path.parent / config_path).resolve()
|
141
|
+
submenu = loader(config_path, _depth=depth + 1)
|
142
|
+
else:
|
143
|
+
submenu_module_path = raw_submenu.get("submenu")
|
144
|
+
if not isinstance(submenu_module_path, str):
|
145
|
+
console.print(
|
146
|
+
f"[{OneColors.DARK_RED}]❌ Invalid submenu path:[/] {submenu_module_path}"
|
147
|
+
)
|
148
|
+
sys.exit(1)
|
149
|
+
submenu = import_action(submenu_module_path)
|
150
|
+
if not isinstance(submenu, Falyx):
|
151
|
+
console.print(f"[{OneColors.DARK_RED}]❌ Invalid submenu:[/] {submenu}")
|
152
|
+
sys.exit(1)
|
153
|
+
|
154
|
+
key = raw_submenu.get("key")
|
155
|
+
if not isinstance(key, str):
|
156
|
+
console.print(f"[{OneColors.DARK_RED}]❌ Invalid submenu key:[/] {key}")
|
157
|
+
sys.exit(1)
|
158
|
+
|
159
|
+
description = raw_submenu.get("description")
|
160
|
+
if not isinstance(description, str):
|
161
|
+
console.print(
|
162
|
+
f"[{OneColors.DARK_RED}]❌ Invalid submenu description:[/] {description}"
|
163
|
+
)
|
164
|
+
sys.exit(1)
|
165
|
+
|
166
|
+
submenus.append(
|
167
|
+
Submenu(
|
168
|
+
key=key,
|
169
|
+
description=description,
|
170
|
+
submenu=submenu,
|
171
|
+
style=raw_submenu.get("style", OneColors.CYAN),
|
172
|
+
).model_dump()
|
173
|
+
)
|
174
|
+
return submenus
|
175
|
+
|
176
|
+
|
177
|
+
class Submenu(BaseModel):
|
178
|
+
"""Submenu model for Falyx CLI configuration."""
|
179
|
+
|
180
|
+
key: str
|
181
|
+
description: str
|
182
|
+
submenu: Any
|
183
|
+
style: str = OneColors.CYAN
|
184
|
+
|
185
|
+
|
132
186
|
class FalyxConfig(BaseModel):
|
133
187
|
"""Falyx CLI configuration model."""
|
134
188
|
|
@@ -140,6 +194,7 @@ class FalyxConfig(BaseModel):
|
|
140
194
|
welcome_message: str = ""
|
141
195
|
exit_message: str = ""
|
142
196
|
commands: list[Command] | list[dict] = []
|
197
|
+
submenus: list[dict[str, Any]] = []
|
143
198
|
|
144
199
|
@model_validator(mode="after")
|
145
200
|
def validate_prompt_format(self) -> FalyxConfig:
|
@@ -160,10 +215,12 @@ class FalyxConfig(BaseModel):
|
|
160
215
|
exit_message=self.exit_message,
|
161
216
|
)
|
162
217
|
flx.add_commands(self.commands)
|
218
|
+
for submenu in self.submenus:
|
219
|
+
flx.add_submenu(**submenu)
|
163
220
|
return flx
|
164
221
|
|
165
222
|
|
166
|
-
def loader(file_path: Path | str) -> Falyx:
|
223
|
+
def loader(file_path: Path | str, _depth: int = 0) -> Falyx:
|
167
224
|
"""
|
168
225
|
Load Falyx CLI configuration from a YAML or TOML file.
|
169
226
|
|
@@ -183,6 +240,9 @@ def loader(file_path: Path | str) -> Falyx:
|
|
183
240
|
Raises:
|
184
241
|
ValueError: If the file format is unsupported or file cannot be parsed.
|
185
242
|
"""
|
243
|
+
if _depth > 5:
|
244
|
+
raise ValueError("Maximum submenu depth exceeded (5 levels deep)")
|
245
|
+
|
186
246
|
if isinstance(file_path, (str, Path)):
|
187
247
|
path = Path(file_path)
|
188
248
|
else:
|
@@ -212,6 +272,7 @@ def loader(file_path: Path | str) -> Falyx:
|
|
212
272
|
)
|
213
273
|
|
214
274
|
commands = convert_commands(raw_config["commands"])
|
275
|
+
submenus = convert_submenus(raw_config.get("submenus", []))
|
215
276
|
return FalyxConfig(
|
216
277
|
title=raw_config.get("title", f"[{OneColors.BLUE_b}]Falyx CLI"),
|
217
278
|
prompt=raw_config.get("prompt", [(OneColors.BLUE_b, "FALYX > ")]),
|
@@ -219,4 +280,5 @@ def loader(file_path: Path | str) -> Falyx:
|
|
219
280
|
welcome_message=raw_config.get("welcome_message", ""),
|
220
281
|
exit_message=raw_config.get("exit_message", ""),
|
221
282
|
commands=commands,
|
283
|
+
submenus=submenus,
|
222
284
|
).to_falyx()
|
@@ -19,6 +19,8 @@ for running commands, actions, and workflows. It supports:
|
|
19
19
|
Falyx enables building flexible, robust, and user-friendly
|
20
20
|
terminal applications with minimal boilerplate.
|
21
21
|
"""
|
22
|
+
from __future__ import annotations
|
23
|
+
|
22
24
|
import asyncio
|
23
25
|
import logging
|
24
26
|
import sys
|
@@ -528,14 +530,15 @@ class Falyx:
|
|
528
530
|
)
|
529
531
|
|
530
532
|
def add_submenu(
|
531
|
-
self, key: str, description: str, submenu:
|
533
|
+
self, key: str, description: str, submenu: Falyx, *, style: str = OneColors.CYAN
|
532
534
|
) -> None:
|
533
535
|
"""Adds a submenu to the menu."""
|
534
536
|
if not isinstance(submenu, Falyx):
|
535
537
|
raise NotAFalyxError("submenu must be an instance of Falyx.")
|
536
538
|
self._validate_command_key(key)
|
537
539
|
self.add_command(key, description, submenu.menu, style=style)
|
538
|
-
submenu.
|
540
|
+
if submenu.exit_command.key == "Q":
|
541
|
+
submenu.update_exit_command(key="B", description="Back", aliases=["BACK"])
|
539
542
|
|
540
543
|
def add_commands(self, commands: list[Command] | list[dict]) -> None:
|
541
544
|
"""Adds a list of Command instances or config dicts."""
|
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "0.1.26"
|
falyx-0.1.25/falyx/version.py
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
__version__ = "0.1.25"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|