def __init__(self, origin, cls, name, doc=None): Dispatcher.__init__(self, name, doc) self.origin = origin self.obj = None # Copy standard and lambda functions standard_funcs = origin.standard_funcs.copy() lambda_funcs = origin.lambda_funcs.copy() # Add all functions from ancestor classes for base_cls in inspect.getmro(cls): if base_cls is cls: continue if hasattr(base_cls, name): parent_func = getattr(base_cls, name) if isinstance(parent_func, MethodDispatcher): parent_func = parent_func.origin for (parent_standard_key_i, parent_standard_func_i ) in parent_func.standard_funcs.items(): if parent_standard_key_i not in standard_funcs: standard_funcs[ parent_standard_key_i] = parent_standard_func_i for (parent_lambda_key_i, parent_lambda_func_i ) in parent_func.lambda_funcs.items(): if parent_lambda_key_i not in lambda_funcs: lambda_funcs[ parent_lambda_key_i] = parent_lambda_func_i elif ismethod(parent_func) and not hasattr( parent_func, "__isabstractmethod__"): parent_signature = ( len(inspect.signature(parent_func).parameters) - 1) * (object, ) parent_standard_key = (parent_signature, parent_signature) if parent_standard_key not in standard_funcs: standard_funcs[parent_standard_key] = parent_func else: pass # This happens with slot wrapper, method wrapper and method descriptor types for builtin objects # Add all standard functions for (key, func) in standard_funcs.items(): self._add(key[0], key[1], func) # Add all lambda functions # NOTE: it is not possible to change the underlying type returned by lambda after this loop has been processed for (signature_lambda, lambda_func) in lambda_funcs.items(): signature = tuple([ typ(cls) if islambda(typ) else typ for typ in signature_lambda ]) self._add(signature, signature, lambda_func) # Trigger reordering, if needed self._cache.clear() try: del self._ordering except AttributeError: pass
def _(func_or_class): nonlocal name name = func_or_class.__name__ if name is None else name is_class = inspect.isclass(func_or_class) is_method = not is_class and ismethod(func_or_class) module = inspect.getmodule(func_or_class) nonlocal types if len(types) == 0: assert is_method or not is_class # is method or function signature = inspect.signature(func_or_class) annotations = tuple(param_value.annotation for (param_name, param_value) in signature.parameters.items() if param_value.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD)) if is_method: annotations = annotations[1:] # throw self away assert all(ann is not inspect.Parameter.empty for ann in annotations) types = annotations if is_method or not is_class: # is method or function signature = inspect.signature(func_or_class) assert len(types) == len(tuple(param_name for (param_name, param_value) in signature.parameters.items() if param_value.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD))) - (1 if is_method else 0) # throw self away if is_method: assert module_kwarg is None assert replaces is None assert replaces_if is None frame = inspect.currentframe() for _ in range(frame_back_times): frame = frame.f_back dispatcher = frame.f_locals.get(name, MethodDispatcher_Wrapper(name)) dispatcher.add(types, func_or_class) return dispatcher else: # is function or class if is_class: assert module_kwarg is not None assert module_kwarg is not module # otherwise module.name is not a class anymore but a function module = module_kwarg else: if module_kwarg is None: pass # we would like to store the dispatched function in its module, rather than the original one else: module = module_kwarg # we would like to store the original function in its module and the dispatched in module_kwarg if not hasattr(module, name): setattr(module, name, Dispatcher(name)) dispatcher = getattr(module, name) assert isinstance(dispatcher, Dispatcher) dispatcher.add(types, func_or_class, replaces=replaces, replaces_if=replaces_if) if module_kwarg is None: return dispatcher else: return func_or_class