# # Copyright 2009 - Cigital, Inc. - www.cigital.com # Romain Gaucher # http://rgaucher.info - @rgaucher # import inspect, math class DDFactory(object): """ Factory that returns the object constructor based on a given list of arguments. The returned class ctor has the best matching number of common args with the given list. example: class Shape(object): pass class Circle(Shape): def __init__(self, center, radius=RAD_MAX): .... class DiskHole(Shape): def __init__(self, center, radius, small_radius=RAD_SMALL): .... factory = DDFactory() factory.register(Shape) factory.register(Circle) factory.register(DiskHole) print factory.get(['center', 'radius']) #> return 'Circle' ctor print factory.get(['center', 'radius', 'small_radius']) #> return 'DiskHole' ctor """ def __init__(self): self.registrar = {} @staticmethod def defaults_values(cls): args, vargs, vkw, defaults = inspect.getargspec(cls.__init__) if inspect.ismethod(cls.__init__): args = args[1:] ret_dict = dict(zip(args, [None] * len(args))) if defaults: ret_dict.update(dict(zip(args[-len(defaults):], defaults))) return args, ret_dict @staticmethod def descent(d, level_return=3): if level_return < 1: return d ld = d.keys(); ld.sort() for i in ld: if not len(d[i]): continue return DDFactory.descent(d[i], level_return - 1) return None def register(self, cls, has_attr='__tablename__'): if hasattr(cls, has_attr) and hasattr(cls, '__init__'): args, defaults_dict = DDFactory.defaults_values(cls) if cls.__tablename__ not in self.registrar: self.registrar[cls.__tablename__] = {'class' : cls, 'args' : args, 'defaults' : defaults_dict} # from given 'args' return matching dict def get(self, args): set_args, len_args = set(args), len(args) best_matches = {} for cls_name in self.registrar: s = set(self.registrar[cls_name]['args']) len_s = len(s) c1 = len_s - len(set_args.intersection(s)) # found minus number args c2 = int(math.fabs(len_s - len_args)) if c1 not in best_matches: best_matches[c1] = {} if c2 >= 0 and c2 not in best_matches[c1]: best_matches[c1][c2] = [] if c2 >= 0 and self.registrar[cls_name] not in best_matches[c1][c2]: best_matches[c1][c2].append((cls_name, self.registrar[cls_name])) return DDFactory.descent(best_matches, 2)[0][1]