- head_and_tail: shows the first few and last few elements of an iterable that
could potentially be pretty long
- size_str: textual representation of data size
+ - strftimedelta: textual representation of a :py:class:`datetime.timedelta`
Part2 contains functions for coloring text, a poor-man's version of other
modules like :py:mod:`colorclass` (which is now also available on Intra2net
from functools import partial
from itertools import islice
import re
+from datetime import timedelta
from .type_helpers import isstr
from sys import stdout
return f'{sign_str}{int(round(curr_num*factor)):,d} {units[-1]}B'
+def strftimedelta(delta, max_parts=2, only_up_to_hours=False):
+ """
+ Length-adjustable text representation of a timespan.
+
+ Will output the most significant non-zero time units, e.g.
+ '2h23m50s' or '3d50s12ms' (skipping 0 minutes), or '3y63d12h'
+ or for max_parts=2: '2h23m' / '3d50s' / '3y63d'
+
+ Assumes that each year has 365 days, every day 24h, every hour 60 mins, ...
+ not accounting for leap years / leap days, changes of timezones, etc
+
+ :param delta: timedelta or anything that can be converted to float;
+ if numeric, this is interpreted as number of seconds
+ :param int max_parts: precision (i.e., length) of output (default: 2)
+ set to None if always want precision down to microseconds
+ :param bool only_up_to_hours: set to True to never give number of days/years
+ (e.g. to get '40h' instead of '1d16h')
+ default: False
+ :rtype: string
+ """
+ units = '\u00B5s', 'ms', 's', 'm', 'h', 'd', 'y'
+
+ if (max_parts is None) or (max_parts < 1):
+ max_parts = 100
+
+ # get number of seconds and sign
+ if isinstance(delta, timedelta):
+ seconds = delta.total_seconds()
+ else:
+ seconds = float(delta)
+ negative = False
+ if seconds < 0:
+ negative = True
+ seconds = abs(seconds)
+
+ # split sub-second part
+ seconds, second_part = divmod(seconds, 1)
+ milliseconds, milli_part = divmod(second_part*1000, 1)
+ microseconds = int(round(milli_part*1000))
+ seconds = int(seconds) # convert to int here --> all other variables will be int
+ vals = [microseconds, int(milliseconds), ]
+
+ # split longer units
+ minutes = hours = days = years = 0
+ if seconds > 0:
+ minutes, seconds = divmod(seconds, 60)
+ if minutes > 0:
+ hours, minutes = divmod(minutes, 60)
+ if hours > 0 and not only_up_to_hours:
+ days, hours = divmod(hours, 24)
+ if days > 0:
+ years, days = divmod(days, 365)
+ vals.append(seconds)
+ vals.append(minutes)
+ vals.append(hours)
+ vals.append(days)
+ vals.append(years)
+
+ # build string
+ str_parts = []
+ if negative:
+ str_parts.append('-')
+
+ n_used = 0
+ for val,unit in zip( reversed(vals), reversed(units) ):
+ if val == 0:
+ continue
+ str_parts.append(str(val))
+ str_parts.append(unit)
+ n_used += 1
+ if n_used >= max_parts:
+ break
+ return ''.join(str_parts)
+
+
+
###############################################################################
# TEXT FORMATTING/COLORING
###############################################################################
"""
import unittest
+from datetime import timedelta
from src.text_helpers import *
self.assertEqual(size_str(5.678 * 1024 * 1024 * 1024 * 1024), '5.7 TB')
self.assertEqual(size_str(56.78 * 1024 * 1024 * 1024 * 1024), '57 TB')
+ def test_strftimedelta(self):
+ """Test function strftimedelta"""
+ self.assertEqual(strftimedelta(0.0001), "100µs")
+ self.assertEqual(strftimedelta(0.1), "100ms")
+ self.assertEqual(strftimedelta(1), "1s")
+ self.assertEqual(strftimedelta(1.1), "1s100ms")
+ self.assertEqual(strftimedelta(100), "1m40s")
+ self.assertEqual(strftimedelta(3600), "1h")
+ self.assertEqual(strftimedelta(3601), "1h1s")
+ self.assertEqual(strftimedelta(3661), "1h1m")
+ self.assertEqual(strftimedelta(3661, max_parts=3), "1h1m1s")
+ self.assertEqual(strftimedelta(timedelta(days=1)), "1d")
+ self.assertEqual(strftimedelta(timedelta(days=1), only_up_to_hours=True), "24h")
+ self.assertEqual(strftimedelta(timedelta(days=1, seconds=1)), "1d1s")
+ self.assertEqual(strftimedelta(timedelta(days=1, seconds=3600)), "1d1h")
+ self.assertEqual(strftimedelta(timedelta(days=1, seconds=3661)), "1d1h")
+ self.assertEqual(strftimedelta(timedelta(days=1, seconds=3661), max_parts=4), "1d1h1m1s")
+ self.assertEqual(strftimedelta(timedelta(days=5), only_up_to_hours=True), "120h")
+ self.assertEqual(strftimedelta(timedelta(days=90)), "90d")
+ self.assertEqual(strftimedelta(timedelta(days=365)), "1y")
+ self.assertEqual(strftimedelta(timedelta(days=3650)), "10y")
+
if __name__ == '__main__':
unittest.main()