Coverage for utilities/ordering.py: 65%

35 statements  

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

1import re 

2 

3__all__ = ( 

4 'naturalize', 

5 'naturalize_interface', 

6) 

7 

8INTERFACE_NAME_REGEX = r'(^(?P<type>[^\d\.:]+)?)' \ 

9 r'((?P<slot>\d+)/)?' \ 

10 r'((?P<subslot>\d+)/)?' \ 

11 r'((?P<position>\d+)/)?' \ 

12 r'((?P<subposition>\d+)/)?' \ 

13 r'((?P<id>\d+))?' \ 

14 r'(:(?P<channel>\d+))?' \ 

15 r'(\.(?P<vc>\d+))?' \ 

16 r'(?P<remainder>.*)$' 

17 

18 

19def naturalize(value, max_length, integer_places=8): 

20 """ 

21 Take an alphanumeric string and prepend all integers to `integer_places` places to ensure the strings 

22 are ordered naturally. For example: 

23 

24 site9router21 

25 site10router4 

26 site10router19 

27 

28 becomes: 

29 

30 site00000009router00000021 

31 site00000010router00000004 

32 site00000010router00000019 

33 

34 :param value: The value to be naturalized 

35 :param max_length: The maximum length of the returned string. Characters beyond this length will be stripped. 

36 :param integer_places: The number of places to which each integer will be expanded. (Default: 8) 

37 """ 

38 if not value: 38 ↛ 40line 38 didn't jump to line 40 because the condition on line 38 was always true

39 return value 

40 output = [] 

41 for segment in re.split(r'(\d+)', value): 

42 if segment.isdigit(): 

43 output.append(segment.rjust(integer_places, '0')) 

44 elif segment: 

45 output.append(segment) 

46 ret = ''.join(output) 

47 

48 return ret[:max_length] 

49 

50 

51def naturalize_interface(value, max_length): 

52 """ 

53 Similar in nature to naturalize(), but takes into account a particular naming format adapted from the old 

54 InterfaceManager. 

55 

56 :param value: The value to be naturalized 

57 :param max_length: The maximum length of the returned string. Characters beyond this length will be stripped. 

58 """ 

59 output = '' 

60 match = re.search(INTERFACE_NAME_REGEX, value) 

61 if match is None: 61 ↛ 62line 61 didn't jump to line 62 because the condition on line 61 was never true

62 return value 

63 

64 # First, we order by slot/position, padding each to four digits. If a field is not present, 

65 # set it to 9999 to ensure it is ordered last. 

66 for part_name in ('slot', 'subslot', 'position', 'subposition'): 

67 part = match.group(part_name) 

68 if part is not None: 68 ↛ 69line 68 didn't jump to line 69 because the condition on line 68 was never true

69 output += part.rjust(4, '0') 

70 else: 

71 output += '9999' 

72 

73 # Append the type, if any. 

74 if match.group('type') is not None: 

75 output += match.group('type') 

76 

77 # Append any remaining fields, left-padding to six digits each. 

78 for part_name in ('id', 'channel', 'vc'): 

79 part = match.group(part_name) 

80 if part is not None: 

81 output += part.rjust(6, '0') 

82 else: 

83 output += '......' 

84 

85 # Finally, naturalize any remaining text and append it 

86 if match.group('remainder') is not None and len(output) < max_length: 86 ↛ 90line 86 didn't jump to line 90 because the condition on line 86 was always true

87 remainder = naturalize(match.group('remainder'), max_length - len(output)) 

88 output += remainder 

89 

90 return output[:max_length]