diff --git a/pyproject.toml b/pyproject.toml index 2e9ee6ef..4ccf89e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ packages = [{ include = "robusta_krr" }] [tool.black] line-length = 120 -target-version = ['py39'] +target-version = ['py310', 'py311', 'py312', 'py313'] [tool.isort] line_length = 120 @@ -23,16 +23,19 @@ plugins = "numpy.typing.mypy_plugin,pydantic.mypy" krr = "robusta_krr.main:run" [tool.poetry.dependencies] -python = ">=3.10,<=3.12.9" -typer = { extras = ["all"], version = "^0.7.0" } +python = ">=3.10,<3.14" +typer = { extras = ["all"], version = ">=0.15.0,<1" } pydantic = "^1.10.7" kubernetes = "^26.1.0" prometheus-api-client = "0.5.3" -numpy = ">=1.26.4,<1.27.0" +numpy = [ + {version = ">=2.1.0,<3", python = ">=3.13"}, + {version = ">=1.26.4,<3", python = "<3.13"}, +] alive-progress = "^3.1.2" prometrix = "0.2.11" slack-sdk = "^3.21.3" -pandas = "2.2.2" +pandas = ">=2.2.3,<4" requests = ">2.32.4" pyyaml = "6.0.1" typing-extensions = "4.6.0" diff --git a/robusta_krr/main.py b/robusta_krr/main.py index c9da3009..a8fd4cf7 100644 --- a/robusta_krr/main.py +++ b/robusta_krr/main.py @@ -35,11 +35,12 @@ @app.command(rich_help_panel="Utils") def version() -> None: + """Print the current version of KRR.""" typer.echo(get_version()) def __process_type(_T: type) -> type: - """Process type to a python literal""" + """Process type annotation to a Python literal suitable for CLI parsing.""" if _T in (int, float, str, bool, datetime, UUID): return _T elif _T is Optional: @@ -49,9 +50,11 @@ def __process_type(_T: type) -> type: def load_commands() -> None: + """Dynamically register CLI commands for all available strategies.""" for strategy_name, strategy_type in BaseStrategy.get_all().items(): # type: ignore # NOTE: This wrapper here is needed to avoid the strategy_name being overwritten in the loop def strategy_wrapper(_strategy_name: str = strategy_name): + """Wrap strategy registration to capture the strategy name.""" def run_strategy( ctx: typer.Context, kubeconfig: Optional[str] = typer.Option( @@ -259,7 +262,7 @@ def run_strategy( ), show_severity: bool = typer.Option( True, - " /--exclude-severity", + "--show-severity/--exclude-severity", help="Whether to include the severity in the output or not", rich_help_panel="Output Settings", ), @@ -352,7 +355,7 @@ def run_strategy( ), **strategy_args, ) -> None: - f"""Run KRR using the `{_strategy_name}` strategy""" + """Run KRR using the configured strategy.""" if not show_severity and format != "csv": raise click.BadOptionUsage("--exclude-severity", "--exclude-severity works only with format=csv") @@ -362,8 +365,8 @@ def run_strategy( impersonate_user=impersonate_user, impersonate_group=impersonate_group, clusters="*" if all_clusters else clusters, - namespaces="*" if "*" in namespaces else namespaces, - resources="*" if "*" in resources else resources, + namespaces="*" if namespaces and "*" in namespaces else namespaces, + resources="*" if resources and "*" in resources else resources, selector=selector, prometheus_url=prometheus_url, prometheus_auth_header=prometheus_auth_header, @@ -444,6 +447,7 @@ def run_strategy( def run() -> None: + """Entry point that loads commands and starts the Typer application.""" load_commands() app() diff --git a/tests/test_krr.py b/tests/test_krr.py index 818fbeed..fbcfad73 100644 --- a/tests/test_krr.py +++ b/tests/test_krr.py @@ -7,13 +7,14 @@ from robusta_krr.core.integrations.kubernetes import ClusterLoader from robusta_krr.core.models.config import settings -runner = CliRunner(mix_stderr=False) +runner = CliRunner() load_commands() STRATEGY_NAME = "simple" def test_help(): + """Verify that the help command exits successfully.""" result = runner.invoke(app, [STRATEGY_NAME, "--help"]) try: assert result.exit_code == 0 @@ -23,6 +24,7 @@ def test_help(): @pytest.mark.parametrize("log_flag", ["-v", "-q"]) def test_run(log_flag: str): + """Verify that a simple scan runs successfully with log flags.""" result = runner.invoke(app, [STRATEGY_NAME, log_flag, "--namespace", "default"]) try: assert result.exit_code == 0, result.stdout @@ -33,6 +35,7 @@ def test_run(log_flag: str): @pytest.mark.parametrize("format", ["json", "yaml", "table", "pprint", "csv"]) @pytest.mark.parametrize("output", ["--logtostderr", "-q"]) def test_output_formats(format: str, output: str): + """Verify that all supported output formats produce successful results.""" result = runner.invoke(app, [STRATEGY_NAME, output, "-f", format]) try: assert result.exit_code == 0, result.exc_info @@ -74,6 +77,7 @@ def test_cluster_namespace_list( cluster_all_ns: list[str], expected: Union[Literal["*"], list[str]], ): + """Verify namespace filtering and regex matching for cluster loader.""" cluster = ClusterLoader() with patch("robusta_krr.core.models.config.settings.namespaces", setting_namespaces): with patch.object( diff --git a/tests/test_runner.py b/tests/test_runner.py index dcf4e19b..9cf8f1f9 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -4,7 +4,7 @@ from robusta_krr.main import app, load_commands -runner = CliRunner(mix_stderr=False) +runner = CliRunner() load_commands() @@ -17,5 +17,6 @@ ], ) def test_exclude_severity_option(args: list[str], expected_exit_code: int) -> None: + """Verify that --exclude-severity flag behaves correctly across formats.""" result: Result = runner.invoke(app, ["simple", *args]) assert result.exit_code == expected_exit_code