falyx 0.1.24__tar.gz → 0.1.25__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. {falyx-0.1.24 → falyx-0.1.25}/PKG-INFO +2 -1
  2. {falyx-0.1.24 → falyx-0.1.25}/falyx/__init__.py +1 -1
  3. falyx-0.1.25/falyx/action/__init__.py +41 -0
  4. {falyx-0.1.24/falyx → falyx-0.1.25/falyx/action}/action.py +1 -1
  5. {falyx-0.1.24/falyx → falyx-0.1.25/falyx/action}/action_factory.py +2 -2
  6. {falyx-0.1.24/falyx → falyx-0.1.25/falyx/action}/http_action.py +2 -2
  7. {falyx-0.1.24/falyx → falyx-0.1.25/falyx/action}/io_action.py +2 -2
  8. {falyx-0.1.24/falyx → falyx-0.1.25/falyx/action}/menu_action.py +4 -80
  9. {falyx-0.1.24/falyx → falyx-0.1.25/falyx/action}/select_file_action.py +3 -37
  10. {falyx-0.1.24/falyx → falyx-0.1.25/falyx/action}/selection_action.py +2 -2
  11. falyx-0.1.25/falyx/action/signal_action.py +43 -0
  12. falyx-0.1.25/falyx/action/types.py +37 -0
  13. {falyx-0.1.24 → falyx-0.1.25}/falyx/bottom_bar.py +1 -1
  14. {falyx-0.1.24 → falyx-0.1.25}/falyx/command.py +3 -3
  15. {falyx-0.1.24 → falyx-0.1.25}/falyx/config.py +2 -2
  16. {falyx-0.1.24 → falyx-0.1.25}/falyx/execution_registry.py +1 -1
  17. {falyx-0.1.24 → falyx-0.1.25}/falyx/falyx.py +2 -2
  18. {falyx-0.1.24 → falyx-0.1.25}/falyx/hooks.py +1 -1
  19. falyx-0.1.25/falyx/menu.py +85 -0
  20. {falyx-0.1.24 → falyx-0.1.25}/falyx/prompt_utils.py +1 -1
  21. {falyx-0.1.24 → falyx-0.1.25}/falyx/protocols.py +1 -1
  22. {falyx-0.1.24 → falyx-0.1.25}/falyx/retry_utils.py +1 -1
  23. {falyx-0.1.24 → falyx-0.1.25}/falyx/selection.py +1 -1
  24. falyx-0.1.25/falyx/themes/__init__.py +15 -0
  25. falyx-0.1.25/falyx/version.py +1 -0
  26. {falyx-0.1.24 → falyx-0.1.25}/pyproject.toml +2 -1
  27. falyx-0.1.24/falyx/signal_action.py +0 -31
  28. falyx-0.1.24/falyx/version.py +0 -1
  29. {falyx-0.1.24 → falyx-0.1.25}/LICENSE +0 -0
  30. {falyx-0.1.24 → falyx-0.1.25}/README.md +0 -0
  31. {falyx-0.1.24 → falyx-0.1.25}/falyx/.pytyped +0 -0
  32. {falyx-0.1.24 → falyx-0.1.25}/falyx/__main__.py +0 -0
  33. {falyx-0.1.24 → falyx-0.1.25}/falyx/config_schema.py +0 -0
  34. {falyx-0.1.24 → falyx-0.1.25}/falyx/context.py +0 -0
  35. {falyx-0.1.24 → falyx-0.1.25}/falyx/debug.py +0 -0
  36. {falyx-0.1.24 → falyx-0.1.25}/falyx/exceptions.py +0 -0
  37. {falyx-0.1.24 → falyx-0.1.25}/falyx/hook_manager.py +0 -0
  38. {falyx-0.1.24 → falyx-0.1.25}/falyx/init.py +0 -0
  39. {falyx-0.1.24 → falyx-0.1.25}/falyx/logger.py +0 -0
  40. {falyx-0.1.24 → falyx-0.1.25}/falyx/options_manager.py +0 -0
  41. {falyx-0.1.24 → falyx-0.1.25}/falyx/parsers.py +0 -0
  42. {falyx-0.1.24 → falyx-0.1.25}/falyx/retry.py +0 -0
  43. {falyx-0.1.24 → falyx-0.1.25}/falyx/signals.py +0 -0
  44. {falyx-0.1.24 → falyx-0.1.25}/falyx/tagged_table.py +0 -0
  45. {falyx-0.1.24 → falyx-0.1.25}/falyx/themes/colors.py +0 -0
  46. {falyx-0.1.24 → falyx-0.1.25}/falyx/utils.py +0 -0
  47. {falyx-0.1.24 → falyx-0.1.25}/falyx/validators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: falyx
3
- Version: 0.1.24
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)
@@ -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
@@ -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.colors import OneColors
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.colors import OneColors
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.colors import OneColors
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.colors import OneColors
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.colors import OneColors
20
- from falyx.utils import CaseInsensitiveDict, chunks
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.colors import OneColors
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.colors import OneColors
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)
@@ -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}")
@@ -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.colors import OneColors
11
+ from falyx.themes import OneColors
12
12
  from falyx.utils import CaseInsensitiveDict, chunks
13
13
 
14
14
 
@@ -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.colors import OneColors
41
+ from falyx.themes import OneColors
42
42
  from falyx.utils import _noop, ensure_async
43
43
 
44
44
  console = Console(color_system="auto")
@@ -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.colors import OneColors
21
+ from falyx.themes import OneColors
22
22
 
23
23
  console = Console(color_system="auto")
24
24
 
@@ -37,7 +37,7 @@ from rich.table import Table
37
37
 
38
38
  from falyx.context import ExecutionContext
39
39
  from falyx.logger import logger
40
- from falyx.themes.colors import OneColors
40
+ from falyx.themes import OneColors
41
41
 
42
42
 
43
43
  class ExecutionRegistry:
@@ -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.colors import OneColors, get_nord_theme
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
 
@@ -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.colors import OneColors
9
+ from falyx.themes import OneColors
10
10
 
11
11
 
12
12
  class ResultReporter:
@@ -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
@@ -8,7 +8,7 @@ from prompt_toolkit.formatted_text import (
8
8
  )
9
9
 
10
10
  from falyx.options_manager import OptionsManager
11
- from falyx.themes.colors import OneColors
11
+ from falyx.themes import OneColors
12
12
  from falyx.validators import yes_no_validator
13
13
 
14
14
 
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Any, Protocol
6
6
 
7
- from falyx.action import BaseAction
7
+ from falyx.action.action import BaseAction
8
8
 
9
9
 
10
10
  class ActionFactoryProtocol(Protocol):
@@ -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
 
@@ -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.colors import OneColors
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
 
@@ -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
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.25"
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "falyx"
3
- version = "0.1.24"
3
+ version = "0.1.25"
4
4
  description = "Reliable and introspectable async CLI action framework."
5
5
  authors = ["Roland Thomas Jr <roland@rtj.dev>"]
6
6
  license = "MIT"
@@ -15,6 +15,7 @@ pydantic = "^2.0"
15
15
  python-json-logger = "^3.3.0"
16
16
  toml = "^0.10"
17
17
  pyyaml = "^6.0"
18
+ aiohttp = "^3.11"
18
19
 
19
20
  [tool.poetry.group.dev.dependencies]
20
21
  pytest = "^8.3.5"
@@ -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__})"
@@ -1 +0,0 @@
1
- __version__ = "0.1.24"
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