Search code examples
pythonunit-testingmqttmosquittopaho

paho.mqtt + python TypeError: a bytes-like object is required, not 'str'


Two paho.mqtt clients (client1 and client2) are created in Python, which connects to mosquitto brokers. Two clients subscribe to different topics. client1 runs loop_forever(), and client2 runs loop_start(). client1's on_message() callback stores the message in MySQL database. client2's on_message() callback runs a simple test.

The test contains a test method which uses mock object paho.mqtt.client.MQTTMessage. For client2, I am publishing the message from mosquitto_pub in a command line. when I receive the message to run the test, the test runs successfully. But after that I get this below error message. when I comment the test method which uses this mock object paho.mqtt.client.MQTTMessage for test, I don't get this error message.

Is this error coming from Python or paho.mqtt? or is it from client1.loop_forever() method?

File "/usr/local/lib/python3.6/site-packages/paho_mqtt-1.3.1-py3.6.egg/paho/mqtt/client.py", line 1481, in loop_forever
rc = self.loop(timeout, max_packets)
File "/usr/local/lib/python3.6/site-packages/paho_mqtt-1.3.1-py3.6.egg/paho/mqtt/client.py", line 1003, in loop
rc = self.loop_read(max_packets)
File "/usr/local/lib/python3.6/site-packages/paho_mqtt-1.3.1-py3.6.egg/paho/mqtt/client.py", line 1284, in loop_read
rc = self._packet_read()
File "/usr/local/lib/python3.6/site-packages/paho_mqtt-1.3.1-py3.6.egg/paho/mqtt/client.py", line 1849, in _packet_read
rc = self._packet_handle()
File "/usr/local/lib/python3.6/site-packages/paho_mqtt-1.3.1-py3.6.egg/paho/mqtt/client.py", line 2305, in _packet_handle
return self._handle_publish()
File "/usr/local/lib/python3.6/site-packages/paho_mqtt-1.3.1-py3.6.egg/paho/mqtt/client.py", line 2500, in _handle_publish
self._handle_on_message(message)
File "/usr/local/lib/python3.6/site-packages/paho_mqtt-1.3.1-py3.6.egg/paho/mqtt/client.py", line 2640, in _handle_on_message
for callback in self._on_message_filtered.iter_match(message.topic):
File "/usr/local/lib/python3.6/site-packages/paho_mqtt-1.3.1-py3.6.egg/paho/mqtt/matcher.py", line 60, in iter_match
lst = topic.split('/')
TypeError: a bytes-like object is required, not 'str'

Solution

  • As mentioned in the paho documentation, your topic must be of type bytes:

    class MQTTMessage(object):
    """ This is a class that describes an incoming or outgoing message. It is
    passed to the on_message callback as the message parameter.
    
    Members:
    
    topic : String/bytes. topic that the message was published on.
    payload : String/bytes the message payload.
    qos : Integer. The message Quality of Service 0, 1 or 2.
    retain : Boolean. If true, the message is a retained message and not fresh.
    mid : Integer. The message id.
    
    On Python 3, topic must be bytes.
    """
    
    __slots__ = 'timestamp', 'state', 'dup', 'mid', '_topic', 'payload', 'qos', 'retain', 'info'
    
    def __init__(self, mid=0, topic=b""):
        self.timestamp = 0
        self.state = mqtt_ms_invalid
        self.dup = False
        self.mid = mid
        self._topic = topic
        self.payload = b""
        self.qos = 0
        self.retain = False
        self.info = MQTTMessageInfo(mid)
    
    def __eq__(self, other):
        """Override the default Equals behavior"""
        if isinstance(other, self.__class__):
            return self.mid == other.mid
        return False
    
    def __ne__(self, other):
        """Define a non-equality test"""
        return not self.__eq__(other)
    
    @property
    def topic(self):
        if sys.version_info[0] >= 3:
            return self._topic.decode('utf-8')
        else:
            return self._topic
    
    @topic.setter
    def topic(self, value):
        self._topic = value
    

    -> On Python 3, topic must be bytes.

    So ensure that your topic is a bytes object, then it should work

    BTW, here a short example for string and byte converting:

    mystring = 'hello_string'
    b1 = bytes(mystring, 'utf-8')
    b2 = str(b1, 'utf-8')
    print(b1)
    print(b2)
    

    output is:

    b'hello_string'
    hello_string