Proper way to deal with external objects with functions in Python

Question:

This implementation of the AWS MQTT broker is confusing me.
In the code, there is this function definition:

def on_message_received(topic, payload, **kwargs):
    print("Received message from topic '{}': {}".format(topic, payload))
    global received_count
    received_count += 1
    if received_count == args.count:
        received_all_event.set()

And then the function is called like this:

subscribe_future, packet_id = mqtt_connection.subscribe(
    topic=args.topic,
    qos=mqtt.QoS.AT_LEAST_ONCE,
    callback=on_message_received
)
subscribe_result = subscribe_future.result()

There are two things which are confusing me:

  1. Why the on_message_received function is called without parameters?
  2. If I want to pass a variable to on_message_received and do something to it from within that function, which would be the correct approach?

About point (2), consider the following example, which seems to be working:

last_message = ''

def on_message_received(topic, payload, **kwargs):
    print("Received message from topic '{}': {}".format(topic, payload))
    last_message = payload

subscribe_future, packet_id = mqtt_connection.subscribe(
    topic=args.topic,
    qos=mqtt.QoS.AT_LEAST_ONCE,
    callback=on_message_received
)
subscribe_result = subscribe_future.result()

But I don’t think it’s the correct approach. But I don’t know how I should pass the external variable to the function.

Asked By: espogian

||

Answers:

The example you posted doesn’t actually work because last_message is a local variable — you can’t access it from outside the function. I’d suggest putting it in a handler object, like this:

class MessageHandler:
    def __init__(self):
        self.last_message = ''

    def on_message_received(self, topic, payload, **_kwargs):
        print(f"Received message from topic '{topic}': {payload}")
        self.last_message = payload

handler = MessageHandler()
subscribe_future, packet_id = mqtt_connection.subscribe(
    topic=args.topic,
    qos=mqtt.QoS.AT_LEAST_ONCE,
    callback=handler.on_message_received
)
subscribe_result = subscribe_future.result()
print("Last message:", handler.last_message)

The on_message_received function is not called by your code, it is passed as an argument to mqtt_connection.subscribe. The API is then what will call it (later, when there’s a message), and at that time it should provide the topic and payload args to your function.

Calling subscribe_future.result() causes your program to stop and wait for a message to be received (i.e. it waits for the Future object to produce a result). Note that in real life you usually don’t want to immediately block on a future (because the point of a future is that it’s something that will happen in the future and you probably have other things to do while you wait for that future to happen).

Answered By: Samwise
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.