falyx 0.1.24__py3-none-any.whl → 0.1.25__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 +1 -1
- falyx/action/__init__.py +41 -0
- falyx/{action.py → action/action.py} +1 -1
- falyx/{action_factory.py → action/action_factory.py} +2 -2
- falyx/{http_action.py → action/http_action.py} +2 -2
- falyx/{io_action.py → action/io_action.py} +2 -2
- falyx/{menu_action.py → action/menu_action.py} +4 -80
- falyx/{select_file_action.py → action/select_file_action.py} +3 -37
- falyx/{selection_action.py → action/selection_action.py} +2 -2
- falyx/action/signal_action.py +43 -0
- falyx/action/types.py +37 -0
- falyx/bottom_bar.py +1 -1
- falyx/command.py +3 -3
- falyx/config.py +2 -2
- falyx/execution_registry.py +1 -1
- falyx/falyx.py +2 -2
- falyx/hooks.py +1 -1
- falyx/menu.py +85 -0
- falyx/prompt_utils.py +1 -1
- falyx/protocols.py +1 -1
- falyx/retry_utils.py +1 -1
- falyx/selection.py +1 -1
- falyx/themes/__init__.py +15 -0
- falyx/version.py +1 -1
- {falyx-0.1.24.dist-info → falyx-0.1.25.dist-info}/METADATA +2 -1
- falyx-0.1.25.dist-info/RECORD +46 -0
- falyx/signal_action.py +0 -31
- falyx-0.1.24.dist-info/RECORD +0 -42
- {falyx-0.1.24.dist-info → falyx-0.1.25.dist-info}/LICENSE +0 -0
- {falyx-0.1.24.dist-info → falyx-0.1.25.dist-info}/WHEEL +0 -0
- {falyx-0.1.24.dist-info → falyx-0.1.25.dist-info}/entry_points.txt +0 -0
falyx/__init__.py
CHANGED
@@ -7,7 +7,7 @@ Licensed under the MIT License. See LICENSE file for details.
|
|
7
7
|
|
8
8
|
import logging
|
9
9
|
|
10
|
-
from .action import Action, ActionGroup, ChainedAction, ProcessAction
|
10
|
+
from .action.action import Action, ActionGroup, ChainedAction, ProcessAction
|
11
11
|
from .command import Command
|
12
12
|
from .context import ExecutionContext, SharedContext
|
13
13
|
from .execution_registry import ExecutionRegistry
|
falyx/action/__init__.py
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
"""
|
2
|
+
Falyx CLI Framework
|
3
|
+
|
4
|
+
Copyright (c) 2025 rtj.dev LLC.
|
5
|
+
Licensed under the MIT License. See LICENSE file for details.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from .action import (
|
9
|
+
Action,
|
10
|
+
ActionGroup,
|
11
|
+
BaseAction,
|
12
|
+
ChainedAction,
|
13
|
+
FallbackAction,
|
14
|
+
LiteralInputAction,
|
15
|
+
ProcessAction,
|
16
|
+
)
|
17
|
+
from .action_factory import ActionFactoryAction
|
18
|
+
from .http_action import HTTPAction
|
19
|
+
from .io_action import BaseIOAction, ShellAction
|
20
|
+
from .menu_action import MenuAction
|
21
|
+
from .select_file_action import SelectFileAction
|
22
|
+
from .selection_action import SelectionAction
|
23
|
+
from .signal_action import SignalAction
|
24
|
+
|
25
|
+
__all__ = [
|
26
|
+
"Action",
|
27
|
+
"ActionGroup",
|
28
|
+
"BaseAction",
|
29
|
+
"ChainedAction",
|
30
|
+
"ProcessAction",
|
31
|
+
"ActionFactoryAction",
|
32
|
+
"HTTPAction",
|
33
|
+
"BaseIOAction",
|
34
|
+
"ShellAction",
|
35
|
+
"SelectionAction",
|
36
|
+
"SelectFileAction",
|
37
|
+
"MenuAction",
|
38
|
+
"SignalAction",
|
39
|
+
"FallbackAction",
|
40
|
+
"LiteralInputAction",
|
41
|
+
]
|
@@ -48,7 +48,7 @@ from falyx.hook_manager import Hook, HookManager, HookType
|
|
48
48
|
from falyx.logger import logger
|
49
49
|
from falyx.options_manager import OptionsManager
|
50
50
|
from falyx.retry import RetryHandler, RetryPolicy
|
51
|
-
from falyx.themes
|
51
|
+
from falyx.themes import OneColors
|
52
52
|
from falyx.utils import ensure_async
|
53
53
|
|
54
54
|
|
@@ -4,13 +4,13 @@ from typing import Any
|
|
4
4
|
|
5
5
|
from rich.tree import Tree
|
6
6
|
|
7
|
-
from falyx.action import BaseAction
|
7
|
+
from falyx.action.action import BaseAction
|
8
8
|
from falyx.context import ExecutionContext
|
9
9
|
from falyx.execution_registry import ExecutionRegistry as er
|
10
10
|
from falyx.hook_manager import HookType
|
11
11
|
from falyx.logger import logger
|
12
12
|
from falyx.protocols import ActionFactoryProtocol
|
13
|
-
from falyx.themes
|
13
|
+
from falyx.themes import OneColors
|
14
14
|
|
15
15
|
|
16
16
|
class ActionFactoryAction(BaseAction):
|
@@ -13,11 +13,11 @@ from typing import Any
|
|
13
13
|
import aiohttp
|
14
14
|
from rich.tree import Tree
|
15
15
|
|
16
|
-
from falyx.action import Action
|
16
|
+
from falyx.action.action import Action
|
17
17
|
from falyx.context import ExecutionContext, SharedContext
|
18
18
|
from falyx.hook_manager import HookManager, HookType
|
19
19
|
from falyx.logger import logger
|
20
|
-
from falyx.themes
|
20
|
+
from falyx.themes import OneColors
|
21
21
|
|
22
22
|
|
23
23
|
async def close_shared_http_session(context: ExecutionContext) -> None:
|
@@ -23,13 +23,13 @@ from typing import Any
|
|
23
23
|
|
24
24
|
from rich.tree import Tree
|
25
25
|
|
26
|
-
from falyx.action import BaseAction
|
26
|
+
from falyx.action.action import BaseAction
|
27
27
|
from falyx.context import ExecutionContext
|
28
28
|
from falyx.exceptions import FalyxError
|
29
29
|
from falyx.execution_registry import ExecutionRegistry as er
|
30
30
|
from falyx.hook_manager import HookManager, HookType
|
31
31
|
from falyx.logger import logger
|
32
|
-
from falyx.themes
|
32
|
+
from falyx.themes import OneColors
|
33
33
|
|
34
34
|
|
35
35
|
class BaseIOAction(BaseAction):
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed
|
2
2
|
"""menu_action.py"""
|
3
|
-
from dataclasses import dataclass
|
4
3
|
from typing import Any
|
5
4
|
|
6
5
|
from prompt_toolkit import PromptSession
|
@@ -8,91 +7,16 @@ from rich.console import Console
|
|
8
7
|
from rich.table import Table
|
9
8
|
from rich.tree import Tree
|
10
9
|
|
11
|
-
from falyx.action import BaseAction
|
10
|
+
from falyx.action.action import BaseAction
|
12
11
|
from falyx.context import ExecutionContext
|
13
12
|
from falyx.execution_registry import ExecutionRegistry as er
|
14
13
|
from falyx.hook_manager import HookType
|
15
14
|
from falyx.logger import logger
|
15
|
+
from falyx.menu import MenuOptionMap
|
16
16
|
from falyx.selection import prompt_for_selection, render_table_base
|
17
|
-
from falyx.signal_action import SignalAction
|
18
17
|
from falyx.signals import BackSignal, QuitSignal
|
19
|
-
from falyx.themes
|
20
|
-
from falyx.utils import
|
21
|
-
|
22
|
-
|
23
|
-
@dataclass
|
24
|
-
class MenuOption:
|
25
|
-
"""Represents a single menu option with a description and an action to execute."""
|
26
|
-
|
27
|
-
description: str
|
28
|
-
action: BaseAction
|
29
|
-
style: str = OneColors.WHITE
|
30
|
-
|
31
|
-
def __post_init__(self):
|
32
|
-
if not isinstance(self.description, str):
|
33
|
-
raise TypeError("MenuOption description must be a string.")
|
34
|
-
if not isinstance(self.action, BaseAction):
|
35
|
-
raise TypeError("MenuOption action must be a BaseAction instance.")
|
36
|
-
|
37
|
-
def render(self, key: str) -> str:
|
38
|
-
"""Render the menu option for display."""
|
39
|
-
return f"[{OneColors.WHITE}][{key}][/] [{self.style}]{self.description}[/]"
|
40
|
-
|
41
|
-
|
42
|
-
class MenuOptionMap(CaseInsensitiveDict):
|
43
|
-
"""
|
44
|
-
Manages menu options including validation, reserved key protection,
|
45
|
-
and special signal entries like Quit and Back.
|
46
|
-
"""
|
47
|
-
|
48
|
-
RESERVED_KEYS = {"Q", "B"}
|
49
|
-
|
50
|
-
def __init__(
|
51
|
-
self,
|
52
|
-
options: dict[str, MenuOption] | None = None,
|
53
|
-
allow_reserved: bool = False,
|
54
|
-
):
|
55
|
-
super().__init__()
|
56
|
-
self.allow_reserved = allow_reserved
|
57
|
-
if options:
|
58
|
-
self.update(options)
|
59
|
-
self._inject_reserved_defaults()
|
60
|
-
|
61
|
-
def _inject_reserved_defaults(self):
|
62
|
-
self._add_reserved(
|
63
|
-
"Q",
|
64
|
-
MenuOption("Exit", SignalAction("Quit", QuitSignal()), OneColors.DARK_RED),
|
65
|
-
)
|
66
|
-
self._add_reserved(
|
67
|
-
"B",
|
68
|
-
MenuOption("Back", SignalAction("Back", BackSignal()), OneColors.DARK_YELLOW),
|
69
|
-
)
|
70
|
-
|
71
|
-
def _add_reserved(self, key: str, option: MenuOption) -> None:
|
72
|
-
"""Add a reserved key, bypassing validation."""
|
73
|
-
norm_key = key.upper()
|
74
|
-
super().__setitem__(norm_key, option)
|
75
|
-
|
76
|
-
def __setitem__(self, key: str, option: MenuOption) -> None:
|
77
|
-
if not isinstance(option, MenuOption):
|
78
|
-
raise TypeError(f"Value for key '{key}' must be a MenuOption.")
|
79
|
-
norm_key = key.upper()
|
80
|
-
if norm_key in self.RESERVED_KEYS and not self.allow_reserved:
|
81
|
-
raise ValueError(
|
82
|
-
f"Key '{key}' is reserved and cannot be used in MenuOptionMap."
|
83
|
-
)
|
84
|
-
super().__setitem__(norm_key, option)
|
85
|
-
|
86
|
-
def __delitem__(self, key: str) -> None:
|
87
|
-
if key.upper() in self.RESERVED_KEYS and not self.allow_reserved:
|
88
|
-
raise ValueError(f"Cannot delete reserved option '{key}'.")
|
89
|
-
super().__delitem__(key)
|
90
|
-
|
91
|
-
def items(self, include_reserved: bool = True):
|
92
|
-
for k, v in super().items():
|
93
|
-
if not include_reserved and k in self.RESERVED_KEYS:
|
94
|
-
continue
|
95
|
-
yield k, v
|
18
|
+
from falyx.themes import OneColors
|
19
|
+
from falyx.utils import chunks
|
96
20
|
|
97
21
|
|
98
22
|
class MenuAction(BaseAction):
|
@@ -5,7 +5,6 @@ from __future__ import annotations
|
|
5
5
|
import csv
|
6
6
|
import json
|
7
7
|
import xml.etree.ElementTree as ET
|
8
|
-
from enum import Enum
|
9
8
|
from pathlib import Path
|
10
9
|
from typing import Any
|
11
10
|
|
@@ -15,7 +14,8 @@ from prompt_toolkit import PromptSession
|
|
15
14
|
from rich.console import Console
|
16
15
|
from rich.tree import Tree
|
17
16
|
|
18
|
-
from falyx.action import BaseAction
|
17
|
+
from falyx.action.action import BaseAction
|
18
|
+
from falyx.action.types import FileReturnType
|
19
19
|
from falyx.context import ExecutionContext
|
20
20
|
from falyx.execution_registry import ExecutionRegistry as er
|
21
21
|
from falyx.hook_manager import HookType
|
@@ -25,41 +25,7 @@ from falyx.selection import (
|
|
25
25
|
prompt_for_selection,
|
26
26
|
render_selection_dict_table,
|
27
27
|
)
|
28
|
-
from falyx.themes
|
29
|
-
|
30
|
-
|
31
|
-
class FileReturnType(Enum):
|
32
|
-
"""Enum for file return types."""
|
33
|
-
|
34
|
-
TEXT = "text"
|
35
|
-
PATH = "path"
|
36
|
-
JSON = "json"
|
37
|
-
TOML = "toml"
|
38
|
-
YAML = "yaml"
|
39
|
-
CSV = "csv"
|
40
|
-
TSV = "tsv"
|
41
|
-
XML = "xml"
|
42
|
-
|
43
|
-
@classmethod
|
44
|
-
def _get_alias(cls, value: str) -> str:
|
45
|
-
aliases = {
|
46
|
-
"yml": "yaml",
|
47
|
-
"txt": "text",
|
48
|
-
"file": "path",
|
49
|
-
"filepath": "path",
|
50
|
-
}
|
51
|
-
return aliases.get(value, value)
|
52
|
-
|
53
|
-
@classmethod
|
54
|
-
def _missing_(cls, value: object) -> FileReturnType:
|
55
|
-
if isinstance(value, str):
|
56
|
-
normalized = value.lower()
|
57
|
-
alias = cls._get_alias(normalized)
|
58
|
-
for member in cls:
|
59
|
-
if member.value == alias:
|
60
|
-
return member
|
61
|
-
valid = ", ".join(member.value for member in cls)
|
62
|
-
raise ValueError(f"Invalid FileReturnType: '{value}'. Must be one of: {valid}")
|
28
|
+
from falyx.themes import OneColors
|
63
29
|
|
64
30
|
|
65
31
|
class SelectFileAction(BaseAction):
|
@@ -6,7 +6,7 @@ from prompt_toolkit import PromptSession
|
|
6
6
|
from rich.console import Console
|
7
7
|
from rich.tree import Tree
|
8
8
|
|
9
|
-
from falyx.action import BaseAction
|
9
|
+
from falyx.action.action import BaseAction
|
10
10
|
from falyx.context import ExecutionContext
|
11
11
|
from falyx.execution_registry import ExecutionRegistry as er
|
12
12
|
from falyx.hook_manager import HookType
|
@@ -18,7 +18,7 @@ from falyx.selection import (
|
|
18
18
|
render_selection_dict_table,
|
19
19
|
render_selection_indexed_table,
|
20
20
|
)
|
21
|
-
from falyx.themes
|
21
|
+
from falyx.themes import OneColors
|
22
22
|
from falyx.utils import CaseInsensitiveDict
|
23
23
|
|
24
24
|
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed
|
2
|
+
"""signal_action.py"""
|
3
|
+
from rich.tree import Tree
|
4
|
+
|
5
|
+
from falyx.action.action import Action
|
6
|
+
from falyx.signals import FlowSignal
|
7
|
+
from falyx.themes import OneColors
|
8
|
+
|
9
|
+
|
10
|
+
class SignalAction(Action):
|
11
|
+
"""
|
12
|
+
An action that raises a control flow signal when executed.
|
13
|
+
|
14
|
+
Useful for exiting a menu, going back, or halting execution gracefully.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(self, name: str, signal: Exception):
|
18
|
+
self.signal = signal
|
19
|
+
super().__init__(name, action=self.raise_signal)
|
20
|
+
|
21
|
+
async def raise_signal(self, *args, **kwargs):
|
22
|
+
raise self.signal
|
23
|
+
|
24
|
+
@property
|
25
|
+
def signal(self):
|
26
|
+
return self._signal
|
27
|
+
|
28
|
+
@signal.setter
|
29
|
+
def signal(self, value: FlowSignal):
|
30
|
+
if not isinstance(value, FlowSignal):
|
31
|
+
raise TypeError(
|
32
|
+
f"Signal must be an FlowSignal instance, got {type(value).__name__}"
|
33
|
+
)
|
34
|
+
self._signal = value
|
35
|
+
|
36
|
+
def __str__(self):
|
37
|
+
return f"SignalAction(name={self.name}, signal={self._signal.__class__.__name__})"
|
38
|
+
|
39
|
+
async def preview(self, parent: Tree | None = None):
|
40
|
+
label = f"[{OneColors.LIGHT_RED}]⚡ SignalAction[/] '{self.signal.__class__.__name__}'"
|
41
|
+
tree = parent.add(label) if parent else Tree(label)
|
42
|
+
if not parent:
|
43
|
+
self.console.print(tree)
|
falyx/action/types.py
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from enum import Enum
|
4
|
+
|
5
|
+
|
6
|
+
class FileReturnType(Enum):
|
7
|
+
"""Enum for file return types."""
|
8
|
+
|
9
|
+
TEXT = "text"
|
10
|
+
PATH = "path"
|
11
|
+
JSON = "json"
|
12
|
+
TOML = "toml"
|
13
|
+
YAML = "yaml"
|
14
|
+
CSV = "csv"
|
15
|
+
TSV = "tsv"
|
16
|
+
XML = "xml"
|
17
|
+
|
18
|
+
@classmethod
|
19
|
+
def _get_alias(cls, value: str) -> str:
|
20
|
+
aliases = {
|
21
|
+
"yml": "yaml",
|
22
|
+
"txt": "text",
|
23
|
+
"file": "path",
|
24
|
+
"filepath": "path",
|
25
|
+
}
|
26
|
+
return aliases.get(value, value)
|
27
|
+
|
28
|
+
@classmethod
|
29
|
+
def _missing_(cls, value: object) -> FileReturnType:
|
30
|
+
if isinstance(value, str):
|
31
|
+
normalized = value.lower()
|
32
|
+
alias = cls._get_alias(normalized)
|
33
|
+
for member in cls:
|
34
|
+
if member.value == alias:
|
35
|
+
return member
|
36
|
+
valid = ", ".join(member.value for member in cls)
|
37
|
+
raise ValueError(f"Invalid FileReturnType: '{value}'. Must be one of: {valid}")
|
falyx/bottom_bar.py
CHANGED
@@ -8,7 +8,7 @@ from prompt_toolkit.key_binding import KeyBindings
|
|
8
8
|
from rich.console import Console
|
9
9
|
|
10
10
|
from falyx.options_manager import OptionsManager
|
11
|
-
from falyx.themes
|
11
|
+
from falyx.themes import OneColors
|
12
12
|
from falyx.utils import CaseInsensitiveDict, chunks
|
13
13
|
|
14
14
|
|
falyx/command.py
CHANGED
@@ -26,19 +26,19 @@ 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 import Action, ActionGroup, BaseAction, ChainedAction
|
29
|
+
from falyx.action.action import Action, ActionGroup, BaseAction, ChainedAction
|
30
|
+
from falyx.action.io_action import BaseIOAction
|
30
31
|
from falyx.context import ExecutionContext
|
31
32
|
from falyx.debug import register_debug_hooks
|
32
33
|
from falyx.exceptions import FalyxError
|
33
34
|
from falyx.execution_registry import ExecutionRegistry as er
|
34
35
|
from falyx.hook_manager import HookManager, HookType
|
35
|
-
from falyx.io_action import BaseIOAction
|
36
36
|
from falyx.logger import logger
|
37
37
|
from falyx.options_manager import OptionsManager
|
38
38
|
from falyx.prompt_utils import confirm_async, should_prompt_user
|
39
39
|
from falyx.retry import RetryPolicy
|
40
40
|
from falyx.retry_utils import enable_retries_recursively
|
41
|
-
from falyx.themes
|
41
|
+
from falyx.themes import OneColors
|
42
42
|
from falyx.utils import _noop, ensure_async
|
43
43
|
|
44
44
|
console = Console(color_system="auto")
|
falyx/config.py
CHANGED
@@ -13,12 +13,12 @@ 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 import Action, BaseAction
|
16
|
+
from falyx.action.action import Action, BaseAction
|
17
17
|
from falyx.command import Command
|
18
18
|
from falyx.falyx import Falyx
|
19
19
|
from falyx.logger import logger
|
20
20
|
from falyx.retry import RetryPolicy
|
21
|
-
from falyx.themes
|
21
|
+
from falyx.themes import OneColors
|
22
22
|
|
23
23
|
console = Console(color_system="auto")
|
24
24
|
|
falyx/execution_registry.py
CHANGED
falyx/falyx.py
CHANGED
@@ -38,7 +38,7 @@ from rich.console import Console
|
|
38
38
|
from rich.markdown import Markdown
|
39
39
|
from rich.table import Table
|
40
40
|
|
41
|
-
from falyx.action import Action, BaseAction
|
41
|
+
from falyx.action.action import Action, BaseAction
|
42
42
|
from falyx.bottom_bar import BottomBar
|
43
43
|
from falyx.command import Command
|
44
44
|
from falyx.context import ExecutionContext
|
@@ -56,7 +56,7 @@ from falyx.options_manager import OptionsManager
|
|
56
56
|
from falyx.parsers import get_arg_parsers
|
57
57
|
from falyx.retry import RetryPolicy
|
58
58
|
from falyx.signals import BackSignal, QuitSignal
|
59
|
-
from falyx.themes
|
59
|
+
from falyx.themes import OneColors, get_nord_theme
|
60
60
|
from falyx.utils import CaseInsensitiveDict, chunks, get_program_invocation
|
61
61
|
from falyx.version import __version__
|
62
62
|
|
falyx/hooks.py
CHANGED
@@ -6,7 +6,7 @@ from typing import Any, Callable
|
|
6
6
|
from falyx.context import ExecutionContext
|
7
7
|
from falyx.exceptions import CircuitBreakerOpen
|
8
8
|
from falyx.logger import logger
|
9
|
-
from falyx.themes
|
9
|
+
from falyx.themes import OneColors
|
10
10
|
|
11
11
|
|
12
12
|
class ResultReporter:
|
falyx/menu.py
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
|
5
|
+
from falyx.action import BaseAction
|
6
|
+
from falyx.signals import BackSignal, QuitSignal
|
7
|
+
from falyx.themes import OneColors
|
8
|
+
from falyx.utils import CaseInsensitiveDict
|
9
|
+
|
10
|
+
|
11
|
+
@dataclass
|
12
|
+
class MenuOption:
|
13
|
+
"""Represents a single menu option with a description and an action to execute."""
|
14
|
+
|
15
|
+
description: str
|
16
|
+
action: BaseAction
|
17
|
+
style: str = OneColors.WHITE
|
18
|
+
|
19
|
+
def __post_init__(self):
|
20
|
+
if not isinstance(self.description, str):
|
21
|
+
raise TypeError("MenuOption description must be a string.")
|
22
|
+
if not isinstance(self.action, BaseAction):
|
23
|
+
raise TypeError("MenuOption action must be a BaseAction instance.")
|
24
|
+
|
25
|
+
def render(self, key: str) -> str:
|
26
|
+
"""Render the menu option for display."""
|
27
|
+
return f"[{OneColors.WHITE}][{key}][/] [{self.style}]{self.description}[/]"
|
28
|
+
|
29
|
+
|
30
|
+
class MenuOptionMap(CaseInsensitiveDict):
|
31
|
+
"""
|
32
|
+
Manages menu options including validation, reserved key protection,
|
33
|
+
and special signal entries like Quit and Back.
|
34
|
+
"""
|
35
|
+
|
36
|
+
RESERVED_KEYS = {"Q", "B"}
|
37
|
+
|
38
|
+
def __init__(
|
39
|
+
self,
|
40
|
+
options: dict[str, MenuOption] | None = None,
|
41
|
+
allow_reserved: bool = False,
|
42
|
+
):
|
43
|
+
super().__init__()
|
44
|
+
self.allow_reserved = allow_reserved
|
45
|
+
if options:
|
46
|
+
self.update(options)
|
47
|
+
self._inject_reserved_defaults()
|
48
|
+
|
49
|
+
def _inject_reserved_defaults(self):
|
50
|
+
from falyx.action import SignalAction
|
51
|
+
|
52
|
+
self._add_reserved(
|
53
|
+
"Q",
|
54
|
+
MenuOption("Exit", SignalAction("Quit", QuitSignal()), OneColors.DARK_RED),
|
55
|
+
)
|
56
|
+
self._add_reserved(
|
57
|
+
"B",
|
58
|
+
MenuOption("Back", SignalAction("Back", BackSignal()), OneColors.DARK_YELLOW),
|
59
|
+
)
|
60
|
+
|
61
|
+
def _add_reserved(self, key: str, option: MenuOption) -> None:
|
62
|
+
"""Add a reserved key, bypassing validation."""
|
63
|
+
norm_key = key.upper()
|
64
|
+
super().__setitem__(norm_key, option)
|
65
|
+
|
66
|
+
def __setitem__(self, key: str, option: MenuOption) -> None:
|
67
|
+
if not isinstance(option, MenuOption):
|
68
|
+
raise TypeError(f"Value for key '{key}' must be a MenuOption.")
|
69
|
+
norm_key = key.upper()
|
70
|
+
if norm_key in self.RESERVED_KEYS and not self.allow_reserved:
|
71
|
+
raise ValueError(
|
72
|
+
f"Key '{key}' is reserved and cannot be used in MenuOptionMap."
|
73
|
+
)
|
74
|
+
super().__setitem__(norm_key, option)
|
75
|
+
|
76
|
+
def __delitem__(self, key: str) -> None:
|
77
|
+
if key.upper() in self.RESERVED_KEYS and not self.allow_reserved:
|
78
|
+
raise ValueError(f"Cannot delete reserved option '{key}'.")
|
79
|
+
super().__delitem__(key)
|
80
|
+
|
81
|
+
def items(self, include_reserved: bool = True):
|
82
|
+
for k, v in super().items():
|
83
|
+
if not include_reserved and k in self.RESERVED_KEYS:
|
84
|
+
continue
|
85
|
+
yield k, v
|
falyx/prompt_utils.py
CHANGED
falyx/protocols.py
CHANGED
falyx/retry_utils.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed
|
2
2
|
"""retry_utils.py"""
|
3
|
-
from falyx.action import Action, BaseAction
|
3
|
+
from falyx.action.action import Action, BaseAction
|
4
4
|
from falyx.hook_manager import HookType
|
5
5
|
from falyx.retry import RetryHandler, RetryPolicy
|
6
6
|
|
falyx/selection.py
CHANGED
@@ -9,7 +9,7 @@ from rich.console import Console
|
|
9
9
|
from rich.markup import escape
|
10
10
|
from rich.table import Table
|
11
11
|
|
12
|
-
from falyx.themes
|
12
|
+
from falyx.themes import OneColors
|
13
13
|
from falyx.utils import chunks
|
14
14
|
from falyx.validators import int_range_validator, key_validator
|
15
15
|
|
falyx/themes/__init__.py
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
"""
|
2
|
+
Falyx CLI Framework
|
3
|
+
|
4
|
+
Copyright (c) 2025 rtj.dev LLC.
|
5
|
+
Licensed under the MIT License. See LICENSE file for details.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from .colors import ColorsMeta, NordColors, OneColors, get_nord_theme
|
9
|
+
|
10
|
+
__all__ = [
|
11
|
+
"OneColors",
|
12
|
+
"NordColors",
|
13
|
+
"get_nord_theme",
|
14
|
+
"ColorsMeta",
|
15
|
+
]
|
falyx/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.1.
|
1
|
+
__version__ = "0.1.25"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: falyx
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.25
|
4
4
|
Summary: Reliable and introspectable async CLI action framework.
|
5
5
|
License: MIT
|
6
6
|
Author: Roland Thomas Jr
|
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
14
14
|
Classifier: Programming Language :: Python :: 3.13
|
15
|
+
Requires-Dist: aiohttp (>=3.11,<4.0)
|
15
16
|
Requires-Dist: prompt_toolkit (>=3.0,<4.0)
|
16
17
|
Requires-Dist: pydantic (>=2.0,<3.0)
|
17
18
|
Requires-Dist: python-json-logger (>=3.3.0,<4.0.0)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
falyx/.pytyped,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
falyx/__init__.py,sha256=L40665QyjAqHQxHdxxY2_yPeDa4p0LE7Nu_2dkm08Ls,650
|
3
|
+
falyx/__main__.py,sha256=g_LwJieofK3DJzCYtpkAMEeOXhzSLQenb7pRVUqcf-Y,2152
|
4
|
+
falyx/action/__init__.py,sha256=P3Eh0lE2VunzxqwfLbZKxE6FEcdR_u5OROkDTBYgeP0,904
|
5
|
+
falyx/action/action.py,sha256=CJB9eeeEqBGkZHjMpG24eXHRjouKSfESCI1zWzoE7JQ,32488
|
6
|
+
falyx/action/action_factory.py,sha256=qhlx8-BAEiNDJrbzF0HZzipY09Ple6J7FxxNvoBda9Y,4228
|
7
|
+
falyx/action/http_action.py,sha256=aIieGHyZSkz1ZGay-fwgDYZ0QF17XypAWtKeVAYp5f4,5806
|
8
|
+
falyx/action/io_action.py,sha256=zdDq07zSLlaShBQ3ztXTRC6aZL0JoERNZSmvHy1V22w,9718
|
9
|
+
falyx/action/menu_action.py,sha256=8qttPuq0AUh_oFaa3A82e19dSl4ZhQ9Y_j5CU7zHAlc,5532
|
10
|
+
falyx/action/select_file_action.py,sha256=hHLhmTSacWaUXhRTeIIiXt8gR7zbjkXJ2MAkKQYCpp4,7799
|
11
|
+
falyx/action/selection_action.py,sha256=22rF7UqRrQAMjGIheDqAbUizVMBg9aCl9e4VOLLZZJo,8811
|
12
|
+
falyx/action/signal_action.py,sha256=5UMqvzy7fBnLANGwYUWoe1VRhrr7e-yOVeLdOnCBiJo,1350
|
13
|
+
falyx/action/types.py,sha256=iVD-bHm1GRXOTIlHOeT_KcDBRZm4Hz5Xzl_BOalvEf4,961
|
14
|
+
falyx/bottom_bar.py,sha256=iWxgOKWgn5YmREeZBuGA50FzqzEfz1-Vnqm0V_fhldc,7383
|
15
|
+
falyx/command.py,sha256=s7r9aeUYEk9iUNE69JQtlFoPx9AehTxkHMPxpLKVIOA,12238
|
16
|
+
falyx/config.py,sha256=yg5KHkiZh4sNOPrkqL-ym0YrMUomBqUSyuOcXFOp6Ew,7204
|
17
|
+
falyx/config_schema.py,sha256=j5GQuHVlaU-VLxLF9t8idZRjqOP9MIKp1hyd9NhpAGU,3124
|
18
|
+
falyx/context.py,sha256=FNF-IS7RMDxel2l3kskEqQImZ0mLO6zvGw_xC9cIzgI,10338
|
19
|
+
falyx/debug.py,sha256=oWWTLOF8elrx_RGZ1G4pbzfFr46FjB0woFXpVU2wmjU,1567
|
20
|
+
falyx/exceptions.py,sha256=Qxp6UScZWEyno-6Lgksrv3s9iwjbr2U-d6hun-_xpc0,798
|
21
|
+
falyx/execution_registry.py,sha256=re56TImfL67p30ZlVBjqxz9Nn34SD4gvTlwFVPSzVCM,4712
|
22
|
+
falyx/falyx.py,sha256=cSCVtLsW8n5ZfRvjxVTJF5Rf3Qg1pNWWVqRdqSHTEGo,40545
|
23
|
+
falyx/hook_manager.py,sha256=GuGxVVz9FXrU39Tk220QcsLsMXeut7ZDkGW3hU9GcwQ,2952
|
24
|
+
falyx/hooks.py,sha256=IV2nbj5FjY2m3_L7x4mYBnaRDG45E8tWQU90i4butlw,2940
|
25
|
+
falyx/init.py,sha256=abcSlPmxVeByLIHdUkNjqtO_tEkO3ApC6f9WbxsSEWg,3393
|
26
|
+
falyx/logger.py,sha256=1Mfb_vJFJ1tQwziuyU2p-cSMi2Js8N2byniFEnI6vOQ,132
|
27
|
+
falyx/menu.py,sha256=faxGgocqQYY6HtzVbenHaFj8YqsmycBEyziC8Ahzqjo,2870
|
28
|
+
falyx/options_manager.py,sha256=dFAnQw543tQ6Xupvh1PwBrhiSWlSACHw8K-sHP_lUh4,2842
|
29
|
+
falyx/parsers.py,sha256=hxrBouQEqdgk6aWzNa7UwTg7u55vJffSEUUTiiQoI0U,5602
|
30
|
+
falyx/prompt_utils.py,sha256=qgk0bXs7mwzflqzWyFhEOTpKQ_ZtMIqGhKeg-ocwNnE,1542
|
31
|
+
falyx/protocols.py,sha256=dXNS-kh-5XB92PE5POy4uJ4KLT0O3ZAoiqw55jgR2IM,306
|
32
|
+
falyx/retry.py,sha256=UUzY6FlKobr84Afw7yJO9rj3AIQepDk2fcWs6_1gi6Q,3788
|
33
|
+
falyx/retry_utils.py,sha256=EAzc-ECTu8AxKkmlw28ioOW9y-Y9tLQ0KasvSkBRYgs,694
|
34
|
+
falyx/selection.py,sha256=l2LLISqgP8xfHdcTAEbTTqs_Bae4-LVUKMN7VQH7tM0,10731
|
35
|
+
falyx/signals.py,sha256=4PTuVRB_P_aWfnU8pANqhMxGTLq7TJDEyk9jCp0Bx2c,713
|
36
|
+
falyx/tagged_table.py,sha256=4SV-SdXFrAhy1JNToeBCvyxT-iWVf6cWY7XETTys4n8,1067
|
37
|
+
falyx/themes/__init__.py,sha256=1CZhEUCin9cUk8IGYBUFkVvdHRNNJBEFXccHwpUKZCA,284
|
38
|
+
falyx/themes/colors.py,sha256=4aaeAHJetmeNInI0Zytg4E3YqKfPFelpf04vtjSvsS8,19776
|
39
|
+
falyx/utils.py,sha256=uss-FV8p164pmhoqYtQt8gNp5z8fGbuMAk4dRJ6RopI,6717
|
40
|
+
falyx/validators.py,sha256=t5iyzVpY8tdC4rfhr4isEfWpD5gNTzjeX_Hbi_Uq6sA,1328
|
41
|
+
falyx/version.py,sha256=Ej7LsXg-6CASlaEHsZkUoLDpYEfHeFKdIeXMIM0esgA,23
|
42
|
+
falyx-0.1.25.dist-info/LICENSE,sha256=B0yqgaHuSdhN7T3OBmgQSiDTy8HqT5Oe_dLypRe4Ra4,1073
|
43
|
+
falyx-0.1.25.dist-info/METADATA,sha256=ZFvg_e4wFYKOB5f85YQswxEk1bQ9H3Eg6di63jcLrkU,5521
|
44
|
+
falyx-0.1.25.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
45
|
+
falyx-0.1.25.dist-info/entry_points.txt,sha256=j8owOSl2j1Ss8DtGMnKfgehKaolqnIPhVFHaUBLUnMs,45
|
46
|
+
falyx-0.1.25.dist-info/RECORD,,
|
falyx/signal_action.py
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed
|
2
|
-
"""signal_action.py"""
|
3
|
-
from falyx.action import Action
|
4
|
-
from falyx.signals import FlowSignal
|
5
|
-
|
6
|
-
|
7
|
-
class SignalAction(Action):
|
8
|
-
"""
|
9
|
-
An action that raises a control flow signal when executed.
|
10
|
-
|
11
|
-
Useful for exiting a menu, going back, or halting execution gracefully.
|
12
|
-
"""
|
13
|
-
|
14
|
-
def __init__(self, name: str, signal: Exception):
|
15
|
-
if not isinstance(signal, FlowSignal):
|
16
|
-
raise TypeError(
|
17
|
-
f"Signal must be an FlowSignal instance, got {type(signal).__name__}"
|
18
|
-
)
|
19
|
-
|
20
|
-
async def raise_signal(*args, **kwargs):
|
21
|
-
raise signal
|
22
|
-
|
23
|
-
super().__init__(name=name, action=raise_signal)
|
24
|
-
self._signal = signal
|
25
|
-
|
26
|
-
@property
|
27
|
-
def signal(self):
|
28
|
-
return self._signal
|
29
|
-
|
30
|
-
def __str__(self):
|
31
|
-
return f"SignalAction(name={self.name}, signal={self._signal.__class__.__name__})"
|
falyx-0.1.24.dist-info/RECORD
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
falyx/.pytyped,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
falyx/__init__.py,sha256=dYRamQJlT1Zoy5Uu1uG4NCV05Xk98nN1LAQrSR1CT2A,643
|
3
|
-
falyx/__main__.py,sha256=g_LwJieofK3DJzCYtpkAMEeOXhzSLQenb7pRVUqcf-Y,2152
|
4
|
-
falyx/action.py,sha256=9iOsqi-7tiNdBcGFrAQHGBCsTKZoBYXkClZE0DM9qhQ,32495
|
5
|
-
falyx/action_factory.py,sha256=RKKb9C49xYNnMBgNvaDYTspVF3jmpBtNibrxlzLGiQQ,4228
|
6
|
-
falyx/bottom_bar.py,sha256=GdyM-IY-E08c2GSaeAGVv1h1suOQuohFTRwbqtawbW0,7390
|
7
|
-
falyx/command.py,sha256=PCnGDbx8E-168FcuUvz87YqR94FvYCQX8dj6tVXnMtw,12231
|
8
|
-
falyx/config.py,sha256=nbAEea1-3zNhycrQ9aKuMqqa_EaQGBK0HcCAGsj_rZ0,7204
|
9
|
-
falyx/config_schema.py,sha256=j5GQuHVlaU-VLxLF9t8idZRjqOP9MIKp1hyd9NhpAGU,3124
|
10
|
-
falyx/context.py,sha256=FNF-IS7RMDxel2l3kskEqQImZ0mLO6zvGw_xC9cIzgI,10338
|
11
|
-
falyx/debug.py,sha256=oWWTLOF8elrx_RGZ1G4pbzfFr46FjB0woFXpVU2wmjU,1567
|
12
|
-
falyx/exceptions.py,sha256=Qxp6UScZWEyno-6Lgksrv3s9iwjbr2U-d6hun-_xpc0,798
|
13
|
-
falyx/execution_registry.py,sha256=io2hX9VkWJBRch-G7thla1eH_PgyVjjWf9qU5foOOEA,4719
|
14
|
-
falyx/falyx.py,sha256=KuIAaZj4RgiFdiAzOMswZ8n6lUKN00L5k4zWoJA1PBk,40545
|
15
|
-
falyx/hook_manager.py,sha256=GuGxVVz9FXrU39Tk220QcsLsMXeut7ZDkGW3hU9GcwQ,2952
|
16
|
-
falyx/hooks.py,sha256=KOmUGP6xWU-eTW8QOl-qEflNRxZRf_OHA0N7gph13UM,2947
|
17
|
-
falyx/http_action.py,sha256=sGADtRhSRuNu4UiEo1oeTHeC2rgAlALZm08-X4Pne34,5806
|
18
|
-
falyx/init.py,sha256=abcSlPmxVeByLIHdUkNjqtO_tEkO3ApC6f9WbxsSEWg,3393
|
19
|
-
falyx/io_action.py,sha256=5TIWeRUIffvkbdMbureLMiNk6mcy-tCaMMBRCeFFfgM,9718
|
20
|
-
falyx/logger.py,sha256=1Mfb_vJFJ1tQwziuyU2p-cSMi2Js8N2byniFEnI6vOQ,132
|
21
|
-
falyx/menu_action.py,sha256=jLYUR6-6F5NIDb9kP0Ysovmy3mtfO8xTXRRZMRaTtY0,8183
|
22
|
-
falyx/options_manager.py,sha256=dFAnQw543tQ6Xupvh1PwBrhiSWlSACHw8K-sHP_lUh4,2842
|
23
|
-
falyx/parsers.py,sha256=hxrBouQEqdgk6aWzNa7UwTg7u55vJffSEUUTiiQoI0U,5602
|
24
|
-
falyx/prompt_utils.py,sha256=6qt65HESo79-rZhIWpgndrYG6yJwk8tMSJCKXar0dP0,1549
|
25
|
-
falyx/protocols.py,sha256=Sk2a1rz5Tk7iDUVTeitItNX-Kg3kwXOSwIjojEOE1mI,299
|
26
|
-
falyx/retry.py,sha256=UUzY6FlKobr84Afw7yJO9rj3AIQepDk2fcWs6_1gi6Q,3788
|
27
|
-
falyx/retry_utils.py,sha256=1xPSr-1ZJsUXnXFyGNZZJ9h9eE4ExHjJMkyjHIm5cd8,687
|
28
|
-
falyx/select_file_action.py,sha256=698t0Mc5_6dPXjxuKBC_O7u35o08nLqkgpQH82dZafc,8678
|
29
|
-
falyx/selection.py,sha256=r__wrXaLz4oJobeqniKvHrEhe9vYVsZo1SoB3Cn6qRM,10738
|
30
|
-
falyx/selection_action.py,sha256=PdY-3ewMhgC8F3DKS6TCWcAoOD3va9Hp8bPkdZSMWBE,8811
|
31
|
-
falyx/signal_action.py,sha256=DSwVkL81PLWAHRHZbpiYlPH1ew97KL6TPs3XlU--RJY,916
|
32
|
-
falyx/signals.py,sha256=4PTuVRB_P_aWfnU8pANqhMxGTLq7TJDEyk9jCp0Bx2c,713
|
33
|
-
falyx/tagged_table.py,sha256=4SV-SdXFrAhy1JNToeBCvyxT-iWVf6cWY7XETTys4n8,1067
|
34
|
-
falyx/themes/colors.py,sha256=4aaeAHJetmeNInI0Zytg4E3YqKfPFelpf04vtjSvsS8,19776
|
35
|
-
falyx/utils.py,sha256=uss-FV8p164pmhoqYtQt8gNp5z8fGbuMAk4dRJ6RopI,6717
|
36
|
-
falyx/validators.py,sha256=t5iyzVpY8tdC4rfhr4isEfWpD5gNTzjeX_Hbi_Uq6sA,1328
|
37
|
-
falyx/version.py,sha256=Jq7e1LcKcQSNVg4EOJ-acPyPgs8Os5cYEZWXrQsI7Pg,23
|
38
|
-
falyx-0.1.24.dist-info/LICENSE,sha256=B0yqgaHuSdhN7T3OBmgQSiDTy8HqT5Oe_dLypRe4Ra4,1073
|
39
|
-
falyx-0.1.24.dist-info/METADATA,sha256=b7Ca1bcIlHrVsb3YLlSUQCgzoh0Jvd7qfUeD6ms5mWo,5484
|
40
|
-
falyx-0.1.24.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
41
|
-
falyx-0.1.24.dist-info/entry_points.txt,sha256=j8owOSl2j1Ss8DtGMnKfgehKaolqnIPhVFHaUBLUnMs,45
|
42
|
-
falyx-0.1.24.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|