def __init__(self, *args, **kwargs): InstanceCounterMixin.__init__(self) self.data = defaultdict(None) self.required = kwargs.pop("required", False) self._init(*args, **kwargs) # N.B. ``functools.singledispatch()`` dispatches on the type of the # first argument of a function, so we can't use decorator syntax on # a method (unless it's a staticmethod). Instead we use it in # functional form on a bound method. self.convert = functools.singledispatch(self.convert) self.convert.register(type(None), self._convert_none) self.convert.register(str, self._convert_str) self.convert.register(bool, self._convert_bool) self.convert.register(int, self._convert_int) self.convert.register(decimal.Decimal, self._convert_decimal) self.convert.register(datetime.datetime, self._convert_datetime) self.convert.register(datetime.time, self._convert_time) self.unconvert = functools.singledispatch(self.unconvert) self.unconvert.register(type(None), self._unconvert_none) self.unconvert.register(str, self._unconvert_str) self.unconvert.register(bool, self._unconvert_bool) self.unconvert.register(int, self._unconvert_int) self.unconvert.register(decimal.Decimal, self._unconvert_decimal) self.unconvert.register(datetime.datetime, self._unconvert_datetime) self.unconvert.register(datetime.time, self._unconvert_time)
def _generalizing(func: Callable, types: Iterable[Type]) -> Callable: """Returns the generic function and register the types""" if object in types: generic = singledispatch(func) else: # have to define this function here, so that every time a new function # is generated. # Otherwise, singledispatch mixed the registry when registering the # same function @wraps(func) def _not_implemented(_data: Any, *args: Any, **kwargs: Any) -> None: raise NotImplementedError(f"{func.__qualname__!r} is not " f"registered for type: {type(_data)}.") _not_implemented.__name__ = "_not_implemented" # __name__ is used to tell if object is allowed _not_implemented.context = func.context _not_implemented.extra_contexts = func.extra_contexts generic = singledispatch(_not_implemented) for typ in types: if typ is not object: generic.register(typ, func) generic.__origfunc__ = func return generic
def __init__(self, config: dict): self._serialize_values = singledispatch(self._serialize_values) self._serialize_values.register(dict, self._serialize_values_dict) self._serialize_values.register(list, self._serialize_values_list) if config: for key, value in config.items(): self._serialize_values(value, key)
def decorator(func: Callable) -> Callable[Any]: """ Decorator for function/method overloading based on argument at position n instead of 0, or at keyword n like functools.singledispatch. Similar to https://stackoverflow.com/questions/24601722/how-can-i-use-functools-singledispatch-with-instance-methods """ dispatcher = singledispatch(func) if isinstance(n, numbers.Integral): def wrapper(*args: Any, **kwargs: Any) -> Any: return dispatcher.dispatch(args[n].__class__)(*args, **kwargs) elif isinstance(n, str): def wrapper(*args, **kwargs) -> Any: return dispatcher.dispatch(kwargs[n].__class__)(*args, **kwargs) # so that registering a type for the wrapped instance method # can be done by calling method.register(type) wrapper.register = dispatcher.register update_wrapper(wrapper, func) return wrapper
def __init__(self, formatter=None, parser=None, error_handler=None, pre_processor=None, post_processor=None): self.route = singledispatch(self.route) self.route.register(str, self.route_decorator) if error_handler is None: error_handler = ErrorHandler() error_handler.register(BaseException, self._handle_exception) error_handler.register(HTTPException, self._handle_http_exception) if parser is None: parser = Parser() if formatter is None: formatter = Formatter() formatter.register('text/plain', str, default=True) self.formatter = formatter self.parser = parser self.error_handler = error_handler self.pre_processor = DataPipe() if pre_processor is None else pre_processor self.post_processor = DataPipe() if post_processor is None else post_processor self.resources = {} self.routes = [] self.endpoints = {}
def __init__(self): super().__init__() self.import_kf_root = singledispatch(self.import_kf_root) self.import_kf_root.register(NifFormat.NiControllerSequence, self.import_controller_sequence) self.import_kf_root.register(NifFormat.NiSequenceStreamHelper, self.import_sequence_stream_helper)
def methdispatch(func): dispatcher = singledispatch(func) def wrapper(*args, **kw): return dispatcher.dispatch(args[1].__class__)(*args, **kw) wrapper.register = dispatcher.register update_wrapper(wrapper, func) return wrapper
def dispatch( method: Callable[[Any, Type[T], Any, PipelineContext], None] ) -> Callable[[Any, Type[T], Any, PipelineContext], None]: dispatcher = singledispatch(method) accepts = set() def wrapper(self: Any, type: Type[T], items: Any, context: PipelineContext = None) -> None: call = dispatcher.dispatch(type) try: return call(self, items, context=context) except TypeError as error: raise DataSink.unsupported(type) from error def register( type: Type[T] ) -> Callable[[Any, Type[T], Any, PipelineContext], None]: accepts.add(type) return dispatcher.register(type) wrapper.register = register wrapper._accepts = accepts update_wrapper(wrapper, method) return wrapper
def __init__(self): # the current symbol table: a dictionary of variable names and values self.curSymTable = {} # self.stack = [] # a dict of function name mapping to a function node self.funcTable = {} self.visit = singledispatch(self.visit) self.visit.register(ast.If, self.visit_if) self.visit.register(ast.Conditional, self.visit_conditional) self.visit.register(ast.Equality, self.visit_equality) self.visit.register(ast.UnaryOp, self.visit_unary) self.visit.register(ast.Constant, self.visit_constant) self.visit.register(ast.ID, self.visit_id) self.visit.register(ast.Assignment, self.visit_assignment) self.visit.register(ast.InterpreterMultipleBlock, self.visit_interpreter_multiple_block) self.visit.register(ast.MultipleStmt, self.visit_multiple_statement) self.visit.register(ast.BasicOperators, self.visit_basic_operators) self.visit.register(ast.While, self.visit_while) self.visit.register(ast.Do, self.visit_do) self.visit.register(ast.Sequence, self.visit_sequence) self.visit.register(ast.For, self.visit_for) self.visit.register(ast.LogicalOR, self.visit_logical_or) self.visit.register(ast.LogicalAND, self.visit_logical_and) self.visit.register(ast.Break, self.visit_break) self.visit.register(ast.FunctionDecl, self.visit_function_decl) self.visit.register(ast.CompoundStmt, self.visit_compound) self.visit.register(ast.FunctionCall, self.visit_function_call) self.visit.register(ast.ParamSpec, self.visit_param_spec) self.visit.register(ast.Return, self.visit_return) self.visit.register(ast.Relational, self.visit_relational) self.visit.register(ast.CreateMatrix, self.visit_matrix)
def dispatchmethod( func: Callable[[Any, TARG], TRET]) -> DispatchMethodWrapper[TARG, TRET]: """A variation of functools.singledispatch for methods.""" from functools import singledispatch, update_wrapper origwrapper: Any = singledispatch(func) # Pull this out so hopefully origwrapper can die, # otherwise we reference origwrapper in our wrapper. dispatch = origwrapper.dispatch # All we do here is recreate the end of functools.singledispatch # where it returns a wrapper except instead of the wrapper using the # first arg to the function ours uses the second (to skip 'self'). # This was made against Python 3.7; we should probably check up on # this in later versions in case anything has changed. # (or hopefully they'll add this functionality to their version) # NOTE: sounds like we can use functools singledispatchmethod in 3.8 def wrapper(*args: Any, **kw: Any) -> Any: if not args or len(args) < 2: raise TypeError(f'{funcname} requires at least ' '2 positional arguments') return dispatch(args[1].__class__)(*args, **kw) funcname = getattr(func, '__name__', 'dispatchmethod method') wrapper.register = origwrapper.register # type: ignore wrapper.dispatch = dispatch # type: ignore wrapper.registry = origwrapper.registry # type: ignore # pylint: disable=protected-access wrapper._clear_cache = origwrapper._clear_cache # type: ignore update_wrapper(wrapper, func) # pylint: enable=protected-access return cast(DispatchMethodWrapper, wrapper)
def symbolic_dispatch(f=None, cls=object): if f is None: return lambda f: symbolic_dispatch(f, cls) # TODO: don't use singledispatch if it has already been done dispatch_func = singledispatch(f) if cls is not object: dispatch_func.register(cls, f) dispatch_func.register(object, _dispatch_not_impl(dispatch_func.__name__)) @dispatch_func.register(Symbolic) def _dispatch_symbol(__data, *args, **kwargs): return create_sym_call(FuncArg(dispatch_func), __data.source, *args, **kwargs) @dispatch_func.register(Call) def _dispatch_call(__data, *args, **kwargs): # TODO: want to just create call, for now use hack of creating a symbolic # call and getting the source off of it... return create_sym_call(FuncArg(dispatch_func), __data, *args, **kwargs).source return dispatch_func
def singledispatchmethod(func): ''' This is a temporary function decorator used to replace the new functionality which will be implemented in python 3.8 under the functools library. This is used as a way to get dispatching for class methods. This is used with the following syntax: @singledispatchmethod def func(self, arg): pass @func.register(type) def _(self, arg): pass ''' dispatcher = singledispatch(func) def wrapper(*args, **kw): return dispatcher.dispatch(args[1].__class__)(*args, **kw) wrapper.register = dispatcher.register update_wrapper(wrapper, func) return wrapper
def dispatch( method: Callable[[Any, Type[T], Mapping[str, Any], PipelineContext], Any] ) -> Callable[[Any, Type[T], Mapping[str, Any], PipelineContext], Any]: dispatcher = singledispatch(method) provides = set() def wrapper(self: Any, type: Type[T], query: Mapping[str, Any], context: PipelineContext = None) -> Any: call = dispatcher.dispatch(type) try: return call(self, query, context=context) except TypeError: raise DataSource.unsupported(type) def register( type: Type[T] ) -> Callable[[Any, Type[T], Mapping[str, Any], PipelineContext], Any]: provides.add(type) return dispatcher.register(type) wrapper.register = register wrapper._provides = provides update_wrapper(wrapper, method) return wrapper
class MessageMixin: def __init__(self) -> None: self.mailbox: Deque[Message] = collections.deque() async def send_message_to(self, other: int, msg: Message) -> None: await chaos.fuzz() letter = Letter(self.id, other, msg) self.coordinator.send_letter(letter) self.log("sent message to " + str(other) + ": " + str(msg)) await chaos.fuzz() async def process_mailbox(self): while len(self.mailbox) > 0: await chaos.fuzz() letter = self.mailbox.popleft() assert letter.to == self.id fn = self.handle_message.dispatch(type(letter.message)) await fn(self, letter.message, letter.frm) self.log(f"recv message from {letter.frm}: {letter.message}") await chaos.fuzz() async def _handle_message_catch(self, msg: Message, frm: int) -> None: raise Exception("No message handling logic for message type " + msg.__class__.__name__ + " in " + self.__class__.__name__ + " sent by " + str(frm)) handle_message = functools.singledispatch(_handle_message_catch)
def __init__(self, value_differ, *args, **kwargs): super().__init__(*args, **kwargs) self.value_differ = value_differ self.calculate_diff = singledispatch( self._calculate_diff_regression_rep) self.calculate_diff.register(DepthEstimationAnnotation, self._calculate_diff_depth_estimation_rep)
def __init__(self, data_source, config=None, postpone_data_source=False, **kwargs): self.config = config or {} self._postpone_data_source = postpone_data_source self.data_source = data_source self.read_dispatcher = singledispatch(self.read) self.read_dispatcher.register(list, self._read_list) self.read_dispatcher.register(ClipIdentifier, self._read_clip) self.read_dispatcher.register(MultiFramesInputIdentifier, self._read_frames_multi_input) self.read_dispatcher.register(ImagePairIdentifier, self._read_pair) self.read_dispatcher.register(ListIdentifier, self._read_list_ids) self.read_dispatcher.register(MultiInstanceIdentifier, self._read_multi_instance_single_object) self.read_dispatcher.register(ParametricImageIdentifier, self._read_parametric_input) self.read_dispatcher.register(VideoFrameIdentifier, self._read_video_frame) self.multi_infer = False self.validate_config(config, data_source) self.configure()
def __init__(self, func): if not callable(func) and not hasattr(func, "__get__"): raise TypeError( "{!r} is not callable or a descriptor".format(func)) self.dispatcher = singledispatch(func) self.func = func
def __call__(self, default): generic = functools.singledispatch(default) @generic.register(self.Interface) def _(first, *args, **kwargs): return getattr(first, default.__name__)(*args, **kwargs) return generic
def __init__(self): # dictionary mapping bhkRigidBody objects to objects imported in Blender; # we use this dictionary to set the physics constraints (ragdoll etc) collision.DICT_HAVOK_OBJECTS = {} # TODO [collision][havok][property] Need better way to set this, maybe user property if NifData.data._user_version_value_._value == 12 and NifData.data._user_version_2_value_._value == 83: self.HAVOK_SCALE = consts.HAVOK_SCALE * 10 else: self.HAVOK_SCALE = consts.HAVOK_SCALE self.process_bhk = singledispatch(self.process_bhk) self.process_bhk.register(NifFormat.bhkTransformShape, self.import_bhktransform) self.process_bhk.register(NifFormat.bhkRigidBodyT, self.import_bhk_ridgidbody_t) self.process_bhk.register(NifFormat.bhkRigidBody, self.import_bhk_ridgid_body) self.process_bhk.register(NifFormat.bhkBoxShape, self.import_bhkbox_shape) self.process_bhk.register(NifFormat.bhkSphereShape, self.import_bhksphere_shape) self.process_bhk.register(NifFormat.bhkCapsuleShape, self.import_bhkcapsule_shape) self.process_bhk.register(NifFormat.bhkConvexVerticesShape, self.import_bhkconvex_vertices_shape) self.process_bhk.register(NifFormat.bhkPackedNiTriStripsShape, self.import_bhkpackednitristrips_shape) self.process_bhk.register(NifFormat.bhkNiTriStripsShape, self.import_bhk_nitristrips_shape) self.process_bhk.register(NifFormat.NiTriStripsData, self.import_nitristrips) self.process_bhk.register(NifFormat.bhkMoppBvTreeShape, self.import_bhk_mopp_bv_tree_shape) self.process_bhk.register(NifFormat.bhkListShape, self.import_bhk_list_shape) self.process_bhk.register(NifFormat.bhkSimpleShapePhantom, self.import_bhk_simple_shape_phantom)
def __init__(self, source: FilesystemVertex, target: FilesystemVertex, *, templating: BaseTemplating, cost: PolynomialComplexity = On) -> None: """ @param source Source filesystem vertex @param target Target filesystem vertex @param templating Templating engine that provides the script @param cost Route cost NOTE The templating engine that is injected into as instance of this class MUST define a template named "script", in which you may use the following variables, of type Date, which have .filesystem and .address attributes: * source Source data * target Target data """ # TODO Subclass this, rather than relying on runtime checks assert "script" in templating.templates self._templating = templating self.payload = [] self.cost = cost super().__init__(source, target, directed=True) # TODO From Python 3.8 there will be a singledispatchmethod # decorator; move to this when it's available, rather than using # the following workaround self.plan = singledispatch(self._plan_by_data_generator) self.plan.register(DataGenerator, self._plan_by_data_generator) self.plan.register(str, self._plan_by_query)
def __init__(self, *args, **kwargs): InstanceCounterMixin.__init__(self) self.data = defaultdict(None) self.required = kwargs.pop("required", False) # N.B. ``functools.singledispatch()`` dispatches on the type of the # first argument of a function, so we can't use decorator syntax on # a method (unless it's a staticmethod). Instead we use it in # functional form on a bound method. self.convert = functools.singledispatch(self._convert) self.convert.register(type(None), self._convert_none) self.convert.register(str, self._convert_str) self.unconvert = functools.singledispatch(self._unconvert) self.unconvert.register(type(None), self._unconvert_none) self._init(*args, **kwargs)
def register_symbolic(f): # TODO: don't use singledispatch if it has already been done f = singledispatch(f) @f.register(Symbolic) def _dispatch_symbol(__data, *args, **kwargs): return create_sym_call(f, __data.source, *args, **kwargs) return f
def __init__(self): self.visit = singledispatch(self.visit) self.visit.register(ast.If, self.visit_if) self.visit.register(ast.Conditional, self.visit_conditional) self.visit.register(ast.Equality, self.visit_equality) self.visit.register(ast.UnaryOp, self.visit_unary) self.visit.register(ast.Constant, self.visit_constant) self.visit.register(ast.ID, self.visit_id) self.visit.register(ast.Assignment, self.visit_assignment)
def __init__(self, config=None): self.config = config self.data_source_is_dir = True self.data_source_optional = False self.read_dispatcher = singledispatch(self.read) self.read_dispatcher.register(list, self._read_list) self.validate_config() self.configure()
def __init__(self, string, *, key=None, reverse=False): self.natural_key = singledispatch(self.natural_key) self.natural_key.register(str, self._natural_key_str) self.out = [] if key is None: key = self.natural_key print(f"Input string: {string}") self.out = sorted(string, key=key, reverse=reverse)
def method_dispatch(func): # from "https://stackoverflow.com/questions/24601722/how-can-i-use-functools-singledispatch-with-instance-methods" dispatcher = singledispatch(func) def wrapper(*args, **kw): return dispatcher.dispatch(args[1].__class__)(*args, **kw) wrapper.register = dispatcher.register update_wrapper(wrapper, func) return wrapper
def __init__(self, func): if not callable(func) and not hasattr(func, "__get__"): raise TypeError(f"{func!r} is not callable or a descriptor" ) # pragma: no cover import pdb pdb.foo = True self.dispatcher = singledispatch(func) self.func = func
def dispatcher(func): __dispatcher = singledispatch(func) @wraps(func) def wrapper(*args, **kw): return __dispatcher.dispatch(args[1].__class__)(*args, **kw) wrapper.register = __dispatcher.register return wrapper
def method_dispatch(method): dispatcher = functools.singledispatch(method) @functools.wraps(method) def wrapper(*args, **kwargs): return dispatcher.dispatch(args[1].__class__)(*args, **kwargs) wrapper.register = dispatcher.register return wrapper
def __init__(self, data_source, config=None, **kwargs): self.config = config self.data_source = data_source self.read_dispatcher = singledispatch(self.read) self.read_dispatcher.register(list, self._read_list) self.read_dispatcher.register(ClipIdentifier, self._read_clip) self.validate_config() self.configure()
def method_dispatch(func): dispatcher = functools.singledispatch(func) def wrapper(*args, **kwargs): return dispatcher.dispatch(args[1].__class__)(*args, **kwargs) wrapper.register = dispatcher.register functools.update_wrapper(wrapper, func) return wrapper
def methoddispatch(func): """From https://stackoverflow.com/a/24602374""" dispatcher = singledispatch(func) def wrapper(*args, **kw): return dispatcher.dispatch(args[1].__class__)(*args, **kw) wrapper.register = dispatcher.register update_wrapper(wrapper, func) return wrapper
def method_dispatch(func): # wrap the function with a dispatcher dispatcher = functools.singledispatch(func) def wrapper(*args, **kw): return dispatcher.dispatch(args[0].__class__)(*args, **kw) wrapper.register = dispatcher.register functools.update_wrapper(wrapper, func) return wrapper
def _mutator(func): wrapped = singledispatch(func) @wraps(wrapped) def wrapper(initial, event): initial = initial or domain_class return wrapped.dispatch(type(event))(initial, event) wrapper.register = wrapped.register return wrapper
def dispatch(func): """ Like `functools.singledispatch` but for class methods. http://stackoverflow.com/a/24602374/722424 """ dispatcher = functools.singledispatch(func) def wrapper(*args, **kw): return dispatcher.dispatch(args[1].__class__)(*args, **kw) wrapper.register = dispatcher.register functools.update_wrapper(wrapper, func) return wrapper
def method_dispatch(func): """Dispatch class instance method with functools.singledispatch.""" # Note thanks for Zero Piraeus at Stackoverflow: # https://stackoverflow.com/questions/24601722/how-can-i-use-functools-singledispatch-with-instance-methods dispatcher = singledispatch(func) def wrapper(*args, **kw): return dispatcher.dispatch(args[1].__class__)(*args, **kw) wrapper.register = dispatcher.register wrapper.dispatch = dispatcher.dispatch update_wrapper(wrapper, func) return wrapper
def dispatch(func): dispatcher = singledispatch(func) def wrapper(*args, **kw): return dispatcher.dispatch(args[1].__class__)(*args, **kw) def register(cls, func=None): if isinstance(cls, EObject): return dispatcher.register(cls.python_class) return dispatcher.register(cls) wrapper.register = register update_wrapper(wrapper, func) return wrapper
def _single_dispatch_method(method): dispatcher = functools.singledispatch(method) def wrapper(*args, **kwargs): if len(args) > 1: return dispatcher.dispatch(args[1].__class__)(*args, **kwargs) else: # An args tuple with only one element means that the user # is trying to lookup a property of the last answer. return args[0] wrapper.register = dispatcher.register functools.update_wrapper(wrapper, method) return wrapper
def __init__(self, *args, **kwargs): super(MergeDict, self).__init__(*args, **kwargs) # register singlesingle dispatch methods self.merge_value = singledispatch(self.merge_value) # python 2 version if sys.version_info[0] < 3: # pragma: no cover for name, val in inspect.getmembers(self.__class__): _type = getattr(val, 'merge_dispatch', None) if _type: self.merge_value.register(_type, val.__func__) return # python 3 version for name, val in inspect.getmembers(self.__class__, inspect.isfunction): _type = getattr(val, 'merge_dispatch', None) if _type: self.merge_value.register(_type, val)
def _assertdispatch(func): """Adapted `@singledispatch` for custom assertions * Works with methods instead of functions * Keeps track of the data structure via context stack * Detects objects which can be used with `pb.save` and `pb.load` """ dispatcher = singledispatch(func) def wrapper(self, actual, expected, context=None): if context is not None: self.stack.append(context) is_pb_savable = any(hasattr(actual, s) for s in ['__getstate__', '__getinitargs__']) kind = pb.save if is_pb_savable else actual.__class__ dispatcher.dispatch(kind)(self, actual, expected) if context is not None and self.stack: self.stack.pop() wrapper.register = dispatcher.register update_wrapper(wrapper, func) return wrapper
def methodispatch(func): """ New dispatch decorator that looks at the second argument to avoid self Parameters ---------- func : Object Function object to be dispatched Returns wrapper : Object Wrapped function call chosen by the dispatcher ---------- """ dispatcher = singledispatch(func) def wrapper(*args, **kw): return dispatcher.dispatch(args[1].__class__)(*args, **kw) wrapper.register = dispatcher.register update_wrapper(wrapper, dispatcher) return wrapper
return tornado.platform.asyncio.to_tornado_future(asyncio_future) .. versionadded:: 4.1 """ # Lists and dicts containing YieldPoints were handled earlier. if isinstance(yielded, (list, dict)): return multi(yielded) elif is_future(yielded): return yielded elif isawaitable(yielded): return _wrap_awaitable(yielded) else: raise BadYieldError("yielded unknown object %r" % (yielded,)) if singledispatch is not None: convert_yielded = singledispatch(convert_yielded) try: # If we can import t.p.asyncio, do it for its side effect # (registering asyncio.Future with convert_yielded). # It's ugly to do this here, but it prevents a cryptic # infinite recursion in _wrap_awaitable. # Note that even with this, asyncio integration is unlikely # to work unless the application also configures AsyncIOLoop, # but at least the error messages in that case are more # comprehensible than a stack overflow. import tornado.platform.asyncio except ImportError: pass else: # Reference the imported module to make pyflakes happy.
from hashlib import md5 from uuid import UUID def _any_type(value): try: meth = value.persistent_hash except AttributeError: raise TypeError( f"un(persistently-)hashable type: {type(value)!r}") from None return meth() persistent_hash = functools.singledispatch(_any_type) @persistent_hash.register(type(None)) def _none(value): return _str('__edgedb__NONE__') @persistent_hash.register(str) def _str(value): return int(md5(value.encode()).hexdigest(), 16) @persistent_hash.register(bytes) def _bytes(value): return int(md5(value).hexdigest(), 16)
def dispatchmethod(func): """ This provides for a way to use ``functools.singledispatch`` inside of a class. It has the same basic interface that ``singledispatch`` does: >>> class A: ... @dispatchmethod ... def handle_message(self, message): ... # Fallback code... ... pass ... @handle_message.register(int) ... def _(self, message): ... # Special int handling code... ... pass ... >>> a = A() >>> a.handle_message(42) # Runs "Special int handling code..." Note that using ``singledispatch`` in these cases is impossible, since it tries to dispatch on the ``self`` argument, not the ``message`` argument. This is technically a double dispatch, since both the type of ``self`` and the type of the second argument are used to determine what function to call - for example: >>> class A: ... @dispatchmethod ... def handle_message(self, message): ... print('A other', message) ... pass ... @handle_message.register(int) ... def _(self, message): ... print('A int', message) ... pass ... >>> class B: ... @dispatchmethod ... def handle_message(self, message): ... print('B other', message) ... pass ... @handle_message.register(int) ... def _(self, message): ... print('B int', message) ... >>> def do_stuff(A_or_B): ... A_or_B.handle_message(42) ... A_or_B.handle_message('not an int') On one hand, either the ``dispatchmethod`` defined in ``A`` or ``B`` is used depending upon what object one passes to ``do_stuff()``, but on the other hand, ``do_stuff()`` causes different versions of the dispatchmethod (found in either ``A`` or ``B``) to be called (both the fallback and the ``int`` versions are implicitly called). Note that this should be fully compatable with ``singledispatch`` in any other respects (that is, it exposes the same attributes and methods). """ dispatcher = singledispatch(func) def register(type, func=None): if func is not None: return dispatcher.register(type, func) else: def _register(func): return dispatcher.register(type)(func) return _register def dispatch(type): return dispatcher.dispatch(type) def wrapper(inst, dispatch_data, *args, **kwargs): cls = type(dispatch_data) impl = dispatch(cls) return impl(inst, dispatch_data, *args, **kwargs) wrapper.register = register wrapper.dispatch = dispatch wrapper.registry = dispatcher.registry wrapper._clear_cache = dispatcher._clear_cache update_wrapper(wrapper, func) return wrapper