def dispatch_on(*dispatch_args):
assert dispatch_args, '<STR_LIT>'<EOL>dispatch_str = '<STR_LIT>' % '<STR_LIT:U+002CU+0020>'.join(dispatch_args)<EOL>def check(arguments, wrong=operator.ne, msg='<STR_LIT>'):<EOL><INDENT>"""<STR_LIT>"""<EOL>if wrong(len(arguments), len(dispatch_args)):<EOL><INDENT>raise TypeError('<STR_LIT>' %<EOL>(len(dispatch_args), len(arguments), msg))<EOL><DEDENT><DEDENT>def gen_func_dec(func):<EOL><INDENT>"""<STR_LIT>"""<EOL>argset = set(getfullargspec(func).args)<EOL>if not set(dispatch_args) <= argset:<EOL><INDENT>raise NameError('<STR_LIT>' % dispatch_str)<EOL><DEDENT>typemap = {}<EOL>def vancestors(*types):<EOL><INDENT>"""<STR_LIT>"""<EOL>check(types)<EOL>ras = [[] for _ in range(len(dispatch_args))]<EOL>for types_ in typemap:<EOL><INDENT>for t, type_, ra in zip(types, types_, ras):<EOL><INDENT>if issubclass(t, type_) and type_ not in t.__mro__:<EOL><INDENT>append(type_, ra)<EOL><DEDENT><DEDENT><DEDENT>return [set(ra) for ra in ras]<EOL><DEDENT>def ancestors(*types):<EOL><INDENT>"""<STR_LIT>"""<EOL>check(types)<EOL>lists = []<EOL>for t, vas in zip(types, vancestors(*types)):<EOL><INDENT>n_vas = len(vas)<EOL>if n_vas > <NUM_LIT:1>:<EOL><INDENT>raise RuntimeError(<EOL>'<STR_LIT>' % (t, vas))<EOL><DEDENT>elif n_vas == <NUM_LIT:1>:<EOL><INDENT>va, = vas<EOL>mro = type('<STR_LIT:t>', (t, va), {}).__mro__[<NUM_LIT:1>:]<EOL><DEDENT>else:<EOL><INDENT>mro = t.__mro__<EOL><DEDENT>lists.append(mro[:-<NUM_LIT:1>])  <EOL><DEDENT>return lists<EOL><DEDENT>def register(*types):<EOL><INDENT>"""<STR_LIT>"""<EOL>check(types)<EOL>def dec(f):<EOL><INDENT>check(getfullargspec(f).args, operator.lt, '<STR_LIT>' + f.__name__)<EOL>typemap[types] = f<EOL>return f<EOL><DEDENT>return dec<EOL><DEDENT>def dispatch_info(*types):<EOL><INDENT>"""<STR_LIT>"""<EOL>check(types)<EOL>lst = []<EOL>for anc in itertools.product(*ancestors(*types)):<EOL><INDENT>lst.append(tuple(a.__name__ for a in anc))<EOL><DEDENT>return lst<EOL><DEDENT>def _dispatch(dispatch_args, *args, **kw):<EOL><INDENT>types = tuple(type(arg) for arg in dispatch_args)<EOL>try:  <EOL><INDENT>f = typemap[types]<EOL><DEDENT>except KeyError:<EOL><INDENT>pass<EOL><DEDENT>else:<EOL><INDENT>return f(*args, **kw)<EOL><DEDENT>combinations = itertools.product(*ancestors(*types))<EOL>next(combinations)  <EOL>for types_ in combinations:<EOL><INDENT>f = typemap.get(types_)<EOL>if f is not None:<EOL><INDENT>return f(*args, **kw)<EOL><DEDENT><DEDENT>return func(*args, **kw)<EOL><DEDENT>return FunctionMaker.create(<EOL>func, '<STR_LIT>' % dispatch_str,<EOL>dict(_f_=_dispatch), register=register, default=func,<EOL>typemap=typemap, vancestors=vancestors, ancestors=ancestors,<EOL>dispatch_info=dispatch_info, __wrapped__=func)<EOL><DEDENT>gen_func_dec.__name__ = '<STR_LIT>' + dispatch_str<EOL>return gen_func_dec<EOL>
Factory of decorators turning a function into a generic function
dispatching on the given arguments.