Skip to content

Commit

Permalink
Add version 2 of user-callback
Browse files Browse the repository at this point in the history
This allow to access reason code & properties on on_publish
  • Loading branch information
PierreF committed Jan 14, 2024
1 parent a63b1dc commit 3bbd0d0
Show file tree
Hide file tree
Showing 40 changed files with 901 additions and 238 deletions.
6 changes: 6 additions & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
v2.0.0 - 2023-xx-xx
===================

- **BREAKING** Added callback_api_version. This break *ALL* users of paho-mqtt Client class.
See migrations.md for details on how to upgrade.
tl; dr; add CallbackAPIVersion.VERSION1 to first argument of Client()
- **BREAKING** Drop support for Python 2.7, Python 3.5 and Python 3.6
Minimum tested version is Python 3.7
Python version up to Python 3.12 are tested.
- **BREAKING** connect_srv changed it signature to take an additional bind_port parameter.
This is a breaking change, but in previous version connect_srv was broken anyway.
Closes #493.
- Add version 2 of user-callback which allow to access MQTTv5 reason code & properties that were
missing from on_publish callback. Also it's more consistent in parameter order or between
MQTTv3 and MQTTv5.
- Add types to Client class, which caused few change which should be compatible.
Known risk of breaking changes:
- Use enum for returned error code (like MQTT_ERR_SUCCESS). It use an IntEnum
Expand Down
189 changes: 189 additions & 0 deletions migrations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# Migration

## Change between version 1.x and 2.0

### Improved typing

Version 2.0 improved typing, but this would be compatible with existing code.
The most likely issue are some integer that are now better type, like `dup` on MQTTMessage.

That means that code that used `if msg.dup == 1:` will need to be change to `if msg.dup:` (the later version
for with both paho-mqtt 1.x and 2.0).

### Versioned the user callbacks

Version 2.0 of paho-mqtt introduced versioning of user-callback. To fix some inconsistency in callback
arguments and to provide better support for MQTTv5, version 2.0 changed the arguments passed to user-callback.

You can still use old version of callback, you are just require to tell paho-mqtt that you opt for this
version. For that just change your client creation from:
```
# OLD code
>>> mqttc = mqtt.Client()
# NEW code
>>> mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1)
```

That it, remaining of the code could stay unchanged.

The version 1 of callback is deprecated, but is still supported in version 2.x. If you want to upgrade to newer version of API callback, you will need to update your callbacks:

#### on_connect

```
# OLD code for MQTTv3
def on_connect(client, userdata, flags, rc):
if flags["session present"] == 1:
# ...
if rc == 0:
# success connect
if rc > 0:
# error processing
# OLD code for MQTTv5
def on_connect(client, userdata, flags, reason_code, properties):
if flags["session present"] == 1:
# ...
if reason_code == 0:
# success connect
# NEW code for both version
def on_connect(client, userdata, flags, reason_code, properties):
if flags.session_present:
# ...
if reason_code == 0:
# success connect
if reason_code > 0:
# error processing
```

Be careful that for MQTTv3, `rc` (an integer) changed to `reason_code` (an instance of ReasonCodes), and the numeric value changed.
The numeric value 0 means success for both, so as in above example, using `reason_code == 0`, `reason_code != 0` or other comparison with zero
is fine.
But if you had comparison with other value, you will need to update the code. It's recommended to compare to string value:

```
# OLD code for MQTTv3
def on_connect(client, userdata, flags, rc):
if rc == 1:
# handle bad protocol version
if rc == CONNACK_REFUSED_IDENTIFIER_REJECTED:
# handle bad identifier
# NEW code
def on_connect(client, userdata, flags, reason_code, properties):
if rc == "Unsupported protocol version":
# handle bad protocol version
if rc == "Client identifier not valid":
# handle bad identifier
```

#### on_disconnect

```
# OLD code for MQTTv3
def on_disconnect(client, userdata, rc):
if rc == 0:
# success disconnect
if rc > 0:
# error processing
# OLD code for MQTTv5
def on_disconnect(client, userdata, reason_code, properties):
if reason_code:
# error processing
# NEW code for both version
def on_disconnect(client, userdata, flags, reason_code, properties):
if reason_code == 0:
# success disconnect
if reason_code > 0:
# error processing
```


#### on_subscribe

```
# OLD code for MQTTv3
def on_subscribe(client, userdata, mid, granted_qos):
for sub_result in granted_qos:
if sub_result == 1:
# process QoS == 1
if sub_result == 0x80:
# error processing
# OLD code for MQTTv5
def on_disconnect(client, userdata, mid, reason_codes, properties):
for sub_result in reason_codes:
if sub_result == 1:
# process QoS == 1
# Any reason code >= 128 is a failure.
if sub_result >= 128:
# error processing
# NEW code for both version
def on_subscribe(client, userdata, mid, reason_codes, properties):
for sub_result in reason_codes:
if sub_result == 1:
# process QoS == 1
# Any reason code >= 128 is a failure.
if sub_result >= 128:
# error processing
```


#### on_unsubscribe

```
# OLD code for MQTTv3
def on_unsubscribe(client, userdata, mid):
# ...
# OLD code for MQTTv5
def on_unsubscribe(client, userdata, mid, properties, reason_codes):
# In OLD version, reason_codes could be a list or a single ReasonCodes object
if isinstance(reason_codes, list):
for unsub_result in reason_codes:
# Any reason code >= 128 is a failure.
if reason_codes[0] >= 128:
# error processing
else:
# Any reason code >= 128 is a failure.
if reason_codes > 128:
# error processing
# NEW code for both version
def on_subscribe(client, userdata, mid, reason_codes, properties):
# In NEW version, reason_codes is always a list. Empty for MQTTv3
for unsub_result in reason_codes:
# Any reason code >= 128 is a failure.
if reason_codes[0] >= 128:
# error processing
```


#### on_publish

```
# OLD code
def on_publish(client, userdata, mid):
# ...
# NEW code
def on_publish(client, userdata, mid, reason_codes, properties):
# ...
```


#### on_message

No change for this callback.
```
# OLD & NEW code
def on_message(client, userdata, message):
# ...
```
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ disallow_untyped_defs = true
[tool.pytest.ini_options]
addopts = ["-r", "xs"]
testpaths = "tests src"
filterwarnings = [
"ignore:Callback API version 1 is deprecated, update to latest version"
]

[tool.ruff]
exclude = ["test/lib/python/*"]
Expand Down
Loading

0 comments on commit 3bbd0d0

Please sign in to comment.