diff --git a/pyproject.toml b/pyproject.toml index 22bba38a..6f3e5e0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,7 +70,6 @@ pretty = true [[tool.mypy.overrides]] module = [ - "cleo._utils", "cleo.ui.table", ] ignore_errors = true diff --git a/src/cleo/_utils.py b/src/cleo/_utils.py index b6835f41..e0960996 100644 --- a/src/cleo/_utils.py +++ b/src/cleo/_utils.py @@ -2,8 +2,8 @@ import math +from dataclasses import dataclass from html.parser import HTMLParser -from typing import Any from rapidfuzz.distance import Levenshtein @@ -13,22 +13,22 @@ def __init__(self) -> None: super().__init__(convert_charrefs=False) self.reset() - self.fed = [] + self.fed: list[str] = [] - def handle_data(self, d) -> None: + def handle_data(self, d: str) -> None: self.fed.append(d) - def handle_entityref(self, name) -> None: + def handle_entityref(self, name: str) -> None: self.fed.append(f"&{name};") - def handle_charref(self, name) -> None: + def handle_charref(self, name: str) -> None: self.fed.append(f"&#{name};") def get_data(self) -> str: return "".join(self.fed) -def _strip(value) -> str: +def _strip(value: str) -> str: s = TagStripper() s.feed(value) s.close() @@ -36,8 +36,7 @@ def _strip(value) -> str: return s.get_data() -def strip_tags(value: Any) -> str: - value = str(value) +def strip_tags(value: str) -> str: while "<" in value and ">" in value: new_value = _strip(value) if value.count("<") == new_value.count("<"): @@ -72,30 +71,37 @@ def find_similar_names(name: str, names: list[str]) -> list[str]: distance_by_name = { k: v for k, v in distance_by_name.items() if v[0] < 2 * threshold } - # Display results with shortest distance first - return sorted(distance_by_name, key=distance_by_name.get) - - -_TIME_FORMATS = [ - (0, "< 1 sec"), - (2, "1 sec"), - (59, "secs", 1), - (60, "1 min"), - (3600, "mins", 60), - (5400, "1 hr"), - (86400, "hrs", 3600), - (129600, "1 day"), - (604800, "days", 86400), + return sorted(distance_by_name, key=lambda x: distance_by_name[x]) + + +@dataclass +class TimeFormat: + threshold: int + alias: str + divisor: int | None = None + + def apply(self, secs: float) -> str: + if self.divisor: + return f"{math.ceil(secs / self.divisor)} {self.alias}" + return self.alias + + +_TIME_FORMATS: list[TimeFormat] = [ + TimeFormat(1, "< 1 sec"), + TimeFormat(2, "1 sec"), + TimeFormat(60, "secs", 1), + TimeFormat(61, "1 min"), + TimeFormat(3600, "mins", 60), + TimeFormat(5401, "1 hr"), + TimeFormat(86400, "hrs", 3600), + TimeFormat(129601, "1 day"), + TimeFormat(604801, "days", 86400), ] def format_time(secs: float) -> str: - for fmt in _TIME_FORMATS: - if secs > fmt[0]: - continue - - if len(fmt) == 2: - return fmt[1] - - return f"{math.ceil(secs / fmt[2])} {fmt[1]}" + format = next( + (fmt for fmt in _TIME_FORMATS if secs < fmt.threshold), _TIME_FORMATS[-1] + ) + return format.apply(secs) diff --git a/tests/test_utils.py b/tests/test_utils.py index 85030773..999e0d6e 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3,6 +3,27 @@ import pytest from cleo._utils import find_similar_names +from cleo._utils import format_time + + +@pytest.mark.parametrize( + ["input_secs", "expected"], + [ + (0.1, "< 1 sec"), + (1.0, "1 sec"), + (2.0, "2 secs"), + (59.0, "59 secs"), + (60.0, "1 min"), + (120.0, "2 mins"), + (3600.0, "1 hr"), + (7200.0, "2 hrs"), + (129600.0, "1 day"), + (129601.0, "2 days"), + (700000.0, "9 days"), + ], +) +def test_format_time(input_secs: float, expected: str) -> None: + assert format_time(input_secs) == expected @pytest.mark.parametrize(