{"id":2682,"library":"pydispatcher","title":"PyDispatcher","description":"PyDispatcher is a multi-producer, multi-consumer in-memory signal dispatch system for Python. It provides a robust mechanism for event routing, originally extracted from Django's dispatch module. Primarily maintained by Mike Fletcher, the library is currently at version 2.0.7 and sees infrequent but ongoing updates.","status":"active","version":"2.0.7","language":"en","source_language":"en","source_url":"https://github.com/mcfletch/pydispatcher","tags":["dispatch","signal","event","observer","loose-coupling","pubsub"],"install":[{"cmd":"pip install pydispatcher","lang":"bash","label":"Install PyDispatcher"}],"dependencies":[],"imports":[{"note":"The primary API is exposed through the `dispatcher` object, imported directly from the `pydispatch` package.","wrong":"import pydispatcher","symbol":"dispatcher","correct":"from pydispatch import dispatcher"}],"quickstart":{"code":"from pydispatch import dispatcher\n\n# Define a signal name (can be any hashable object)\nMY_SIGNAL = 'my_custom_event'\n\ndef my_receiver(sender, **kwargs):\n    \"\"\"A function to receive the signal.\"\"\"\n    print(f\"Receiver got signal from {sender}: {kwargs}\")\n\ndef another_receiver(sender, data, event_type, **kwargs):\n    \"\"\"Another receiver that expects specific keyword args.\"\"\"\n    print(f\"Another receiver got signal from {sender}: data={data}, type={event_type}\")\n\nclass MySender:\n    def __init__(self, name):\n        self.name = name\n\n    def trigger_event(self):\n        print(f\"\\n{self.name} is sending MY_SIGNAL...\")\n        # Send the signal, specifying the sender and any keyword arguments\n        dispatcher.send(signal=MY_SIGNAL, sender=self, data='important data', event_type='click')\n\n# Connect receivers to the signal\n# Use dispatcher.Any to receive signals from any sender\ndispatcher.connect(receiver=my_receiver, signal=MY_SIGNAL, sender=dispatcher.Any)\ndispatcher.connect(receiver=another_receiver, signal=MY_SIGNAL, sender=MySender)\n\n# Create a sender instance and trigger the event\nsender_instance = MySender(name=\"ComponentA\")\nsender_instance.trigger_event()\n\n# Disconnect a receiver (optional)\ndispatcher.disconnect(receiver=my_receiver, signal=MY_SIGNAL, sender=dispatcher.Any)","lang":"python","description":"This example demonstrates how to define a signal, connect multiple receiver functions to it (including one that expects specific keyword arguments), and send the signal from a custom sender object. It also shows how to disconnect a receiver."},"warnings":[{"fix":"Always define receiver functions with the signature `def receiver_func(sender, **kwargs):` to ensure forward compatibility and prevent argument mismatch errors. Extract specific arguments from `kwargs` inside the function body.","message":"Receiver functions must accept `sender` as the first positional argument and `**kwargs` for any additional arguments. Failing to include `**kwargs` can cause runtime errors if the signal sender later provides arguments not explicitly defined in the receiver's signature.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Explicitly define the `sender` argument in both `dispatcher.connect` and `dispatcher.send` to match desired behavior. Use `dispatcher.Any` to listen for all senders, and specify a concrete sender object or `dispatcher.Anonymous` when sending.","message":"The `dispatcher.connect` method defaults to listening for signals from `dispatcher.Any` sender, while `dispatcher.send` defaults to sending signals with `dispatcher.Anonymous` sender. This difference can lead to signals not being received if not explicitly specified.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Pass all data to `dispatcher.send` using keyword arguments (e.g., `signal=MY_SIGNAL, sender=self, data='value'`). This improves clarity and avoids potential mismatches with receiver function signatures.","message":"While `dispatcher.send` accepts positional arguments, it is generally recommended to use keyword arguments for all data passed with a signal. Positional arguments can lead to conflicts with receiver signatures if not carefully managed.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Avoid disconnecting receivers dynamically within the signal processing chain. If necessary, defer disconnection until after all signal processing for the current event cycle has completed.","message":"Disconnecting a receiver within another receiver's execution flow might prevent subsequent receivers for the same signal from being called.","severity":"breaking","affected_versions":"2.0.x (Reported in 2.0.7, issue #8 on GitHub)"},{"fix":"Be mindful when using keyword-only arguments in receiver functions. Test thoroughly if your signal handlers depend on this feature. Where possible, define keyword arguments with defaults or handle them within `**kwargs`.","message":"There's an open issue where PyDispatcher's internal `robustApply` module may not fully support Python's keyword-only arguments, which could impact how signal arguments are processed if receivers rely heavily on them.","severity":"gotcha","affected_versions":"2.0.x (Reported in 2.0.7, issue #6 on GitHub)"}],"env_vars":null,"last_verified":"2026-04-10T00:00:00.000Z","next_check":"2026-07-09T00:00:00.000Z"}