diff --git a/cmk/base/check_api.py b/cmk/base/check_api.py index 300dd9c282c..9cc4bb3ea87 100644 --- a/cmk/base/check_api.py +++ b/cmk/base/check_api.py @@ -37,13 +37,13 @@ from cmk.checkengine.submitters import ServiceDetails, ServiceState import cmk.base.config as _config -from cmk.base.api.agent_based import render as _render # pylint: disable=unused-import from cmk.base.api.agent_based.register.utils_legacy import ( LegacyCheckDefinition as LegacyCheckDefinition, ) +from cmk.agent_based.v1 import render as _render from cmk.agent_based.v1_backend.plugin_contexts import ( host_name as host_name, # pylint: disable=unused-import ) diff --git a/cmk/base/plugins/agent_based/agent_based_api/v1/render.py b/cmk/base/plugins/agent_based/agent_based_api/v1/render.py index ee8c4bd7957..1ccd6facefb 100644 --- a/cmk/base/plugins/agent_based/agent_based_api/v1/render.py +++ b/cmk/base/plugins/agent_based/agent_based_api/v1/render.py @@ -2,13 +2,8 @@ # Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2 # This file is part of Checkmk (https://checkmk.com). It is subject to the terms and # conditions defined in the file COPYING, which is part of this source code package. -""" -The "render" namespace adds functions to render values in a human readable way. -All of the render functions take a single numerical value as an argument, and return -a string. -""" -from cmk.base.api.agent_based.render import ( # pylint: disable=redefined-builtin +from cmk.agent_based.v1.render import ( # pylint: disable=redefined-builtin bytes, date, datetime, @@ -23,15 +18,15 @@ ) __all__ = [ + "bytes", "date", "datetime", - "timespan", "disksize", - "bytes", "filesize", "frequency", + "iobandwidth", "networkbandwidth", "nicspeed", - "iobandwidth", "percent", + "timespan", ] diff --git a/packages/cmk-agent-based/cmk/agent_based/v1/__init__.py b/packages/cmk-agent-based/cmk/agent_based/v1/__init__.py index 775fe43711a..9911fb04b7b 100644 --- a/packages/cmk-agent-based/cmk/agent_based/v1/__init__.py +++ b/packages/cmk-agent-based/cmk/agent_based/v1/__init__.py @@ -3,6 +3,7 @@ # This file is part of Checkmk (https://checkmk.com). It is subject to the terms and # conditions defined in the file COPYING, which is part of this source code package. +from . import render from ._checking_classes import ( CheckResult, DiscoveryResult, @@ -27,6 +28,7 @@ "ServiceLabel", "State", "regex", + "render", "CheckResult", "DiscoveryResult", ] diff --git a/cmk/base/api/agent_based/render.py b/packages/cmk-agent-based/cmk/agent_based/v1/render.py similarity index 77% rename from cmk/base/api/agent_based/render.py rename to packages/cmk-agent-based/cmk/agent_based/v1/render.py index 688e8630fd0..e2dccc7ec7b 100644 --- a/cmk/base/api/agent_based/render.py +++ b/packages/cmk-agent-based/cmk/agent_based/v1/render.py @@ -2,13 +2,15 @@ # Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2 # This file is part of Checkmk (https://checkmk.com). It is subject to the terms and # conditions defined in the file COPYING, which is part of this source code package. -"""Render functions for check development +""" +The "render" namespace adds functions to render values in a human readable way. -These are meant to be exposed in the API +All of the render functions take a single numerical value as an argument, and return +a string. """ -import math -import time -from collections.abc import Iterable +import math as _math +import time as _time +from collections.abc import Iterable as _Iterable _DATE_FORMAT = "%b %d %Y" @@ -48,7 +50,7 @@ def date(epoch: float | None) -> str: """ if epoch is None: return "never" - return time.strftime(_DATE_FORMAT, time.localtime(float(epoch))) + return _time.strftime(_DATE_FORMAT, _time.localtime(float(epoch))) def datetime(epoch: float | None) -> str: @@ -65,10 +67,10 @@ def datetime(epoch: float | None) -> str: """ if epoch is None: return "never" - return time.strftime("%s %%H:%%M:%%S" % _DATE_FORMAT, time.localtime(float(epoch))) + return _time.strftime(f"{_DATE_FORMAT} %H:%M:%S", _time.localtime(float(epoch))) -def _gen_timespan_chunks(seconds: float, nchunks: int) -> Iterable[str]: +def _gen_timespan_chunks(seconds: float, nchunks: int) -> _Iterable[str]: if seconds < 0: raise ValueError("Cannot render negative timespan") @@ -79,7 +81,7 @@ def _gen_timespan_chunks(seconds: float, nchunks: int) -> Iterable[str]: for unit, scale in _TIME_UNITS[start : start + nchunks]: last_chunk = unit.endswith("seconds") - value = (round if last_chunk else int)(seconds / scale) # type: ignore[operator] + value = round(seconds / scale) if last_chunk else int(seconds / scale) yield f"{value:.0f} {unit if value != 1 else unit[:-1]}" if last_chunk: break @@ -105,7 +107,7 @@ def timespan(seconds: float) -> str: """ ts = " ".join(_gen_timespan_chunks(float(seconds), nchunks=2)) - if ts == "0 %s" % _TIME_UNITS[-1][0]: + if ts == f"0 {_TIME_UNITS[-1][0]}": ts = "0 seconds" return ts @@ -119,21 +121,21 @@ def _digits_left(value: float) -> int: """ try: - return max(int(math.log10(abs(value)) + 1), 1) + return max(int(_math.log10(abs(value)) + 1), 1) except ValueError: return 1 def _auto_scale(value: float, use_si_units: bool, add_bytes_prefix: bool = True) -> tuple[str, str]: if use_si_units: - base = 1000 + base = 1000.0 size_prefixes = _SIZE_PREFIXES_SI else: - base = 1024 + base = 1024.0 size_prefixes = _SIZE_PREFIXES_IEC try: - log_value = int(math.log(abs(value), base)) + log_value = int(_math.log(abs(value), base)) except ValueError: log_value = 0 @@ -142,7 +144,7 @@ def _auto_scale(value: float, use_si_units: bool, add_bytes_prefix: bool = True) if add_bytes_prefix: unit = (unit + ("B" if use_si_units else "iB")).lstrip("i") scaled_value = float(value) / base**exponent - fmt = "%%.%df" % max(3 - _digits_left(scaled_value), 0) + fmt = f"%.{max(3 - _digits_left(scaled_value), 0)}f" return fmt % scaled_value, unit @@ -153,7 +155,8 @@ def frequency(hertz: float) -> str: >>> frequency(1e10 / 3.) '3.33 GHz' """ - return "%s %sHz" % _auto_scale(float(hertz), use_si_units=True, add_bytes_prefix=False) + value_str, unit = _auto_scale(float(hertz), use_si_units=True, add_bytes_prefix=False) + return f"{value_str} {unit}Hz" def disksize(bytes_: float) -> str: @@ -164,7 +167,7 @@ def disksize(bytes_: float) -> str: '1.02 kB' """ value_str, unit = _auto_scale(float(bytes_), use_si_units=True) - return "{} {}".format(value_str if unit != "B" else value_str.split(".")[0], unit) + return f"{value_str if unit != 'B' else value_str.split('.')[0]} {unit}" def bytes(bytes_: float) -> str: # pylint: disable=redefined-builtin @@ -175,7 +178,7 @@ def bytes(bytes_: float) -> str: # pylint: disable=redefined-builtin '1.00 MiB' """ value_str, unit = _auto_scale(float(bytes_), use_si_units=False) - return "{} {}".format(value_str if unit != "B" else value_str.split(".")[0], unit) + return f"{value_str if unit != 'B' else value_str.split('.')[0]} {unit}" def filesize(bytes_: float) -> str: @@ -185,16 +188,17 @@ def filesize(bytes_: float) -> str: >>> filesize(12345678) '12,345,678 B' """ - val_str = "%.0f" % float(bytes_) + val_str = f"{float(bytes_):.0f}" offset = len(val_str) % 3 groups = [val_str[0:offset]] + [val_str[i : i + 3] for i in range(offset, len(val_str), 3)] - return "%s B" % ",".join(groups).strip(",") + return f"{','.join(groups).strip(',')} B" def networkbandwidth(octets_per_sec: float) -> str: """Render network bandwidth using an appropriate SI prefix""" - return "%s %sit/s" % _auto_scale(float(octets_per_sec) * 8, use_si_units=True) + value_str, unit = _auto_scale(float(octets_per_sec) * 8, use_si_units=True) + return f"{value_str} {unit}it/s" def nicspeed(octets_per_sec: float) -> str: @@ -219,7 +223,8 @@ def iobandwidth(bytes_: float) -> str: '128 B/s' """ - return "%s %s/s" % _auto_scale(float(bytes_), use_si_units=True) + value_str, unit = _auto_scale(float(bytes_), use_si_units=True) + return f"{value_str} {unit}/s" def percent(percentage: float) -> str: @@ -246,3 +251,18 @@ def percent(percentage: float) -> str: # this includes negative values! return f"{value:.2f}%" + + +__all__ = [ + "bytes", + "date", + "datetime", + "disksize", + "filesize", + "frequency", + "iobandwidth", + "networkbandwidth", + "nicspeed", + "percent", + "timespan", +] diff --git a/tests/unit/cmk/base/api/agent_based/test_render_api.py b/packages/cmk-agent-based/tests/cmk/agent_based/v1/test_render_api.py similarity index 90% rename from tests/unit/cmk/base/api/agent_based/test_render_api.py rename to packages/cmk-agent-based/tests/cmk/agent_based/v1/test_render_api.py index 22dd6c35cc6..f0d3492a3e0 100644 --- a/tests/unit/cmk/base/api/agent_based/test_render_api.py +++ b/packages/cmk-agent-based/tests/cmk/agent_based/v1/test_render_api.py @@ -3,12 +3,11 @@ # This file is part of Checkmk (https://checkmk.com). It is subject to the terms and # conditions defined in the file COPYING, which is part of this source code package. +import time import pytest -from tests.testlib import set_timezone - -import cmk.base.api.agent_based.render as render +from cmk.agent_based.v1 import render @pytest.mark.parametrize( @@ -19,9 +18,9 @@ (1587908220.0, "Apr 26 2020"), ], ) -def test_date(epoch: float | None, output: str) -> None: - with set_timezone("UTC"): - assert output == render.date(epoch=epoch) +def test_date(epoch: float | None, output: str, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(time, "localtime", time.gmtime) + assert output == render.date(epoch=epoch) @pytest.mark.parametrize( @@ -32,9 +31,9 @@ def test_date(epoch: float | None, output: str) -> None: (1587908220.0, "Apr 26 2020 13:37:00"), ], ) -def test_datetime(epoch: float | None, output: str) -> None: - with set_timezone("UTC"): - assert output == render.datetime(epoch=epoch) +def test_datetime(epoch: float | None, output: str, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(time, "localtime", time.gmtime) + assert output == render.datetime(epoch=epoch) @pytest.mark.parametrize( @@ -72,7 +71,7 @@ def test_timespan_negative() -> None: ], ) def test__digits_left(value: float, output: int) -> None: - assert output == render._digits_left(value) + assert output == render._digits_left(value) # pylint: disable=protected-access @pytest.mark.parametrize( @@ -90,7 +89,7 @@ def test__digits_left(value: float, output: int) -> None: ], ) def test__auto_scale(value: float, use_si_units: bool, output: tuple[str, str]) -> None: - assert output == render._auto_scale(value, use_si_units) + assert output == render._auto_scale(value, use_si_units) # pylint: disable=protected-access @pytest.mark.parametrize( diff --git a/tests/unit/cmk/base/api/agent_based/test_utils_check_levels.py b/tests/unit/cmk/base/api/agent_based/test_utils_check_levels.py index bd1931ea529..dfa86166d3d 100644 --- a/tests/unit/cmk/base/api/agent_based/test_utils_check_levels.py +++ b/tests/unit/cmk/base/api/agent_based/test_utils_check_levels.py @@ -9,9 +9,9 @@ import pytest -from cmk.base.api.agent_based import render, utils +from cmk.base.api.agent_based import utils -from cmk.agent_based.v1 import Metric, Result, State +from cmk.agent_based.v1 import Metric, render, Result, State @pytest.mark.parametrize( diff --git a/tests/unit/cmk/base/plugins/agent_based/test_agent_based_api_namespace_v1.py b/tests/unit/cmk/base/plugins/agent_based/test_agent_based_api_namespace_v1.py index c43121ca6a8..a951272e487 100644 --- a/tests/unit/cmk/base/plugins/agent_based/test_agent_based_api_namespace_v1.py +++ b/tests/unit/cmk/base/plugins/agent_based/test_agent_based_api_namespace_v1.py @@ -12,15 +12,19 @@ | light heartedly! | +---------------------------------------------------------+ """ +from types import ModuleType + from cmk.base.plugins.agent_based.agent_based_api import v1 +from cmk.agent_based import v1 as v1_new_location + -def _names(space): - return sorted(n for n in dir(space) if not n.startswith("_")) +def _names(space: ModuleType) -> set[str]: + return {n for n in dir(space) if not n.startswith("_")} def test_v1() -> None: - if _names(v1) != [ + expected = { "Attributes", "CheckResult", "GetRateError", @@ -61,13 +65,14 @@ def test_v1() -> None: "render", "startswith", "type_defs", - ]: + } + if _names(v1) != expected: # TODO or _names(v1_new_location) != expected: # do not output actual names. Changing this is meant to hurt! raise AssertionError(__doc__) def test_v1_render() -> None: - if _names(v1.render) != [ + expected = { "bytes", "date", "datetime", @@ -79,33 +84,37 @@ def test_v1_render() -> None: "nicspeed", "percent", "timespan", - ]: - raise AssertionError(__doc__) + } + assert _names(v1.render) == expected + assert _names(v1_new_location.render) == expected def test_v1_type_defs() -> None: - if _names(v1.type_defs) != [ + expected = { "CheckResult", "DiscoveryResult", "HostLabelGenerator", "InventoryResult", "StringByteTable", "StringTable", - ]: + } + if _names(v1.type_defs) != expected: # TODO or _names(v1_new_location.type_defs) != expected: raise AssertionError(__doc__) def test_v1_register() -> None: - if _names(v1.register) != [ + expected = { "RuleSetType", "agent_section", "check_plugin", "inventory_plugin", "snmp_section", - ]: + } + if _names(v1.register) != expected: # TODO or _names(v1_new_location.register) != expected: raise AssertionError(__doc__) def test_v1_clusterize() -> None: - if _names(v1.clusterize) != ["make_node_notice_results"]: + expected = {"make_node_notice_results"} + if _names(v1.clusterize) != expected: # TODO or _names(v1_new_location.clusterize) != expected: raise AssertionError(__doc__)