/catkin_ws/src/00-infrastructure/easy_node/include/easy_node/user_config/get_configuration_files.py

https://github.com/duckietown/Software · Python · 159 lines · 101 code · 32 blank · 26 comment · 21 complexity · d364e16a4d9d1f9b8e7f4f22ce0f9fff MD5 · raw file

  1. from collections import namedtuple
  2. import os
  3. from frozendict import frozendict
  4. import yaml
  5. from yaml.error import YAMLError
  6. import duckietown_utils as dtu
  7. SUFFIX = '.config.yaml'
  8. ConfigInfo = namedtuple('ConfigInfo',
  9. 'filename package_name node_name config_name date_effective description extends values '
  10. 'valid error_if_invalid ')
  11. def get_configuration_files(package_name, node_name):
  12. """
  13. Gets all the configuration files for easynodes.
  14. These are of the form
  15. package-node.![name].config.yaml
  16. For timing:
  17. package-node.![name].201XMMDD.config.yaml
  18. """
  19. def get_all_configuration_files():
  20. configs = search_all_configuration_files()
  21. results = []
  22. for filename in configs:
  23. c = interpret_config_file(filename)
  24. results.append(c)
  25. return results
  26. def search_all_configuration_files():
  27. sources = []
  28. # We look in $DUCKIETOWN_ROOT/catkin_ws/src
  29. sources.append(dtu.get_catkin_ws_src())
  30. # then we look in $DUCKIETOWN_FLEET
  31. sources.append(dtu.get_duckiefleet_root())
  32. configs = []
  33. pattern = '*' + SUFFIX
  34. dtu.logger.info('Looking for %s files.' % pattern)
  35. for s in sources:
  36. fs = dtu.locate_files(s, pattern)
  37. dtu.logger.info('%4d files in %s' % (len(fs), s))
  38. configs.extend(fs)
  39. # logger.debug('I found:\n' + "\n".join(configs))
  40. return configs
  41. def interpret_config_file(filename):
  42. """
  43. Returns a ConfigInfo.
  44. """
  45. try:
  46. basename = os.path.basename(filename)
  47. base = basename.replace(SUFFIX, '')
  48. # now we have something like
  49. # package-node.config_name.date
  50. # or
  51. # package-node.config_name
  52. if not '.' in base:
  53. msg = 'Invalid filename %r.' % filename
  54. raise dtu.DTConfigException(msg)
  55. tokens = base.split('.')
  56. if len(tokens) > 3:
  57. msg = 'Too many periods/tokens (tokens=%s)' % tokens
  58. raise dtu.DTConfigException(msg)
  59. if len(tokens) <= 2:
  60. # package-node.config_name
  61. package_node = tokens[0]
  62. if not '-' in package_node:
  63. msg = 'Expected a "-" in "%s".' % package_node
  64. raise dtu.DTConfigException(msg)
  65. i = package_node.index('-')
  66. package_name = package_node[:i]
  67. node_name = package_node[i+1:]
  68. config_name = tokens[1]
  69. if len(tokens) == 3:
  70. # package-node.config_name.date
  71. date_effective = tokens[2]
  72. else:
  73. date_effective = '20170101'
  74. from dateutil.parser import parse
  75. try:
  76. date_effective = parse(date_effective)
  77. except:
  78. msg = 'Cannot interpret "%s" as a date.' % date_effective
  79. raise dtu.DTConfigException(msg)
  80. # now read file
  81. contents = open(filename).read()
  82. try:
  83. try:
  84. data = yaml.load(contents)
  85. except YAMLError as e:
  86. dtu.raise_wrapped(dtu.DTConfigException, e, 'Invalid YAML', compact=True)
  87. if not isinstance(data, dict):
  88. msg = 'Expected a dictionary inside.'
  89. raise dtu.DTConfigException(msg)
  90. for field in ['description', 'values']:
  91. if not field in data:
  92. msg = 'Missing field "%s".' % field
  93. raise dtu.DTConfigException(msg)
  94. description = data.pop('description')
  95. if not isinstance(description, str):
  96. msg = 'I expected that "description" is a string, obtained %r.' % description
  97. raise dtu.DTConfigException(msg)
  98. extends = data.pop('extends', [])
  99. if not isinstance(extends, list):
  100. msg = 'I expected that "extends" is a list, obtained %r.' % extends
  101. raise dtu.DTConfigException(msg)
  102. values = data.pop('values')
  103. if not isinstance(values, dict):
  104. msg = 'I expected that "values" is a dictionary, obtained %s.' % type(values)
  105. raise dtu.DTConfigException(msg)
  106. # Freeze the data
  107. extends = tuple(extends)
  108. values = frozendict(values)
  109. except dtu.DTConfigException as e:
  110. msg = 'Could not interpret the contents of the file\n'
  111. msg += ' %s\n' % dtu.friendly_path(filename)
  112. msg += 'Contents:\n' + dtu.indent(contents, ' > ')
  113. dtu.raise_wrapped(dtu.DTConfigException, e, msg, compact=True)
  114. return ConfigInfo(filename=filename, package_name=package_name, node_name=node_name,
  115. config_name=config_name,
  116. date_effective=date_effective,extends=extends,
  117. description=description, values=values,
  118. # not decided
  119. valid=None,
  120. error_if_invalid=None)
  121. except dtu.DTConfigException as e:
  122. msg = 'Invalid file %s' % dtu.friendly_path(filename)
  123. dtu.raise_wrapped(dtu.DTConfigException, e, msg, compact=True)