From 8a2e245b41dcfeb8ccd7492e07bea09613267d64 Mon Sep 17 00:00:00 2001 From: wwade Date: Wed, 3 Apr 2024 14:59:12 -0700 Subject: [PATCH 1/2] utils: clean up pyright errors in utils.py - logging debug() takes an exc_info bool. - chardet.detect() can return None, so handle that case. --- jobrunner/utils.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/jobrunner/utils.py b/jobrunner/utils.py index e7f3738..349c38e 100644 --- a/jobrunner/utils.py +++ b/jobrunner/utils.py @@ -38,7 +38,7 @@ def strForEach(value): try: return text_type(value) except (UnicodeDecodeError, UnicodeEncodeError): - LOG.debug("%r", value, exc_info=1) + LOG.debug("%r", value, exc_info=True) return f"{value!r}" @@ -47,12 +47,12 @@ def sprint(*args, **kwargs): try: print(*list(map(strForEach, args)), **kwargs) except IOError: - LOG.debug("sprint ignore IOError", exc_info=1) + LOG.debug("sprint ignore IOError", exc_info=True) except (UnicodeEncodeError, UnicodeDecodeError): print("codec error", repr(args)) - LOG.debug("%r", args, exc_info=1) + LOG.debug("%r", args, exc_info=True) except BaseException: - LOG.debug("sprint caught error", exc_info=1) + LOG.debug("sprint caught error", exc_info=True) raise @@ -90,7 +90,7 @@ def lockedSection(jobs): try: yield except BaseException: - LOG.debug("lockedSection exception", exc_info=1) + LOG.debug("lockedSection exception", exc_info=True) raise finally: jobs.unlock() @@ -353,11 +353,15 @@ def sudoKillProcGroup(pgrp): return None -def autoDecode(byteArray): +def autoDecode(byteArray: bytes) -> str: detected = chardet.detect(byteArray) + if not detected: + return byteArray.decode() + encoding = detected["encoding"] if detected["confidence"] < 0.8: # very arbitrary LOG.debug("char encoding below confidence level 0.8 (%r). " "Fall back to UTF-8.", detected) encoding = "utf-8" + return byteArray.decode(encoding) From 9a7b20e3d104d6ef57946697105a142c2adfad4b Mon Sep 17 00:00:00 2001 From: wwade Date: Wed, 3 Apr 2024 15:00:05 -0700 Subject: [PATCH 2/2] db: fix display of "-a" (last pass/fail) output It was using float division instead of integer, and so was always showing zeroes for time deltas less than 1 hour. --- jobrunner/db/__init__.py | 8 ++------ jobrunner/test/utils_test.py | 30 +++++++++++++++++++++++++++++- jobrunner/utils.py | 8 ++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/jobrunner/db/__init__.py b/jobrunner/db/__init__.py index ac8141b..df52e5c 100644 --- a/jobrunner/db/__init__.py +++ b/jobrunner/db/__init__.py @@ -27,6 +27,7 @@ dateTimeFromJson, dateTimeToJson, doMsg, + humanTimeDeltaSecs, pidDebug, safeSleep, sprint, @@ -771,12 +772,7 @@ def _byAge(refA, refB): for res in ["pass", "fail"]: if wkspace in perWs and res in perWs[wkspace]: j = perWs[wkspace][res] - sec = int((unow - j.stopTime).total_seconds()) - tmHour = sec / (60 * 60) - sec -= tmHour * 60 * 60 - tmMin = sec / 60 - sec -= tmMin * 60 - diffTime = "%d:%02d:%02d" % (tmHour, tmMin, sec) + diffTime = humanTimeDeltaSecs(unow, j.stopTime) sprint( " last %s, \033[97m%s\033[0m ago" % (res, diffTime)) diff --git a/jobrunner/test/utils_test.py b/jobrunner/test/utils_test.py index 148a489..cfc0446 100644 --- a/jobrunner/test/utils_test.py +++ b/jobrunner/test/utils_test.py @@ -4,9 +4,12 @@ from __future__ import absolute_import, division, print_function +from dataclasses import dataclass +from datetime import datetime, timedelta + import pytest -from jobrunner.utils import autoDecode +from jobrunner.utils import autoDecode, humanTimeDeltaSecs @pytest.mark.parametrize(("value", "encoding"), [ @@ -16,3 +19,28 @@ ]) def testAutoDecode(value, encoding): assert value.decode(encoding) == autoDecode(value) + + +@dataclass(frozen=True) +class HTDCase: + delta: timedelta + expected: str + + +@pytest.mark.parametrize("tc", [ + HTDCase(timedelta(), "0:00:00"), + HTDCase(timedelta(seconds=1), "0:00:01"), + HTDCase(timedelta(seconds=59), "0:00:59"), + HTDCase(timedelta(minutes=1), "0:01:00"), + HTDCase(timedelta(minutes=59), "0:59:00"), + HTDCase(timedelta(hours=1), "1:00:00"), + HTDCase(timedelta(hours=23), "23:00:00"), + HTDCase(timedelta(days=6), "6 days, 0:00:00"), + HTDCase(timedelta(days=4, hours=3, minutes=2, seconds=1), "4 days, 3:02:01"), + HTDCase(timedelta(milliseconds=900), "0:00:01"), +]) +def testHumanTimeDeltaSecs(tc: HTDCase) -> None: + b = datetime.now() + a = b + tc.delta + actual = humanTimeDeltaSecs(a, b) + assert tc.expected == actual diff --git a/jobrunner/utils.py b/jobrunner/utils.py index 349c38e..f4de6a0 100644 --- a/jobrunner/utils.py +++ b/jobrunner/utils.py @@ -365,3 +365,11 @@ def autoDecode(byteArray: bytes) -> str: encoding = "utf-8" return byteArray.decode(encoding) + + +def humanTimeDeltaSecs(a: datetime.datetime, b: datetime.datetime) -> str: + """ + Returns a human readable string for the time difference a - b. + """ + seconds = round((a - b).total_seconds(), 0) + return f"{datetime.timedelta(seconds=seconds)}"