human_count.py 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. from typing import Optional
  2. from .features import FEATURES, conv_space
  3. SI_1000_SPEC = ('', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
  4. SI_1024_SPEC = ('', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
  5. IEC_1024_SPEC = ('', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi')
  6. DECIMALS = [1, 1, 1, 2, 2, 2, 2, 2, 2]
  7. def __human_count(val: float, unit: str, prec: Optional[int], space: str, divisor: int,
  8. spec: tuple) -> str:
  9. for scale, dec in zip(spec, DECIMALS):
  10. r = round(val, dec)
  11. if r >= divisor:
  12. val /= divisor
  13. continue
  14. break
  15. else:
  16. r, scale = val, '+'
  17. if prec is not None:
  18. r = round(val, prec)
  19. elif r % 1. == 0.:
  20. prec = 0
  21. elif (r * 10.) % 1. == 0.:
  22. prec = 1
  23. else:
  24. prec = 2
  25. return '{:.{}f}{}{}{}'.format(r, prec, space, scale, unit)
  26. def fn_human_count(space: bool, d1024: bool, iec: bool):
  27. def run(val: float, unit: str, prec: Optional[int]):
  28. return __human_count(val, unit, prec, space, divisor, spec)
  29. space = conv_space(space)
  30. divisor, spec = {
  31. (False, False): (1000, SI_1000_SPEC),
  32. (True, False): (1024, SI_1024_SPEC),
  33. (True, True): (1024, IEC_1024_SPEC),
  34. (False, True): (1024, IEC_1024_SPEC), # invalid combination, which just returns the above.
  35. }[(d1024, iec)]
  36. return run
  37. class HumanCount(object):
  38. def __init__(self, value, unit):
  39. assert value >= 0.
  40. self._value = value
  41. self._unit = unit
  42. @property
  43. def value(self):
  44. return self._value
  45. def unit(self, value: str) -> 'HumanCount':
  46. self._unit = value
  47. return self
  48. def as_human(self, prec: Optional[int] = None) -> str:
  49. """Return a beautiful representation of this count.
  50. It dynamically calculates the best scale to use.
  51. Args:
  52. prec: an optional custom precision
  53. Returns:
  54. the human friendly representation.
  55. """
  56. return fn_human_count(FEATURES.feature_space, FEATURES.feature_1024,
  57. FEATURES.feature_iec)(self._value, self._unit, prec)
  58. def __str__(self):
  59. return self.as_human()
  60. def __repr__(self): # pragma: no cover
  61. return 'HumanCount{{ value={} }} -> {}'.format(self._value, self)
  62. def __eq__(self, other):
  63. return self.__str__() == other