Package: messagio
Welcome to MessagioIt's sort of likepubsub, but evolved.Messagio works out of the box withDjangoandCelery.You can define events, aka messagios, with specific payloads, the messagios themselves are preferably
python dataclasses, and their payloads are pickled, but you can in theory use any class as a messagio.Now you can subscribe any function to listen for those events and do stuff when
an event is received.Any number of functions can subscribe to a single event.
Every function can subscribe to one or more events.
The events are transmitted using celery, so make sure you configureCelery ConfigAs long as celery is configured in your django setup, you don't need to do anything.
However, it might be wise to have a custom queue for your messagio events, to make sure they get the priority they deserve.In django you can configure it as such in djangosettings.pyor your celery settings:CELERY_TASK_ROUTES=([('messagio.tasks.*',{'queue':'messagio'}),],)Don't forget to also set up the queues, for example:CELERY_TASK_QUEUES=(Queue('celery',Exchange('celery'),routing_key='celery'),Queue("messagio",Exchange("messagio"),routing_key="messagio"),)And finally, you need to ensure thatpickleis supported by your Celery config.CELERY_ACCEPT_CONTENT=["pickle"]Keep in mind that if you are usingjsonfor pickling, you need to also addjsonto the array above.Defining MessagiosA messagio is a class that can be pickled, and extends from theMessagioclass.
The best way to create messagios is withdataclasses, so just decorate your class with@dataclass.
Your messagio class needs to extendMessagio, the@dataclassdecorator is optional however.Here are two ways to define messagios:@dataclassclassPerformAction(Messagio):action_name:strobj:anyclassActionPerformed(Messagio):def__init__(action_name:str,obj:any):self.action_name=action_nameself.obj=objAutodiscover the messagios and project structureIn order for your decorated messagio listeners to be respected, you must make sure they are imported at runtime.The simplest way is to use theautodiscover_listenersfunction.For autodiscovery to work your messagio definitions need to follow a certain structure.Place your listener functions in any of the below files or folder structures.- messagio.py

- tasks.py

- messagio
  - __init__.pyThen use the following code somewhere in your project that is always imported,
forDjangoprojects this can be in the mainurls.pyfile.frommessagioimportautodiscover_listenersautodiscover_listeners()Publishing messagiosPublishing messagios is simple, justfireit.obj=Model.objects.get(pk=123)PerformAction("foo_action",obj).fire()Fire accepts certain additional parameters:sync:boolwill call the messagio synchronously without going through celery (usescelery.task.apply)Listening to messagiosAny function can be configured as a listener.
A listener can listen to one or more messagio types and will receive the messagio object itself as the first and only parameter.The subsciption can come with some extra arguments, these are all passed directly to celery.priority: TASK_PRIORITYset a priority that will be passed to celery, if your celery does not use priorities, this is ignored. The priorities are integers and depend on your celery configuration, some default options are available inconst.pyautoretry_for: tuple[Exception]a list of exceptions that trigger an auto-retry, again passed to celerymax_retries: intnumber of times to auto retry if auto retry is enabled, default is no auto retrydefault_retry_delay: floathow long to wait before auto retrying# high priority listener@listen_to_message(PerformAction,priority=10)defhigh_prio_listener(messagio:PerformAction):# do something with your messagiopass@listen_to_message(PerformAction,ActionPerformed)deflistener_of_many(messagio:typing.Union[PerformAction,ActionPerformed]):# do something with your messagiopass# low priority listener@listen_to_message(PerformAction,priority=1)deflog_me(messagio:PerformAction):logging.getLogger("foo").info("Action was performed%s"%messagio.action_name)Messagio functions will be executed by your celery worker and they are executed sequentially in the order that they were subscribed.
A single published messagio will be executed by a single worker and a messagio can be published as many times as you want.You can also subscribe directly using the message centerdefany_func(msg:PerformAction):#do stuffpassMessageCenter.singleton().subscribe(PerformAction,any_function)Abstract message center?If you want, you can roll your own message center that does not use celery to transport messages, for example if you want to use a daemon or some other transport protocol like huey.