generator.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. # Copyright 2022 The Chromium Authors. All rights reserved.
  2. # Use of this source code is governed by a BSD-style license that can be
  3. # found in the LICENSE file.
  4. import collections
  5. import importlib.util
  6. import os
  7. import re
  8. import sys
  9. from typing import Dict, List
  10. from json_data_generator.util import (GetDirNameFromPath, GetFileNameFromPath,
  11. GetFileNameWithoutExtensionFromPath,
  12. JoinPath)
  13. _FILE_PATH = os.path.dirname(os.path.realpath(__file__))
  14. _JSON5_PATH = os.path.join(_FILE_PATH, os.pardir, os.pardir, 'third_party',
  15. 'pyjson5', 'src')
  16. sys.path.insert(1, _JSON5_PATH)
  17. import json5
  18. _JINJA2_PATH = os.path.join(_FILE_PATH, os.pardir, os.pardir, 'third_party')
  19. sys.path.insert(1, _JINJA2_PATH)
  20. import jinja2
  21. class JSONDataGenerator(object):
  22. '''A generic json data generator.'''
  23. def __init__(self, out_dir: str):
  24. self.out_dir = out_dir
  25. self.model: Dict = {}
  26. # Store all json sources used in the generator.
  27. self.sources: List[str] = list()
  28. def AddJSONFilesToModel(self, paths: List[str]):
  29. '''Adds one or more JSON files to the model.'''
  30. for path in paths:
  31. try:
  32. with open(path, 'r') as f:
  33. self.AddJSONToModel(path, f.read())
  34. self.sources.append(path)
  35. except ValueError as err:
  36. raise ValueError('\n%s:\n %s' % (path, err))
  37. def AddJSONToModel(self, json_path: str, json_string: str):
  38. '''Adds a |json_string| with data to the model.
  39. Every json file is added to |self.model| with the original file
  40. name as the key.
  41. '''
  42. data = json5.loads(json_string,
  43. object_pairs_hook=collections.OrderedDict)
  44. # Use the json file name as the key of the loaded json data.
  45. key = GetFileNameWithoutExtensionFromPath(json_path)
  46. self.model[key] = data
  47. def GetGlobals(self, template_path: str):
  48. file_name_without_ext = GetFileNameWithoutExtensionFromPath(
  49. template_path)
  50. out_file_path = JoinPath(self.out_dir, file_name_without_ext)
  51. return {
  52. 'model': self.model,
  53. 'source_json_files': self.sources,
  54. 'out_file_path': out_file_path,
  55. }
  56. def GetFilters(self):
  57. return {
  58. 'to_header_guard': self._ToHeaderGuard,
  59. }
  60. def RenderTemplate(self,
  61. path_to_template: str,
  62. path_to_template_helper: str = None):
  63. template_dir = GetDirNameFromPath(path_to_template)
  64. template_name = GetFileNameFromPath(path_to_template)
  65. jinja_env = jinja2.Environment(
  66. loader=jinja2.FileSystemLoader(template_dir),
  67. keep_trailing_newline=True)
  68. jinja_env.globals.update(self.GetGlobals(path_to_template))
  69. jinja_env.filters.update(self.GetFilters())
  70. if path_to_template_helper:
  71. template_helper_module = self._LoadTemplateHelper(
  72. path_to_template_helper)
  73. jinja_env.globals.update(
  74. template_helper_module.get_custom_globals(self.model))
  75. jinja_env.filters.update(
  76. template_helper_module.get_custom_filters(self.model))
  77. template = jinja_env.get_template(template_name)
  78. return template.render()
  79. def _LoadTemplateHelper(self, path_to_template_helper: str):
  80. template_helper_dir = GetDirNameFromPath(path_to_template_helper)
  81. try:
  82. sys.path.append(template_helper_dir)
  83. spec = importlib.util.spec_from_file_location(
  84. path_to_template_helper, path_to_template_helper)
  85. module = importlib.util.module_from_spec(spec)
  86. spec.loader.exec_module(module)
  87. return module
  88. finally:
  89. # Restore sys.path to what it was before.
  90. sys.path.remove(template_helper_dir)
  91. def _ToHeaderGuard(self, path: str):
  92. return re.sub(r'[\\\/\.\-]+', '_', path.upper())