ordering.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import re
  2. __all__ = (
  3. 'naturalize',
  4. 'naturalize_interface',
  5. )
  6. INTERFACE_NAME_REGEX = r'(^(?P<type>[^\d\.:]+)?)' \
  7. r'((?P<slot>\d+)/)?' \
  8. r'((?P<subslot>\d+)/)?' \
  9. r'((?P<position>\d+)/)?' \
  10. r'((?P<subposition>\d+)/)?' \
  11. r'((?P<id>\d+))?' \
  12. r'(:(?P<channel>\d+))?' \
  13. r'(\.(?P<vc>\d+))?' \
  14. r'(?P<remainder>.*)$'
  15. def naturalize(value, max_length, integer_places=8):
  16. """
  17. Take an alphanumeric string and prepend all integers to `integer_places` places to ensure the strings
  18. are ordered naturally. For example:
  19. site9router21
  20. site10router4
  21. site10router19
  22. becomes:
  23. site00000009router00000021
  24. site00000010router00000004
  25. site00000010router00000019
  26. :param value: The value to be naturalized
  27. :param max_length: The maximum length of the returned string. Characters beyond this length will be stripped.
  28. :param integer_places: The number of places to which each integer will be expanded. (Default: 8)
  29. """
  30. if not value:
  31. return value
  32. output = []
  33. for segment in re.split(r'(\d+)', value):
  34. if segment.isdigit():
  35. output.append(segment.rjust(integer_places, '0'))
  36. elif segment:
  37. output.append(segment)
  38. ret = ''.join(output)
  39. return ret[:max_length]
  40. def naturalize_interface(value, max_length):
  41. """
  42. Similar in nature to naturalize(), but takes into account a particular naming format adapted from the old
  43. InterfaceManager.
  44. :param value: The value to be naturalized
  45. :param max_length: The maximum length of the returned string. Characters beyond this length will be stripped.
  46. """
  47. output = ''
  48. match = re.search(INTERFACE_NAME_REGEX, value)
  49. if match is None:
  50. return value
  51. # First, we order by slot/position, padding each to four digits. If a field is not present,
  52. # set it to 9999 to ensure it is ordered last.
  53. for part_name in ('slot', 'subslot', 'position', 'subposition'):
  54. part = match.group(part_name)
  55. if part is not None:
  56. output += part.rjust(4, '0')
  57. else:
  58. output += '9999'
  59. # Append the type, if any.
  60. if match.group('type') is not None:
  61. output += match.group('type')
  62. # Append any remaining fields, left-padding to six digits each.
  63. for part_name in ('id', 'channel', 'vc'):
  64. part = match.group(part_name)
  65. if part is not None:
  66. output += part.rjust(6, '0')
  67. else:
  68. output += '......'
  69. # Finally, naturalize any remaining text and append it
  70. if match.group('remainder') is not None and len(output) < max_length:
  71. remainder = naturalize(match.group('remainder'), max_length - len(output))
  72. output += remainder
  73. return output[:max_length]