From 8e01d4287b716f1513a33397074ae6e085ecbe4c Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Sun, 30 Mar 2025 12:28:11 +0100 Subject: [PATCH 1/3] DRIVERS.md: Add note re Delay_ms.deinit(). --- v3/docs/DRIVERS.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/v3/docs/DRIVERS.md b/v3/docs/DRIVERS.md index d1570b8..aebb691 100644 --- a/v3/docs/DRIVERS.md +++ b/v3/docs/DRIVERS.md @@ -1070,7 +1070,9 @@ Synchronous methods: the `Task` instance. This allows the `Task` to be cancelled or awaited. 6. `callback` args `func=None`, `args=()`. Allows the callable and its args to be assigned, reassigned or disabled at run time. - 7. `deinit` No args. Cancels the running task. See [Object scope](./TUTORIAL.md#44-object-scope). + 7. `deinit` No args. Cancels the running task. To avoid a memory leak this + should be called before allowing a `Delay_ms` object to go out of scope. See + [Object scope](./TUTORIAL.md#44-object-scope). 8. `clear` No args. Clears the `Event` described in `wait` below. 9. `set` No args. Sets the `Event` described in `wait` below. From 4001e28f402b9567d9e01318ba2e30d28151d6d1 Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Thu, 8 May 2025 18:35:17 +0100 Subject: [PATCH 2/3] broker.py: Provide instance. Pub message defaults to None. --- v3/docs/DRIVERS.md | 40 ++++++++++++++++-------------- v3/primitives/__init__.py | 1 + v3/primitives/broker.py | 9 ++++--- v3/primitives/tests/broker_test.py | 4 +-- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/v3/docs/DRIVERS.md b/v3/docs/DRIVERS.md index aebb691..dcaf218 100644 --- a/v3/docs/DRIVERS.md +++ b/v3/docs/DRIVERS.md @@ -1139,14 +1139,15 @@ finally: # 9. Message Broker ```python -from primitives import Broker # broker.py +from primitives import Broker, broker # broker.py ``` The `Broker` class provides a flexible means of messaging between running tasks. It uses a publish-subscribe model (akin to MQTT) whereby the transmitting task publishes to a topic. Objects subscribed to that topic will receive the message. This enables one to one, one to many, many to one or many to many messaging. -A task subscribes to a topic via an `agent`. This is stored by the broker. When +A task subscribes to a topic via an `agent`: this term describes a set of Python +types which may be used in this role. An `agent` is stored by the broker. When the broker publishes a message, every `agent` subscribed to the message topic will be triggered. In the simplest case the `agent` is a `Queue` instance: the broker puts the topic and message onto the subscriber's queue for retrieval. @@ -1159,6 +1160,12 @@ no "knowledge" of the number or type of agents subscribed to a topic. The module is not threadsafe: `Broker` methods should not be called from a hard ISR or from another thread. +A `Broker` instance `broker` is provided. Where multiple modules issue +```python +from primitives import broker +``` +all will see the same instance, facilitating message passing between modules. + #### Broker methods All are synchronous. @@ -1168,12 +1175,17 @@ with a matching `topic`. Any additional args will be passed to the `agent` when it is triggered. * `unsubscribe(topic, agent, *args)` The `agent` will stop being triggered. If args were passed on subscription, the same args must be passed. -* `publish(topic, message)` All `agent` instances subscribed to `topic` will be -triggered, receiving `topic` and `message` plus any further args that were -passed to `subscribe`. +* `publish(topic, message=None)` All `agent` instances subscribed to `topic` +will be triggered, receiving `topic` and `message` plus any further args that +were passed to `subscribe`. The `topic` arg is typically a string but may be any hashable object. A -`message` is an arbitrary Python object. +`message` is an arbitrary Python object. Where string topics are used, wildcard +subscriptions are possible. + +#### Broker class variable + +* `Verbose=True` Enables printing of debug messages. #### Agent types @@ -1198,16 +1210,11 @@ Note that synchronous `agent` instances must run to completion quickly otherwise the `publish` method will be slowed. See [Notes](./DRIVERS.md#93-notes) for further details on queue behaviour. -#### Broker class variable - -* `Verbose=True` Enables printing of debug messages. - #### example ```py import asyncio -from primitives import Broker, RingbufQueue +from primitives import broker, RingbufQueue -broker = Broker() async def sender(t): for x in range(t): await asyncio.sleep(1) @@ -1245,11 +1252,10 @@ The following illustrates a use case for passing args to an `agent` (pin nos. are for Pyoard 1.1). ```py import asyncio -from primitives import Broker +from primitives import broker from machine import Pin red = Pin("A13", Pin.OUT, value=0) # Pin nos. for Pyboard V1.1 green = Pin("A14", Pin.OUT, value=0) -broker = Broker() async def flash(): broker.publish("led", 1) @@ -1275,9 +1281,8 @@ asyncio.run(main()) A task can wait on multiple topics using a `RingbufQueue`: ```python import asyncio -from primitives import Broker, RingbufQueue +from primitives import broker, RingbufQueue -broker = Broker() async def receiver(): q = RingbufQueue(10) @@ -1316,9 +1321,8 @@ should run to completion quickly. ```py import asyncio -from primitives import Broker, Agent +from primitives import broker, Agent -broker = Broker() class MyAgent(Agent): def put(sef, topic, message, arg): print(f"User agent. Topic: {topic} Message: {message} Arg: {arg}") diff --git a/v3/primitives/__init__.py b/v3/primitives/__init__.py index fe15c4f..ceaad77 100644 --- a/v3/primitives/__init__.py +++ b/v3/primitives/__init__.py @@ -54,6 +54,7 @@ def _handle_exception(loop, context): "Keyboard": "sw_array", "SwArray": "sw_array", "Broker": "broker", + "broker": "broker", "Agent": "broker", "RegExp": "broker", } diff --git a/v3/primitives/broker.py b/v3/primitives/broker.py index 4ee9b37..73072bb 100644 --- a/v3/primitives/broker.py +++ b/v3/primitives/broker.py @@ -1,6 +1,6 @@ # broker.py A message broker for MicroPython -# Copyright (c) 2024 Peter Hinch +# Copyright (c) 2024-2025 Peter Hinch # Released under the MIT License (MIT) - see LICENSE file # Inspired by the following @@ -11,7 +11,7 @@ import re -class Agent: +class Agent: # ABC for user agent pass @@ -58,7 +58,7 @@ def unsubscribe(self, topic, agent, *args): elif Broker.Verbose: print(f"Unsubscribe topic {topic} fail: topic not subscribed.") - def publish(self, topic, message): + def publish(self, topic, message=None): agents = set() # Agents which are triggered by this topic if isinstance(topic, str): # Check regexps # Are any keys RegExp instances? @@ -84,3 +84,6 @@ def publish(self, topic, message): res = agent(topic, message, *args) if isinstance(res, type_coro): asyncio.create_task(res) + + +broker = Broker() diff --git a/v3/primitives/tests/broker_test.py b/v3/primitives/tests/broker_test.py index 50fc87c..ad1357e 100644 --- a/v3/primitives/tests/broker_test.py +++ b/v3/primitives/tests/broker_test.py @@ -3,9 +3,7 @@ # import primitives.tests.broker_test import asyncio -from primitives import Broker, Queue, RingbufQueue, RegExp - -broker = Broker() +from primitives import broker, Queue, RingbufQueue, RegExp # Periodically publish messages to two topics async def test(t): From 84c70cbf49ba3b69ffb59fc8f3cb5e0c9f41d4b0 Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Thu, 15 May 2025 10:50:04 +0100 Subject: [PATCH 3/3] INTERRUPTS.md: fix garbled text. --- v3/docs/INTERRUPTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3/docs/INTERRUPTS.md b/v3/docs/INTERRUPTS.md index 138a2b0..a2c684c 100644 --- a/v3/docs/INTERRUPTS.md +++ b/v3/docs/INTERRUPTS.md @@ -8,7 +8,7 @@ interrupts in `asyncio` applications. Writing an interrupt service routine (ISR) requires care: see the [official docs](https://docs.micropython.org/en/latest/reference/isr_rules.html). There are restrictions (detailed below) on the way an ISR can interface with -`asyncio`. Finally, on many platformasyncioupts are a limited resource. In +`asyncio`. Finally, on many platforms interrupts are a limited resource. In short interrupts are extremely useful but, if a practical alternative exists, it should be seriously considered.