PageRenderTime 56ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/scripts/artificialproject/field_generators.py

https://gitlab.com/smartether/buck
Python | 377 lines | 350 code | 27 blank | 0 comment | 36 complexity | 275c1e225015ca25d02a3d7a064dc374 MD5 | raw file
  1. import collections
  2. import os
  3. import random
  4. from artificialproject.random import weighted_choice
  5. class GenerationFailedException(Exception):
  6. pass
  7. GeneratedField = collections.namedtuple('GeneratedField', [
  8. 'value',
  9. 'deps',
  10. ])
  11. class NullableGenerator:
  12. def __init__(self, value_generator):
  13. self._value_generator = value_generator
  14. self._null_values = collections.Counter()
  15. def add_sample(self, base_path, sample):
  16. if sample is None:
  17. self._null_values.update([True])
  18. else:
  19. self._null_values.update([False])
  20. self._value_generator.add_sample(base_path, sample)
  21. def generate(self, base_path):
  22. if weighted_choice(self._null_values):
  23. return GeneratedField(None, [])
  24. else:
  25. return self._value_generator.generate(base_path)
  26. class SingletonGenerator:
  27. def __init__(self, set_generator):
  28. self._set_generator = set_generator
  29. def add_sample(self, base_path, sample):
  30. self._set_generator.add_sample(base_path, [sample])
  31. def generate(self, base_path):
  32. field = self._set_generator.generate(base_path)
  33. assert len(field.value) == 1, field
  34. return GeneratedField(field.value[0], field.deps)
  35. class EnumSetGenerator:
  36. def __init__(self):
  37. self._lengths = collections.Counter()
  38. self._values = collections.Counter()
  39. def add_sample(self, base_path, sample):
  40. self._lengths.update([len(sample)])
  41. self._values.update(sample)
  42. def generate(self, base_path):
  43. length = weighted_choice(self._lengths)
  44. options = collections.Counter(self._values)
  45. output = []
  46. while len(output) < length:
  47. value = weighted_choice(options)
  48. output.append(value)
  49. del options[value]
  50. return GeneratedField(output, [])
  51. class StringGenerator:
  52. def __init__(self, respect_file_extensions=False):
  53. self._respect_file_extensions = respect_file_extensions
  54. self._lengths = collections.Counter()
  55. self._first_chars = collections.Counter()
  56. self._other_chars = collections.Counter()
  57. if self._respect_file_extensions:
  58. self._extensions = collections.Counter()
  59. def add_sample(self, base_path, sample):
  60. self.add_string_sample(sample)
  61. def add_string_sample(self, sample):
  62. if self._respect_file_extensions:
  63. sample, extension = os.path.splitext(sample)
  64. self._extensions.update([extension])
  65. self._lengths.update([len(sample)])
  66. if sample:
  67. self._first_chars.update(sample[0])
  68. for ch in sample[1:]:
  69. self._other_chars.update(ch)
  70. def generate(self, base_path):
  71. return GeneratedField(self.generate_string(), [])
  72. def generate_string(self):
  73. length = weighted_choice(self._lengths)
  74. output = ''
  75. if length > 0:
  76. output += weighted_choice(self._first_chars)
  77. while len(output) < length:
  78. output += weighted_choice(self._other_chars)
  79. if self._respect_file_extensions:
  80. output += weighted_choice(self._extensions)
  81. return output
  82. class VisibilityGenerator:
  83. def add_sample(self, base_path, sample):
  84. pass
  85. def generate(self, base_path):
  86. return GeneratedField(['PUBLIC'], [])
  87. class BuildTargetSetGenerator:
  88. class DynamicFilteredList:
  89. def __init__(self, input_list, predicate):
  90. self._input_list = input_list
  91. self._predicate = predicate
  92. self._output_list = []
  93. self._processed = 0
  94. def get_values(self):
  95. input_len = len(self._input_list)
  96. while self._processed < input_len:
  97. value = self._input_list[self._processed]
  98. if self._predicate(value):
  99. self._output_list.append(value)
  100. self._processed += 1
  101. return self._output_list
  102. def __init__(
  103. self,
  104. context,
  105. process_output_extensions=False,
  106. override_types=None):
  107. self._context = context
  108. self._process_output_extensions = process_output_extensions
  109. self._lengths = collections.Counter()
  110. self._types = collections.Counter()
  111. self._unique_values_by_type_and_extension = collections.defaultdict(set)
  112. self._unique_values_dirty = False
  113. self._choice_probability_by_type_and_extension = dict()
  114. self._accepted_targets_by_type = dict()
  115. self._accepted_targets_with_output_by_type = dict()
  116. if self._process_output_extensions:
  117. self._output_extensions_by_type = collections.defaultdict(
  118. collections.Counter)
  119. if override_types is None:
  120. self._override_types = {}
  121. else:
  122. self._override_types = dict(override_types)
  123. def add_sample(self, base_path, sample):
  124. self._lengths.update([len(sample)])
  125. for target in sample:
  126. target = target.split('#')[0]
  127. if target.startswith(':'):
  128. target = '//' + base_path + target
  129. target_data = self._context.input_target_data[target]
  130. target_type = target_data['buck.type']
  131. target_type = self._override_types.get(target_type, target_type)
  132. self._types.update([target_type])
  133. extension = None
  134. if self._process_output_extensions:
  135. extension = self._get_output_extension(target_data)
  136. self._output_extensions_by_type[target_type].update([extension])
  137. self._unique_values_by_type_and_extension[
  138. (target_type, extension)].add(target)
  139. self._unique_values_dirty = True
  140. def _update_choice_probability(self):
  141. self._choice_probability_by_type_and_extension = dict()
  142. for (type, extension), used_values in (
  143. self._unique_values_by_type_and_extension.items()):
  144. all_values = (x for x in self._context.input_target_data.values()
  145. if x['buck.type'] == type)
  146. if self._process_output_extensions:
  147. all_values = (x for x in all_values
  148. if self._get_output_extension(x) == extension)
  149. num = len(used_values)
  150. denom = sum(1 for x in all_values)
  151. probability = float(num) / denom
  152. key = (type, extension)
  153. self._choice_probability_by_type_and_extension[key] = probability
  154. def _is_accepted(self, target_name):
  155. target_data = self._context.gen_target_data[target_name]
  156. target_type = target_data['buck.type']
  157. extension = None
  158. if self._process_output_extensions:
  159. extension = self._get_output_extension(target_data)
  160. probability = self._choice_probability_by_type_and_extension.get(
  161. (target_type, extension), 0)
  162. return random.uniform(0, 1) < probability
  163. def generate(self, base_path, force_length=None):
  164. if self._unique_values_dirty:
  165. self._update_choice_probability()
  166. self._unique_values_dirty = False
  167. if force_length is not None:
  168. length = force_length
  169. else:
  170. length = weighted_choice(self._lengths)
  171. type_extension_counts = collections.Counter()
  172. for i in range(length):
  173. type = weighted_choice(self._types)
  174. if self._process_output_extensions:
  175. extension = weighted_choice(
  176. self._output_extensions_by_type[type])
  177. else:
  178. extension = None
  179. type_extension_counts.update([(type, extension)])
  180. output = []
  181. if self._process_output_extensions:
  182. all_targets_dict = self._context.gen_targets_with_output_by_type
  183. accepted_targets_dict = self._accepted_targets_with_output_by_type
  184. else:
  185. all_targets_dict = self._context.gen_targets_by_type
  186. accepted_targets_dict = self._accepted_targets_by_type
  187. for (type, extension), count in type_extension_counts.items():
  188. options = accepted_targets_dict.get(type)
  189. if options is None:
  190. options = self.DynamicFilteredList(
  191. all_targets_dict[type],
  192. lambda x: self._is_accepted(x))
  193. accepted_targets_dict[type] = options
  194. options = options.get_values()
  195. if extension is not None:
  196. options = [x for x in options
  197. if self._get_output_extension(
  198. self._context.gen_target_data[x]) == extension]
  199. if count > len(options):
  200. raise GenerationFailedException()
  201. output.extend(random.sample(options, count))
  202. return GeneratedField(output, output)
  203. def _get_output_extension(self, target_data):
  204. if 'out' not in target_data or target_data['out'] is None:
  205. return None
  206. extension = os.path.splitext(target_data['out'])[1]
  207. if extension == '':
  208. return None
  209. return extension
  210. class PathSetGenerator:
  211. def __init__(self, context):
  212. self._context = context
  213. self._component_generator = StringGenerator()
  214. self._lengths = collections.Counter()
  215. self._component_counts = collections.Counter()
  216. self._extensions = collections.Counter()
  217. def add_sample(self, base_path, sample):
  218. self._lengths.update([len(sample)])
  219. for path in sample:
  220. self._context.file_path_generator.add_package_file_sample(
  221. base_path,
  222. path)
  223. components = []
  224. while path:
  225. path, component = os.path.split(path)
  226. components.append(component)
  227. self._component_counts.update([len(components)])
  228. if not components:
  229. self._extensions.update([''])
  230. else:
  231. components[0], extension = os.path.splitext(components[0])
  232. self._extensions.update([extension])
  233. for component in components:
  234. self._component_generator.add_sample(base_path, component)
  235. def generate(self, base_path, force_length=None):
  236. if force_length is not None:
  237. length = force_length
  238. else:
  239. length = weighted_choice(self._lengths)
  240. extension = weighted_choice(self._extensions)
  241. output = [self._generate_path(base_path, extension)
  242. for i in range(length)]
  243. return GeneratedField(output, [])
  244. def _generate_path(self, base_path, extension):
  245. component_count = weighted_choice(self._component_counts)
  246. path = self._context.file_path_generator.generate_path_in_package(
  247. base_path,
  248. component_count,
  249. self._component_generator,
  250. extension)
  251. full_path = os.path.join(
  252. self._context.output_repository,
  253. base_path,
  254. path)
  255. os.makedirs(os.path.dirname(full_path), exist_ok=True)
  256. with open(full_path, 'w'):
  257. pass
  258. return path
  259. class SourcePathSetGenerator:
  260. def __init__(self, context):
  261. self._build_target_set_generator = BuildTargetSetGenerator(
  262. context, process_output_extensions=True)
  263. self._path_set_generator = PathSetGenerator(context)
  264. self._lengths = collections.Counter()
  265. self._build_target_values = collections.Counter()
  266. def add_sample(self, base_path, sample):
  267. self._lengths.update([len(sample)])
  268. for source_path in sample:
  269. if source_path.startswith('//') or source_path.startswith(':'):
  270. self._build_target_values.update([True])
  271. self._build_target_set_generator.add_sample(
  272. base_path, [source_path])
  273. else:
  274. self._build_target_values.update([False])
  275. self._path_set_generator.add_sample(base_path, [source_path])
  276. def generate(self, base_path):
  277. length = weighted_choice(self._lengths)
  278. build_target_count = 0
  279. path_count = 0
  280. for i in range(length):
  281. if weighted_choice(self._build_target_values):
  282. build_target_count += 1
  283. else:
  284. path_count += 1
  285. build_targets = self._build_target_set_generator.generate(
  286. base_path,
  287. force_length=build_target_count)
  288. paths = self._path_set_generator.generate(
  289. base_path, force_length=path_count)
  290. assert len(build_targets.value) == build_target_count, (
  291. build_targets, build_target_count)
  292. assert len(paths.value) == path_count, (paths, path_count)
  293. return GeneratedField(
  294. build_targets.value + paths.value,
  295. build_targets.deps + paths.deps)
  296. class SourcesWithFlagsGenerator:
  297. def __init__(self, context):
  298. self._source_path_set_generator = SourcePathSetGenerator(context)
  299. self._flag_generator = StringGenerator()
  300. self._flag_counts = collections.Counter()
  301. def add_sample(self, base_path, sample):
  302. source_paths = []
  303. flag_lists = []
  304. for source_with_flags in sample:
  305. if isinstance(source_with_flags, list):
  306. source_paths.append(source_with_flags[0])
  307. flag_lists.append(source_with_flags[1])
  308. else:
  309. source_paths.append(source_with_flags)
  310. flag_lists.append([])
  311. self._source_path_set_generator.add_sample(base_path, source_paths)
  312. for flags in flag_lists:
  313. self._flag_counts.update([len(flags)])
  314. for flag in flags:
  315. self._flag_generator.add_sample(base_path, flag)
  316. def generate(self, base_path):
  317. source_paths = self._source_path_set_generator.generate(base_path)
  318. output = [self._generate_source_with_flags(base_path, sp)
  319. for sp in source_paths.value]
  320. return GeneratedField(output, source_paths.deps)
  321. def _generate_source_with_flags(self, base_path, source_path):
  322. flag_count = weighted_choice(self._flag_counts)
  323. if flag_count == 0:
  324. return source_path
  325. flags = [self._flag_generator.generate(base_path).value
  326. for i in range(flag_count)]
  327. return [source_path, flags]