uberpoet/moduletree.py (81 lines of code) (raw):

# Copyright (c) 2018 Uber Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import absolute_import import random from .util import merge_lists class ModuleGenType(object): flat = 'flat' bs_flat = 'bs_flat' layered = 'layered' bs_layered = 'bs_layered' dot = 'dot' @staticmethod def enum_list(): return [ ModuleGenType.flat, ModuleGenType.bs_flat, ModuleGenType.layered, ModuleGenType.bs_layered, ModuleGenType.dot, ] class ModuleNode(object): """Represents a module in a dependency graph""" APP = 'APP' LIBRARY = 'LIBRARY' def __init__(self, name, node_type, deps=None): super(ModuleNode, self).__init__() if deps is None: deps = [] self.name = name self.node_type = node_type # app or library self.deps = deps # How many code units the module represents. Bigger modules would # have more code units than smaller modules, with 1 being the 'standard' size. self.code_units = 1 self.extra_info = None # useful for file indexes and such def __hash__(self): return hash((self.name, self.node_type)) def __eq__(self, other): return (self.name, self.node_type) == (other.name, other.node_type) def __repr__(self): return "ModuleNode('{}','{}')".format(self.name, self.node_type) def __str__(self): extra = True if self.extra_info else False return "<{} : {} deps: {} has_info: {}>".format(self.name, self.node_type, len(self.deps), extra) @staticmethod def gen_layered_graph(layer_count, nodes_per_layer, deps_per_node=5): """Generates a module dependency graph that has `layer_count` layers, with each module only depending on a random selection of the modules below it""" def node(f_layer, f_node): return ModuleNode('MockLib{}_{}'.format(f_layer, f_node), ModuleNode.LIBRARY) layers = [[node(l, n) for n in xrange(nodes_per_layer)] for l in xrange(layer_count)] all_nodes = merge_lists(layers) app_node = ModuleNode('App', ModuleNode.APP, layers[0]) for i, layer in enumerate(layers): lower_layers = layers[(i + 1):] if i != len(layers) - 1 else [] lower_merged = merge_lists(lower_layers) for node in layer: if deps_per_node < len(lower_merged): node.deps = random.sample(lower_merged, deps_per_node) else: node.deps = lower_merged return app_node, (all_nodes + [app_node]) @staticmethod def gen_flat_graph(module_count): """Generates a module dependency graph that depends on `module_count` libraries that don't depend on anything""" libraries = [ModuleNode('MockLib{}'.format(i), ModuleNode.LIBRARY) for i in xrange(module_count)] app_node = ModuleNode('App', ModuleNode.APP, libraries) return app_node, (libraries + [app_node]) @staticmethod def gen_flat_big_small_graph(big_mod_count, small_mod_count): big_libs = [ModuleNode('BigMockLib{}'.format(i), ModuleNode.LIBRARY) for i in xrange(big_mod_count)] small_libs = [ModuleNode('SmallMockLib{}'.format(i), ModuleNode.LIBRARY) for i in xrange(small_mod_count)] app_node = ModuleNode('App', ModuleNode.APP, big_libs + small_libs) for l in big_libs: l.code_units = 20 return app_node, (big_libs + small_libs + [app_node]) @staticmethod def gen_layered_big_small_graph(big_mod_count, small_mod_count): big_libs = [ModuleNode('AppMockLib{}'.format(i), ModuleNode.LIBRARY) for i in xrange(big_mod_count)] app_node = ModuleNode('App', ModuleNode.APP, big_libs) layer_count = 3 layer_mod_count = small_mod_count / layer_count deps_per_layer = layer_count / 2 if layer_count >= 2 else 1 layer_app_node, layer_nodes = ModuleNode.gen_layered_graph(layer_count, layer_mod_count, deps_per_layer) layer_nodes = [layer_item for layer_item in layer_nodes if layer_item != layer_app_node] for l in big_libs: l.code_units = 20 l.deps = layer_app_node.deps return app_node, (big_libs + layer_nodes + [app_node])