Coverage for utilities/string.py: 21%

36 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 18:35 +0000

1import re 

2 

3__all__ = ( 

4 'enum_key', 

5 'humanize_duration', 

6 'remove_linebreaks', 

7 'title', 

8 'trailing_slash', 

9) 

10 

11 

12def humanize_duration(value): 

13 """ 

14 Express a timedelta in a human-friendly format. Example: 1h 5m 23s. Durations of a second or 

15 more are rounded to whole seconds; shorter durations are rounded to the millisecond (e.g. 

16 0.43s). A negative duration is rendered with a leading minus sign, so that an anomalous value 

17 remains recognizable as one. Returns an empty string for None; zero renders as "0s". 

18 """ 

19 if value is None: 

20 return '' 

21 

22 total_seconds = value.total_seconds() 

23 magnitude = abs(total_seconds) 

24 

25 # Render sub-second durations to the millisecond, as rounding them to whole seconds would 

26 # report every short-lived duration as zero. Trailing zeros are stripped. 

27 if 0 < magnitude < 1: 

28 rendered = f'{magnitude:.3f}'.rstrip('0').rstrip('.') 

29 # A magnitude below a millisecond has no representation here, so fall through to "0s". 

30 # Rounding up to a whole second (e.g. 0.9996) likewise falls through, to "1s". 

31 if rendered not in ('0', '1'): 

32 return f'-{rendered}s' if total_seconds < 0 else f'{rendered}s' 

33 

34 # Round to whole seconds and decompose 

35 days, remainder = divmod(round(magnitude), 86400) 

36 hours, remainder = divmod(remainder, 3600) 

37 minutes, seconds = divmod(remainder, 60) 

38 

39 ret = '' 

40 if days: 

41 ret += f'{days}d ' 

42 if hours: 

43 ret += f'{hours}h ' 

44 if minutes: 

45 ret += f'{minutes}m ' 

46 if seconds or not ret: 

47 ret += f'{seconds}s' 

48 ret = ret.strip() 

49 

50 # Zero carries no sign, however the original value was signed 

51 if total_seconds < 0 and ret != '0s': 

52 ret = f'-{ret}' 

53 return ret 

54 

55 

56def enum_key(value): 

57 """ 

58 Convert the given value to a string suitable for use as an Enum key. 

59 """ 

60 value = str(value).upper() 

61 return re.sub(r'[^_A-Z0-9]', '_', value) 

62 

63 

64def remove_linebreaks(value): 

65 """ 

66 Remove all line breaks from a string and return the result. Useful for log sanitization purposes. 

67 """ 

68 return value.replace('\n', '').replace('\r', '') 

69 

70 

71def title(value): 

72 """ 

73 Improved implementation of str.title(); retains all existing uppercase letters. 

74 """ 

75 return ' '.join([w[0].upper() + w[1:] for w in str(value).split()]) 

76 

77 

78def trailing_slash(value): 

79 """ 

80 Remove a leading slash (if any) and include a trailing slash, except for empty strings. 

81 """ 

82 return f'{value.strip("/")}/' if value else ''