diff --git a/ngraph/__init__.py b/ngraph/__init__.py index e69de29..22d72d2 100644 --- a/ngraph/__init__.py +++ b/ngraph/__init__.py @@ -0,0 +1,7 @@ +"""NetGraph package.""" + +from __future__ import annotations + +from . import cli + +__all__ = ["cli"] diff --git a/ngraph/__main__.py b/ngraph/__main__.py new file mode 100644 index 0000000..fc97e39 --- /dev/null +++ b/ngraph/__main__.py @@ -0,0 +1,6 @@ +from __future__ import annotations + +from .cli import main + +if __name__ == "__main__": + main() diff --git a/ngraph/cli.py b/ngraph/cli.py new file mode 100644 index 0000000..932fe44 --- /dev/null +++ b/ngraph/cli.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +import argparse +import json +from pathlib import Path +from typing import Any, Dict, List, Optional + +from ngraph.scenario import Scenario + + +def _run_scenario(path: Path, output: Optional[Path]) -> None: + """Run a scenario file and store results as JSON.""" + yaml_text = path.read_text() + scenario = Scenario.from_yaml(yaml_text) + scenario.run() + + results_dict: Dict[str, Dict[str, Any]] = scenario.results.to_dict() + json_str = json.dumps(results_dict, indent=2, default=str) + if output: + output.write_text(json_str) + else: + print(json_str) + + +def main(argv: Optional[List[str]] = None) -> None: + """Entry point for the ``ngraph`` command.""" + parser = argparse.ArgumentParser(prog="ngraph") + subparsers = parser.add_subparsers(dest="command", required=True) + + run_parser = subparsers.add_parser("run", help="Run a scenario") + run_parser.add_argument("scenario", type=Path, help="Path to scenario YAML") + run_parser.add_argument( + "--results", + "-r", + type=Path, + default=None, + help="Write JSON results to this file instead of stdout", + ) + + args = parser.parse_args(argv) + + if args.command == "run": + _run_scenario(args.scenario, args.results) + + +if __name__ == "__main__": + main() diff --git a/ngraph/results.py b/ngraph/results.py index 174d815..5422045 100644 --- a/ngraph/results.py +++ b/ngraph/results.py @@ -54,3 +54,7 @@ def get_all(self, key: str) -> Dict[str, Any]: if key in data: result[step_name] = data[key] return result + + def to_dict(self) -> Dict[str, Dict[str, Any]]: + """Return a dictionary representation of all stored results.""" + return {step: data.copy() for step, data in self._store.items()} diff --git a/pyproject.toml b/pyproject.toml index 88212c0..bc22bb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,9 @@ dev = [ "black", "isort", ] +[project.scripts] +ngraph = "ngraph.cli:main" + # --------------------------------------------------------------------- # Pytest flags diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..a6802e1 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,22 @@ +import json +from pathlib import Path + +from ngraph import cli + + +def test_cli_run_file(tmp_path: Path) -> None: + scenario = Path("tests/scenarios/scenario_1.yaml") + out_file = tmp_path / "res.json" + cli.main(["run", str(scenario), "--results", str(out_file)]) + assert out_file.is_file() + data = json.loads(out_file.read_text()) + assert "build_graph" in data + assert "graph" in data["build_graph"] + + +def test_cli_run_stdout(capsys) -> None: + scenario = Path("tests/scenarios/scenario_1.yaml") + cli.main(["run", str(scenario)]) + captured = capsys.readouterr() + data = json.loads(captured.out) + assert "build_graph" in data