From 5eb9cfe1c6f0283b7421f1a8cc681b6995c30e2d Mon Sep 17 00:00:00 2001 From: Adar Nimrod <nimrod@shore.co.il> Date: Sat, 31 Jul 2021 20:36:31 +0300 Subject: [PATCH] Replace the PerfData dataclass with a named tuple. - Works on Python 3.6. - The upsides of dataclass don't help in this case. - Is faster and uses less memory (although in this case it would hard to even measure the difference). --- .gitlab-ci.yml | 1 - Makefile | 2 +- mnpw/nagios.py | 125 +++++++++++++++++++++---------------------- pyproject.toml | 1 - tests/test_nagios.py | 2 +- 5 files changed, 63 insertions(+), 68 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1249888..b815f5b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,7 +20,6 @@ build-executable: extends: .python3-build before_script: - poetry install --no-dev - - poetry run pip freeze script: - poetry run make artifacts: diff --git a/Makefile b/Makefile index f348e25..8f762c1 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ all: dist/mnpw dist/mnpw: mnpw/*.py pyproject.toml *.rst LICENSE.txt - pyinstaller --noconfirm --onefile "--name=$$(basename $@)" --hidden-import dataclasses mnpw/__init__.py + pyinstaller --noconfirm --onefile "--name=$$(basename $@)" mnpw/__init__.py .PHONY: clean clean: diff --git a/mnpw/nagios.py b/mnpw/nagios.py index c1e33c4..30f0edc 100644 --- a/mnpw/nagios.py +++ b/mnpw/nagios.py @@ -5,10 +5,10 @@ https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/3/en/pluginapi.ht """ # pylint: disable=logging-fstring-interpolation +import collections import enum import logging import subprocess # nosec -from dataclasses import dataclass DEFAULT_TIMEOUT = 10 # In seconds. @@ -22,66 +22,63 @@ class NagiosCode(enum.IntEnum): UNKNOWN = 3 -@dataclass() -class PerfData: - """Performance data. - - As published by a Nagios plugin. - """ - - name: str - value: float - unit: str = "" - warning: float = None - critical: float = None - min: float = None - max: float = None - - def __init__(self, line): # noqa: MC0001 - """Initialize using a perf data string output from a plugin.""" - if len(line.splitlines()) > 1: - raise RuntimeError("Can only parse a single line at a time.") - fields = line.split(";") - self.name, self.value = fields[0].split("=") - - # Clean the name, handle a quoted name. - self.name = self.name.strip() - if self.name[0] in ["'", '"']: - self.name = self.name[1:] - if self.name[-1] in ["'", '"']: - self.name = self.name[:-1] - self.name = self.name.strip() - - # Split the unit and value. - if not self.value[-1].isdigit(): - for i in range(len(self.value) - 1, 0, -1): - if self.value[i].isdigit(): - break - self.value, self.unit = ( - float(self.value[: i + 1]), - self.value[i + 1 :], # noqa: E203 - ) - - # Get the remaining fields, if available. - try: - self.warning = float(fields[1]) - except Exception as ex: # pylint: disable=broad-except - logging.info(str(ex)) - - try: - self.critical = float(fields[2]) - except Exception as ex: # pylint: disable=broad-except - logging.info(str(ex)) - - try: - self.min = float(fields[3]) - except Exception as ex: # pylint: disable=broad-except - logging.info(str(ex)) - - try: - self.max = float(fields[4]) - except Exception as ex: # pylint: disable=broad-except - logging.info(str(ex)) +PerfData = collections.namedtuple( + "PerfData", ["name", "value", "unit", "warning", "critical", "min", "max"] +) +PerfData.__new__.__defaults__ = tuple([None] * 7) + + +def parse_perf_data(line): # noqa: MC0001 + """Convert a single-line string to a DataPerf tuple.""" + if len(line.splitlines()) > 1: + raise RuntimeError("Can only parse a single line at a time.") + + fields = line.split(";") + name, value = fields[0].split("=") + + # Clean the name, handle a quoted name. + name = name.strip() + if name[0] in ["'", '"']: + name = name[1:] + if name[-1] in ["'", '"']: + name = name[:-1] + name = name.strip() + pd = {"name": name} # pylint: disable=invalid-name + + # Split the unit and value. + if not value[-1].isdigit(): + for i in range(len(value) - 1, 0, -1): + if value[i].isdigit(): + break + value, unit = ( + float(value[: i + 1]), + value[i + 1 :], # noqa: E203 + ) + pd["value"] = value + pd["unit"] = unit + + # Get the remaining fields, if available. + try: + pd["warning"] = float(fields[1]) + except Exception as ex: # pylint: disable=broad-except + logging.info(str(ex)) + + try: + pd["critical"] = float(fields[2]) + except Exception as ex: # pylint: disable=broad-except + logging.info(str(ex)) + + try: + pd["min"] = float(fields[3]) + except Exception as ex: # pylint: disable=broad-except + logging.info(str(ex)) + + try: + pd["max"] = float(fields[4]) + except Exception as ex: # pylint: disable=broad-except + logging.info(str(ex)) + + return PerfData(**pd) class Check: @@ -121,7 +118,7 @@ class Check: if "|" in lines[0]: p1, p2 = lines[0].split("|") self.Output = p1.strip() - self.PerfData.append(PerfData(p2)) + self.PerfData.append(parse_perf_data(p2)) else: self.Output = lines[0].strip() @@ -131,12 +128,12 @@ class Check: processing_perfdata = False for line in lines[1:]: if processing_perfdata: - self.PerfData.append(PerfData(line)) + self.PerfData.append(parse_perf_data(line)) else: if "|" in line: self.AdditionalOutput.append(line.split("|")[0].strip()) processing_perfdata = True - self.PerfData.append(PerfData(line.split("|")[1])) + self.PerfData.append(parse_perf_data(line.split("|")[1])) else: self.AdditionalOutput.append(line) diff --git a/pyproject.toml b/pyproject.toml index 1118361..4e12108 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,6 @@ exclude = [ [tool.poetry.dependencies] python = "^3.6.1" requests = "^2.25.1" -dataclasses = { version = '^0.8', python = '<3.7'} [tool.poetry.dev-dependencies] pre-commit = "^2.13.0" diff --git a/tests/test_nagios.py b/tests/test_nagios.py index 49b47ff..e44f2fc 100644 --- a/tests/test_nagios.py +++ b/tests/test_nagios.py @@ -114,7 +114,7 @@ def test_output_parsing(output): @pytest.mark.parametrize("line", PERF_DATA) def test_data_perf(line): """Test parsing of perfdata.""" - pd = nagios.PerfData(line) + pd = nagios.parse_perf_data(line) assert isinstance(pd.name, str) assert len(pd.name) > 0 assert isinstance(pd.value, float) -- GitLab