algomancy-cli 0.3.17__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.
File without changes
@@ -0,0 +1,67 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Dict
4
+
5
+ from algomancy_scenario.core_configuration import CoreConfiguration
6
+
7
+
8
+ class CliConfiguration(CoreConfiguration):
9
+ """Configuration for the CLI application.
10
+
11
+ Extends the core configuration with CLI-specific options.
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ # core parameters (see CoreConfiguration)
17
+ data_path: str = "data",
18
+ has_persistent_state: bool = False,
19
+ save_type: str | None = "json",
20
+ data_object_type: type | None = None,
21
+ etl_factory: Any | None = None,
22
+ kpi_templates: Dict[str, Any] | None = None,
23
+ algo_templates: Dict[str, Any] | None = None,
24
+ input_configs: list | None = None,
25
+ autocreate: bool | None = None,
26
+ default_algo: str | None = None,
27
+ default_algo_params_values: Dict[str, Any] | None = None,
28
+ autorun: bool | None = None,
29
+ title: str = "Algomancy CLI",
30
+ # CLI specific
31
+ verbose: bool = True,
32
+ **kwargs: Any,
33
+ ) -> None:
34
+ super().__init__(
35
+ data_path=data_path,
36
+ has_persistent_state=has_persistent_state,
37
+ save_type=save_type,
38
+ data_object_type=data_object_type,
39
+ etl_factory=etl_factory,
40
+ kpi_templates=kpi_templates,
41
+ algo_templates=algo_templates,
42
+ input_configs=input_configs,
43
+ autocreate=autocreate,
44
+ default_algo=default_algo,
45
+ default_algo_params_values=default_algo_params_values,
46
+ autorun=autorun,
47
+ title=title,
48
+ **kwargs,
49
+ )
50
+
51
+ self.verbose = verbose
52
+ self._validate_cli()
53
+
54
+ def as_dict(self) -> Dict[str, Any]:
55
+ base = super().as_dict()
56
+ base.update(
57
+ {
58
+ "verbose": self.verbose,
59
+ }
60
+ )
61
+ return base
62
+
63
+ def _validate_cli(self) -> None:
64
+ if self.verbose is None:
65
+ raise ValueError(
66
+ "Boolean configuration 'verbose' must be set to True or False, not None"
67
+ )
@@ -0,0 +1,34 @@
1
+ from typing import Dict, Any, Union
2
+
3
+ from algomancy_scenario.scenariomanager import ScenarioManager
4
+ from algomancy_scenario.core_configuration import CoreConfiguration
5
+
6
+ from .cli_shell import CliShell
7
+ from .cli_configuration import CliConfiguration
8
+
9
+
10
+ class CliLauncher:
11
+ @staticmethod
12
+ def build(
13
+ cfg: Union[CliConfiguration, CoreConfiguration, Dict[str, Any]],
14
+ ) -> CliShell:
15
+ """Create a CLI shell from a CliConfiguration/CoreConfiguration or equivalent dict."""
16
+ if isinstance(cfg, dict):
17
+ cfg_obj = CliConfiguration(**cfg)
18
+ elif isinstance(cfg, CliConfiguration):
19
+ cfg_obj = cfg
20
+ elif isinstance(cfg, CoreConfiguration):
21
+ # allow passing a CoreConfiguration; wrap minimally
22
+ cfg_obj = CliConfiguration(**cfg.as_dict())
23
+ else:
24
+ raise TypeError(
25
+ "CliLauncher.build expects CliConfiguration, CoreConfiguration, or dict"
26
+ )
27
+
28
+ sm = ScenarioManager.from_config(cfg_obj)
29
+ return CliShell(sm)
30
+
31
+ @staticmethod
32
+ def run(shell: CliShell) -> None:
33
+ """Run the interactive shell."""
34
+ shell.run()
@@ -0,0 +1,170 @@
1
+ import json
2
+ import shlex
3
+ from typing import Any, Callable
4
+
5
+ from algomancy_scenario import ScenarioManager
6
+ from algomancy_utils.logger import MessageStatus
7
+
8
+
9
+ class CliShell:
10
+ """
11
+ Minimal interactive shell to exercise backend functionality via ScenarioManager.
12
+
13
+ This is intended for backend development and manual testing without the GUI.
14
+ """
15
+
16
+ def __init__(self, scenario_manager: ScenarioManager):
17
+ self.sm = scenario_manager
18
+ self._commands: dict[str, Callable[[list[str]], None]] = {
19
+ "help": self._cmd_help,
20
+ "h": self._cmd_help,
21
+ "?": self._cmd_help,
22
+ "quit": self._cmd_quit,
23
+ "exit": self._cmd_quit,
24
+ "list-scenarios": self._cmd_list_scenarios,
25
+ "ls": self._cmd_list_scenarios,
26
+ "list-data": self._cmd_list_data,
27
+ "ld": self._cmd_list_data,
28
+ "load-data": self._cmd_load_data,
29
+ "etl-data": self._cmd_etl_data,
30
+ "create-scenario": self._cmd_create_scenario,
31
+ "run": self._cmd_run,
32
+ "status": self._cmd_status,
33
+ }
34
+ self._running = False
35
+
36
+ # ---------- Public API ----------
37
+ def run(self) -> None:
38
+ self._running = True
39
+ self.sm.log(
40
+ "Algomancy CLI Shell. Type 'help' for available commands.",
41
+ MessageStatus.SUCCESS,
42
+ )
43
+ while self._running:
44
+ try:
45
+ raw = input("algomancy> ").strip()
46
+ except (EOFError, KeyboardInterrupt):
47
+ print()
48
+ break
49
+ if not raw:
50
+ continue
51
+ parts = shlex.split(raw)
52
+ cmd, args = parts[0], parts[1:]
53
+ handler = self._commands.get(cmd)
54
+ if handler is None:
55
+ self.sm.log(
56
+ f"Unknown command: {cmd}. Type 'help' for a list of commands.",
57
+ MessageStatus.ERROR,
58
+ )
59
+ continue
60
+ try:
61
+ handler(args)
62
+ except Exception as ex: # keep shell alive during development
63
+ self.sm.log(f"Error: {ex}", MessageStatus.ERROR)
64
+
65
+ # ---------- Command handlers ----------
66
+ def _cmd_help(self, _: list[str]) -> None:
67
+ print(
68
+ "\nAvailable commands:\n"
69
+ " help | h | ? Show this help.\n"
70
+ " quit | exit Exit the shell.\n"
71
+ " list-data | ld List available datasets.\n"
72
+ " load-data <name> Load example data into dataset <name>.\n"
73
+ " etl-data <name> Run ETL to create dataset <name>.\n"
74
+ " list-scenarios | ls List scenarios.\n"
75
+ " create-scenario <tag> <dataset_key> <algo> [json_params]\n"
76
+ " Create a scenario. Params as JSON object.\n"
77
+ " run <scenario_id_or_tag> Run a scenario (waits until complete).\n"
78
+ " status Show processing status.\n"
79
+ )
80
+
81
+ def _cmd_quit(self, _: list[str]) -> None:
82
+ self._running = False
83
+
84
+ def _cmd_list_data(self, _: list[str]) -> None:
85
+ keys = self.sm.get_data_keys()
86
+ if not keys:
87
+ print("No datasets available.")
88
+ return
89
+ print("Datasets:")
90
+ for k in keys:
91
+ print(f" - {k}")
92
+
93
+ def _cmd_load_data(self, args: list[str]) -> None:
94
+ if len(args) < 1:
95
+ self.sm.log("Usage: load-data <dataset_name>", MessageStatus.WARNING)
96
+ return
97
+ name = args[0]
98
+ self.sm.debug_load_data(name)
99
+ self.sm.log(
100
+ f"Loaded example data into dataset '{name}'.", MessageStatus.SUCCESS
101
+ )
102
+
103
+ def _cmd_etl_data(self, args: list[str]) -> None:
104
+ if len(args) < 1:
105
+ self.sm.log("Usage: etl-data <dataset_name>", MessageStatus.WARNING)
106
+ return
107
+ name = args[0]
108
+ self.sm.debug_etl_data(name)
109
+ self.sm.log(
110
+ f"ETL completed, dataset '{name}' available.", MessageStatus.SUCCESS
111
+ )
112
+
113
+ def _cmd_list_scenarios(self, _: list[str]) -> None:
114
+ scenarios = self.sm.list_scenarios()
115
+ if not scenarios:
116
+ print("No scenarios defined.")
117
+ return
118
+ print("Scenarios:")
119
+ for s in scenarios:
120
+ print(
121
+ f" - id={s.id} tag={s.tag} algo={s.algo_name} dataset={s.dataset_key} status={getattr(s, 'status', 'n/a')}"
122
+ )
123
+
124
+ def _cmd_create_scenario(self, args: list[str]) -> None:
125
+ if len(args) < 3:
126
+ self.sm.log(
127
+ "Usage: create-scenario <tag> <dataset_key> <algo_name> [json_params]",
128
+ MessageStatus.WARNING,
129
+ )
130
+ return
131
+ tag, dataset_key, algo_name = args[0], args[1], args[2]
132
+ params: dict[str, Any] | None = None
133
+ if len(args) >= 4:
134
+ try:
135
+ params = json.loads(args[3])
136
+ except json.JSONDecodeError:
137
+ self.sm.log("Invalid JSON for parameters.", MessageStatus.ERROR)
138
+ return
139
+ s = self.sm.create_scenario(
140
+ tag=tag, dataset_key=dataset_key, algo_name=algo_name, algo_params=params
141
+ )
142
+ self.sm.log(f"Created scenario id={s.id} tag={s.tag}", MessageStatus.SUCCESS)
143
+
144
+ def _resolve_scenario(self, key: str):
145
+ s = self.sm.get_by_id(key)
146
+ if s is None:
147
+ s = self.sm.get_by_tag(key)
148
+ return s
149
+
150
+ def _cmd_run(self, args: list[str]) -> None:
151
+ if len(args) < 1:
152
+ self.sm.log("Usage: run <scenario_id_or_tag>", MessageStatus.WARNING)
153
+ return
154
+ identifier = args[0]
155
+ s = self._resolve_scenario(identifier)
156
+ if s is None:
157
+ self.sm.log(f"Scenario '{identifier}' not found.", MessageStatus.ERROR)
158
+ return
159
+ self.sm.process_scenario_async(s)
160
+ self.sm.wait_for_processing()
161
+ self.sm.log(f"Scenario '{s.tag}' completed.", MessageStatus.SUCCESS)
162
+
163
+ def _cmd_status(self, _: list[str]) -> None:
164
+ processing = self.sm.currently_processing
165
+ if processing:
166
+ print(
167
+ f" - id={processing.id} tag={processing.tag} status={getattr(processing, 'status', 'processing')}"
168
+ )
169
+ else:
170
+ print("No scenarios processing.")
algomancy_cli/main.py ADDED
@@ -0,0 +1,98 @@
1
+ import argparse
2
+ import importlib
3
+ import os
4
+ import sys
5
+ from typing import Callable
6
+
7
+
8
+ def _ensure_dev_path():
9
+ here = os.path.abspath(os.path.dirname(__file__))
10
+ project_root = os.path.abspath(os.path.join(here, "..", "..", "..", ".."))
11
+ if project_root not in sys.path:
12
+ sys.path.insert(0, project_root)
13
+ try:
14
+ from algomancy_cli.cli_configuration import CliConfiguration # noqa: F401
15
+ from algomancy_cli.cli_launcher import CliLauncher # noqa: F401
16
+
17
+ return
18
+ except Exception:
19
+ pass
20
+
21
+
22
+ _ensure_dev_path()
23
+
24
+ from algomancy_cli.cli_configuration import CliConfiguration # type: ignore # noqa: E402
25
+ from algomancy_cli.cli_launcher import CliLauncher # type: ignore # noqa: E402
26
+
27
+
28
+ def _parse_args():
29
+ parser = argparse.ArgumentParser(description="Algomancy CLI shell")
30
+ parser.add_argument(
31
+ "--config-callback",
32
+ type=str,
33
+ default=None,
34
+ help=(
35
+ "Callback to construct CliConfiguration, in the form 'module:function'. "
36
+ "The function must return a CliConfiguration instance."
37
+ ),
38
+ )
39
+ parser.add_argument(
40
+ "--example",
41
+ action="store_true",
42
+ help="Use the example configuration bundled in this repository (for development).",
43
+ )
44
+ return parser.parse_args()
45
+
46
+
47
+ def _load_config_from_callback(spec: str) -> CliConfiguration:
48
+ if ":" not in spec:
49
+ raise ValueError("--config-callback must be in 'module:function' form")
50
+ module_name, func_name = spec.split(":", 1)
51
+ module = importlib.import_module(module_name)
52
+ func: Callable[[], CliConfiguration] = getattr(module, func_name)
53
+ cfg = func()
54
+ if not isinstance(cfg, CliConfiguration):
55
+ raise TypeError("Config callback did not return CliConfiguration")
56
+ return cfg
57
+
58
+
59
+ def _build_example_config() -> CliConfiguration:
60
+ # These imports rely on this repo's example package being available in the workspace
61
+ from example.data_handling.input_configs import example_input_configs
62
+ from example.data_handling.factories import ExampleETLFactory
63
+ from example.templates import kpi_templates, algorithm_templates
64
+
65
+ from algomancy_data import DataSource
66
+
67
+ return CliConfiguration(
68
+ data_path="example/data",
69
+ has_persistent_state=True,
70
+ etl_factory=ExampleETLFactory,
71
+ kpi_templates=kpi_templates,
72
+ algo_templates=algorithm_templates,
73
+ input_configs=example_input_configs,
74
+ data_object_type=DataSource,
75
+ autocreate=True,
76
+ default_algo="Slow",
77
+ default_algo_params_values={"duration": 1},
78
+ autorun=True,
79
+ title="Algomancy CLI (Example)",
80
+ )
81
+
82
+
83
+ def main():
84
+ args = _parse_args()
85
+ if args.config_callback:
86
+ cfg = _load_config_from_callback(args.config_callback)
87
+ elif args.example:
88
+ cfg = _build_example_config()
89
+ else:
90
+ print("Either pass --config-callback module:function or use --example")
91
+ sys.exit(2)
92
+
93
+ shell = CliLauncher.build(cfg)
94
+ CliLauncher.run(shell)
95
+
96
+
97
+ if __name__ == "__main__":
98
+ main()
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.3
2
+ Name: algomancy-cli
3
+ Version: 0.3.17
4
+ Summary: CLI shell for Algomancy to exercise backend functionality without the GUI.
5
+ Requires-Dist: algomancy-utils
6
+ Requires-Dist: algomancy-scenario
7
+ Requires-Dist: algomancy-data
8
+ Requires-Python: >=3.14
9
+ Description-Content-Type: text/markdown
10
+
11
+ Algomancy CLI
12
+
13
+ Interactive terminal shell to exercise Algomancy backend functionality without the GUI. Useful for rapid development of ETL, algorithms, and scenarios.
14
+
15
+ Install / Run
16
+
17
+ ```
18
+ uv run algomancy-cli --example
19
+ ```
20
+
21
+ Or point to your own configuration factory:
22
+
23
+ ```
24
+ uv run algomancy-cli --config-callback myproject.config:make_config
25
+ ```
26
+
27
+ Where `make_config` returns an `AppConfiguration` instance.
28
+
29
+ Commands
30
+
31
+ - `help` — show help
32
+ - `list-data` / `ld` — list datasets
33
+ - `load-data <name>` — load example data into dataset `<name>`
34
+ - `etl-data <name>` — run ETL to create dataset `<name>`
35
+ - `list-scenarios` / `ls` — list scenarios
36
+ - `create-scenario <tag> <dataset_key> <algo_name> [json_params]` — create scenario
37
+ - `run <scenario_id_or_tag>` — run scenario and wait for completion
38
+ - `status` — show processing status
39
+ - `quit` / `exit` — exit shell
40
+
41
+ Parameters for `create-scenario` can be provided as a JSON object, e.g.:
42
+
43
+ ```
44
+ create-scenario test1 "Master data" Fast "{\"duration\": 0.5}"
45
+ ```
46
+
47
+ How it works
48
+
49
+ The CLI wraps `ScenarioManager` created via `AppConfiguration` just like the GUI launcher. See `src/algomancy/cli_launcher.py` for details.
@@ -0,0 +1,8 @@
1
+ algomancy_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ algomancy_cli/cli_configuration.py,sha256=2skq8tJyJHKTU6HLRzmbK7ULRuSMLWzWtj-tknGgFtg,2088
3
+ algomancy_cli/cli_launcher.py,sha256=ztU9PRcuetpD45ioGgfvmrLd7yEzB3y5gicCWio6970,1156
4
+ algomancy_cli/cli_shell.py,sha256=_KJnf4p9SWbs9B0-zOBxu7n5LGMWO3INcw1ojAypXL0,6372
5
+ algomancy_cli/main.py,sha256=2VlfHENrYfyaxeYlkAJGJKygwYzWMom3Y1Mpl3Eb0ng,3069
6
+ algomancy_cli-0.3.17.dist-info/WHEEL,sha256=fAguSjoiATBe7TNBkJwOjyL1Tt4wwiaQGtNtjRPNMQA,80
7
+ algomancy_cli-0.3.17.dist-info/METADATA,sha256=U-WNZOh1GnPiWp12KWl-01ImXD6KfdOxJ2WwBdxMZJs,1477
8
+ algomancy_cli-0.3.17.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.9.28
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any