{"id":21933149,"url":"https://github.com/olekli/mreventloop","last_synced_at":"2025-07-18T06:33:57.291Z","repository":{"id":210993162,"uuid":"727643767","full_name":"olekli/MrEventLoop","owner":"olekli","description":"Simple event system for Python building on asyncio and working seamlessly across sockets.","archived":false,"fork":false,"pushed_at":"2024-01-13T16:23:54.000Z","size":85,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-18T19:02:44.101Z","etag":null,"topics":["asyncio","event-driven","events","rpc","signals-and-slots"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/olekli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2023-12-05T09:30:57.000Z","updated_at":"2024-09-08T23:23:25.000Z","dependencies_parsed_at":"2024-01-04T13:28:01.430Z","dependency_job_id":null,"html_url":"https://github.com/olekli/MrEventLoop","commit_stats":null,"previous_names":["olekli/mreventloop"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olekli%2FMrEventLoop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olekli%2FMrEventLoop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olekli%2FMrEventLoop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olekli%2FMrEventLoop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/olekli","download_url":"https://codeload.github.com/olekli/MrEventLoop/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249800158,"owners_count":21327018,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asyncio","event-driven","events","rpc","signals-and-slots"],"created_at":"2024-11-29T00:08:20.356Z","updated_at":"2025-04-19T20:57:36.366Z","avatar_url":"https://github.com/olekli.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MrEventLoop\nSimple event system for Python building on `asyncio` and working seamlessly across sockets.\n\n### Installation\n```\npip install mreventloop\n```\n\n### Emitting Events\n```python\nfrom mreventloop import emits\n\n@emits('events', [ 'produced' ])\nclass Producer:\n  def requestProduct(self):\n    self.events.produced('some product')\n```\n\nYou can now connect a callback to the event:\n```python\nfrom mreventloop import connect\n\nproducer = Producer()\nconnect(producer, 'produced', lambda product: print(f'{product}'))\n```\nYou can disconnect all listeners from an event:\n```python\nfrom mreventloop import disconnect\ndisconnect(producer, 'produced')\n```\n\n### Event Loop and Slots\nObjects can have an event loop building on `asyncio`:\n```python\nfrom mreventloop import has_event_loop, slot\n\n@has_event_loop('event_loop')\nclass Consumer:\n  @slot\n  def onProduced(self, product):\n    print(f'{product}')\n```\nCalling a `@slot` will not run the corresponding method directly,\nbut queue its execution on the object's event loop.\nThe event loop will run them strictly sequentially in FIFO order.\nThus, slots do not need to be reentrant.\n\nThe event loop has to be started:\n```python\nconsumer = Consumer()\nconnect(producer, 'produced', consumer, 'onProduced')\nasync with consumer.event_loop:\n  producer.requestProduct()\n```\nAfter exiting, the event loop will still process all queued events.\n\nWhen creating an object that `has_event_loop`,\nan event loop will automatically be created.\nBut you can also share event loops between objects:\n```python\nproducer1 = Producer()\nproducer2 = Producer()\nconsumer1 = Consumer()\nconsumer2 = Consumer()\nconsumer2.event_loop = consumer1.event_loop\nconnect(producer1, 'produced', consumer1, 'onProduced')\nconnect(producer2, 'produced', consumer2, 'onProduced')\nasync with consumer1.event_loop:\n  producer1.requestProduct()\n  producer2.requestProduct()\n```\nAll calls to slots in both `consumer1` and `consumer2` will be run sequentially.\n\n### Async Slots\nSlots can also be coroutines:\n```python\nimport asyncio\n\n@has_event_loop('event_loop')\nclass ConsumerAsync:\n  @slot\n  async def onProduced(self, product):\n    await asyncio.sleep(0.1)\n    print(f'{product}')\n```\nThe coroutine will be awaited inside the event loop.\n\n\n### Awaiting Events\nEvents can be awaited:\n```python\nfrom mreventloop import SyncEvent\n\nproducer = Producer()\nsync_event = SyncEvent(producer.events.produced)\n\nasync def emit():\n  producer.requestProduct()\n\nasyncio.create_task(emit())\nresult = await sync_event\nprint(f'{result}')\n```\n\n### Events of the Event Loop\nThe event loop itself emits the following events:\n```\n[ 'started', 'stopped', 'active', 'idle', 'exception' ]\n```\n\n### Exceptions on the Event Loop\nIf an exception ocurrs executing a slot, the app will exit.\nThis can be prevented by creating the loop manually with:\n```python\nfrom mreventloop import EventLoop\n\nconsumer.event_loop = EventLoop(exit_on_exception = False)\n```\nIn this case, any exception will be emitted through the `exception` event.\n\n(The general idea here is to just not use exceptions.)\n\n\n### Crossing Sockets\nEvents and slots across multiple applications can be connected via sockets.\nThis requires one instance of a `Broker` and any number of `Peer`s.\nA `Peer` subscribes to a number of events.\nIf any other `Peer` publishes such events,\nthey will be emitted by the subscribing `Peer`.\n\n```python\nfrom mreventloop import Broker, Peer\n\nin_socket_path = 'ipc:///tmp/mreventloop_test_in.sock'\nout_socket_path = 'ipc:///tmp/mreventloop_test_out.sock'\n\nproducer_peer = Peer(in_socket_path, out_socket_path, [ 'request_product' ], [ 'produced' ])\nconsumer_peer = Peer(in_socket_path, out_socket_path, [ 'produced' ], [ 'request_product' ])\nasync with Broker(in_socket_path, out_socket_path), producer_peer, consumer_peer:\n  consumer = Consumer()\n  connect(consumer, 'request_product', consumer_peer.publish, 'request_product')\n  connect(consumer_peer, 'product', consumer, 'onProduct')\n\n  producer = Producer()\n  connect(producer, 'produce', producer_peer.publish, 'produce')\n  connect(producer_peer, 'request_product', producer, 'onRequestProduct')\n\n  async with producer.event_loop, consumer.event_loop:\n    consumer.events.request_product()\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folekli%2Fmreventloop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Folekli%2Fmreventloop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folekli%2Fmreventloop/lists"}