eclipse / paho.mqtt.python

paho.mqtt.python
Other
2.12k stars 722 forks source link

Migrating from mqtt Paho V1 to V2 reinitialise fails #844

Open MarcEngrie opened 1 month ago

MarcEngrie commented 1 month ago

Question

On a reconnect, on_reconnect calls my reconnect function where I reinitialise the connection calling the reinitialise method of the client

mqttc.reinitialise(mqtt_clientID, clean_session=False, userdata=None)

However, this triggers an error

File "/usr/local/lib/python3.11/dist-packages/paho/mqtt/client.py", line 1156, in reinitialise
    self.__init__(client_id, clean_session, userdata)  # type: ignore[misc]
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/paho/mqtt/client.py", line 772, in __init__
    raise ValueError(
ValueError: Unsupported callback API version: version 2.0 added a callback_api_version, see docs/migrations.rst for details

Searching online for information I do find the migration information but nothing is mentioned about this method. Looking for the method, I only find info in the documentation indicating I do the right thing. (cfr: https://eclipse.dev/paho/files/paho.mqtt.python/html/client.html)

I even tried this

mqttc.reinitialise(mqtt.CallbackAPIVersion.VERSION2, mqtt_clientID, clean_session=False, userdata=None)

but that triggers an error saying clean_session is mentioned twice.

What is wrong here? Where can I find information on the V2 reinitialise method?

Environment

MarcEngrie commented 1 month ago

Reading through some other issues here, I think my problem is related to #842 & #841.

JamesParrott commented 1 month ago

Perhaps the reason there's so little documentation for .reinitialise, and for there being no mention of it in: https://github.com/eclipse/paho.mqtt.python/blob/master/docs/migrations.rst is that .reinitialise should not be used for migrations between API versions.

Create a new client from scratch with the new API enum.

The first error does indeed occur because issue #841 has not been fixed.

In the second attempt, you're calling a method with 4 arguments that expects 3. But your arg no. 3 is the kwarg that is also set by positional arg 2.

My PR's not included yet, but it should preserve the callback_api_version that is already set. It does not alter the .reinitialise call signature however. The call to that with 4 args would still be incorrect.

MarcEngrie commented 1 month ago

Thx. OK, clear. So I understand I should no longer use .reinitiliase when going for V2 of the client. But can you point me to an example that shows the best pratice on what to do when a disconnect happens (in V2)? Or do I misunderstand something here?

JamesParrott commented 1 month ago

You can use .reinitialise for V1. You can use .reinitialise for V2.

Don't try to use .reinitialise to change a V1 Client into a V2 Client (nor vice-versa) (destroy the Client and make a new one).

MarcEngrie commented 1 month ago

OK but that's not what I am doing (I think) as I do create a V2 client

    mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, mqtt_clientID, clean_session=False, userdata=None)
    if mqtt_login != "":
        mqttc.username_pw_set(mqtt_login, mqtt_password)
    mqttc.will_set(mqtt_topicsts, payload="offline", qos=1, retain=True)
    # Assign event callbacks
    mqttc.on_connect     = mqtt_on_connect
    mqttc.on_publish     = mqtt_on_publish
    mqttc.on_subscribe   = mqtt_on_subscribe
    mqttc.on_message     = mqtt_on_message
    mqttc.on_disconnect  = mqtt_on_disconnect
    mqttc.connected_flag = False
    try:
        mqttc.connect(mqtt_server, port=mqtt_port, keepalive=360)
JamesParrott commented 1 month ago

Ah I misunderstood - sorry.

You're creating the Client correctly. However "the V2 reinitialise method" (and the V1 alike) is broken due to #841.

the precise way it breaks is because a Value Error is raised inside __init__ when the arg signature is the old V1 signature. The author intended to raise an informative error within __init__, to help the user. But unfortunately incorrectly assumed that the only way this would happen, was if a user called Client(...) with the old V1 signature (with a client_id string in place of an enum). .reinitialise also uses the old V1 signature, and has not yet been updated for the V2 client.