Uygulamalı Eğitimler

Paho MQTT 2.0'a Geçiş: CallbackAPIVersion Hatalarını Düzeltme

paho-mqtt 1.x'ten 2.x'e geçin: CallbackAPIVersion ValueError hatasını düzeltin, on_connect ve diğer callback imzalarını güncelleyin, ReasonCode kullanın.

Güncellendi · 8 dk okuma

Paho MQTT 2.0'a Geçiş: CallbackAPIVersion Hatalarını Düzeltme

paho-mqtt 2.0, istemcinin nasıl oluşturulduğunu ve callback'lerinizin ne aldığını değiştirdi. Betiğiniz bir pip install --upgrade sonrasında bozulduysa çözüm neredeyse her zaman aynıdır: mqtt.Client()'a ilk argüman olarak mqtt.CallbackAPIVersion.VERSION2 geçin ve callback imzalarını güncelleyin. Bu rehber tam hata mesajlarını, nelerin değiştiğini ve eksiksiz bir önce/sonra örneğini gösteriyor.

Muhtemelen gördüğünüz hatalar

HataNedenÇözüm
ValueError: Unsupported callback API version: version 2.0 added a callback_api_version, see docs/migrations.rst for detailsClient ID ilk konumsal argüman olarak geçilmiş: mqtt.Client("my-id")mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="my-id")
DeprecationWarning: Callback API version 1 is deprecated, update to latest versionSürüm verilmeden mqtt.Client() ya da VERSION1 ile oluşturmaVERSION2 geçin ve callback'leri güncelleyin
TypeError: on_connect() takes 4 positional arguments but 5 were given1.x tarzı on_connect(client, userdata, flags, rc) ile VERSION2 kullanımıproperties parametresini ekleyin (aşağıya bakın)
TypeError: on_disconnect() takes 3 positional arguments but 5 were givenAynı durum on_disconnect(client, userdata, rc) içindisconnect_flags, reason_code, properties içeren yeni imza
AttributeError: module 'paho.mqtt.client' has no attribute 'CallbackAPIVersion'Kodunuz 2.x için yazılmış ama ortamda hâlâ 1.x varpip install "paho-mqtt>=2.0" çalıştırın ve betiği hangi yorumlayıcının çalıştırdığını kontrol edin

Önce kurulu sürümü kontrol edin — sanal ortamlar ile sistem Python'u çoğu zaman farklıdır:

Hangi paho-mqtt kurulu?
python -c "import paho.mqtt; print(paho.mqtt.__version__)"
pip show paho-mqtt

2.0'da neler değişti

1.x sürümünde argümanları protokol sürümüne göre değişen tek bir callback stili vardı: MQTT 3.1.1 callback'leri tamsayı bir rc, MQTT 5 callback'leri ise reason_code ve properties alıyordu. Bu da bir protokolde çalışıp diğerinde çöken kod yazmayı kolaylaştırıyordu.

2.0 callback API sürümlerini getirir. CallbackAPIVersion.VERSION2 ile her callback MQTT 3.1.1 ve MQTT 5'te aynı imzaya sahiptir, reason code'lar her zaman ReasonCode nesnesidir ve properties her zaman geçilir (protokolde yoksa None olarak). Constructor artık şöyle görünür:

python
mqtt.Client(
    callback_api_version,          # yeni, ilk konumsal argüman
    client_id="",
    clean_session=None,
    userdata=None,
    protocol=mqtt.MQTTv311,
    transport="tcp",
    reconnect_on_failure=True,
    manual_ack=False,
)

Bilinmesi gereken diğer noktalar: 2.x, Python 3.7 veya üstünü gerektirir; publish.single() / subscribe.simple() yardımcıları sürümü sizin yerinize yönetir, bu yüzden yalnızca bunları kullanan kod genellikle çalışmaya devam eder.

Callback imzaları: 1.x ve VERSION2

Callback1.x (MQTT 3.1.1)VERSION2 ile 2.x
on_connect(client, userdata, flags, rc)(client, userdata, flags, reason_code, properties)
on_disconnect(client, userdata, rc)(client, userdata, disconnect_flags, reason_code, properties)
on_subscribe(client, userdata, mid, granted_qos)(client, userdata, mid, reason_code_list, properties)
on_unsubscribe(client, userdata, mid)(client, userdata, mid, reason_code_list, properties)
on_publish(client, userdata, mid)(client, userdata, mid, reason_code, properties)
on_message(client, userdata, message)değişmedi
on_log(client, userdata, level, buf)değişmedi

Pratikte üç ayrıntı önemlidir:

  • flags artık bir dict değil. on_connect içinde bir ConnectFlags nesnesidir: flags["session present"] yerine flags.session_present kullanın.
  • disconnect_flags.is_disconnect_packet_from_server, broker'ın bir DISCONNECT gönderip göndermediğini (MQTT 5) ya da bağlantı kaybını kütüphanenin kendisinin mi tespit ettiğini söyler.
  • on_subscribe düz QoS tamsayıları değil, reason code'lar alır. QoS 1 ile kabul edilen bir abonelikte reason code'un value değeri 1'dir; reddedilen bir aboneliğin değeri 0x80 veya üzeridir, dolayısıyla rc.is_failure true olur.

Reason code'lar artık nesne

VERSION2 ile reason_code bir ReasonCode nesnesidir. Hem bir tamsayıya hem de kendi adına eşit kabul edilir, yazdırıldığında adını gösterir ve bir is_failure özelliğine sahiptir:

python
def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print(f"bağlantı başarısız: {reason_code} ({reason_code.value})")
        return
    print("bağlandı, oturum mevcut:", flags.session_present)

Başarı durumu için reason_code == 0 ve reason_code == "Success" hâlâ çalışır; yani en yaygın kontrol güncellemeden değişmeden çıkar.

Eksiksiz bir önce/sonra örneği

Bu, tipik 1.x kodudur — 2.x'te yukarıdaki ValueError ile bozulur:

client_v1.py — paho-mqtt 1.x
import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
    if rc == 0:
        client.subscribe("testmqtt/paho2/#", qos=1)
    else:
        print("connect failed, rc =", rc)

def on_disconnect(client, userdata, rc):
    print("disconnected, rc =", rc)

def on_message(client, userdata, msg):
    print(msg.topic, msg.payload.decode())

client = mqtt.Client("testmqtt-paho2-demo")
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, 60)
client.loop_forever()

Aynı istemcinin VERSION2'ye taşınmış hali:

client_v2.py — paho-mqtt 2.x
import os
import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print(f"bağlantı başarısız: {reason_code}")
        return
    print("bağlandı, oturum mevcut:", flags.session_present)
    client.subscribe("testmqtt/paho2/#", qos=1)

def on_subscribe(client, userdata, mid, reason_code_list, properties):
    for rc in reason_code_list:
        print("abonelik:", "reddedildi" if rc.is_failure else f"QoS {rc.value} verildi")

def on_disconnect(client, userdata, disconnect_flags, reason_code, properties):
    print(f"bağlantı kesildi: {reason_code} (sunucudan: {disconnect_flags.is_disconnect_packet_from_server})")

def on_message(client, userdata, msg):
    print(msg.topic, msg.payload.decode())

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="testmqtt-paho2-demo")
if os.environ.get("MQTT_USER"):
    client.username_pw_set(os.environ["MQTT_USER"], os.environ.get("MQTT_PASS"))
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_disconnect = on_disconnect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_forever()

Aynı callback'ler protocol=mqtt.MQTTv5 ile de değişmeden çalışır — VERSION2'nin asıl faydası budur. Yalnızca constructor argümanları farklıdır: MQTT 5 clean_session kabul etmez (ValueError: Clean session is not used for MQTT 5.0 fırlatır); bunun yerine connect() içinde clean_start ve session expiry kullanın. MQTT 3.1.1'den MQTT 5'e geçiş rehberi bu seçenekleri anlatıyor.

Geçiş kontrol listesi

  1. Geçtiğiniz sürümü sabitleyin; örneğin requirements.txt içinde paho-mqtt>=2.1,<3 kullanın ki bir sonraki major sürüm sizi şaşırtmasın.
  2. Kod tabanında mqtt.Client( araması yapın ve önce mqtt.CallbackAPIVersion.VERSION2'yi, client_id='yi de isimli argüman olarak geçin.
  3. Diğer modüllerde veya kütüphanelerde atananlar dahil tüm callback'leri yeni imzaya güncelleyin.
  4. rc üzerindeki tamsayı karşılaştırmalarını reason_code.is_failure veya ad karşılaştırmalarıyla değiştirin.
  5. flags["session present"] ifadesini flags.session_present ile değiştirin.
  6. Hâlâ VERSION1 kullanan istemcileri yakalamak için uyarılar görünür halde çalıştırın (python -W error::DeprecationWarning app.py).

Zaten çok kod yeniden yazacaksanız asenkron bir istemcinin daha uygun olup olmadığını düşünün — asyncio için aiomqtt paho-mqtt 2.x üzerine kuruludur. Diğer paho istisnaları için paho-mqtt Python hataları yazısına bakın.

Taşınan istemciyi test edin

client_v2.py'yi çalıştırın, ardından tarayıcınızda online MQTT istemcisini açın, broker.connectmq.com adresine bağlanın ve testmqtt/paho2/hello topic'ine publish edin. Betiğiniz topic'i ve payload'ı yazdırmalıdır. Hata yollarını denemek için kendi broker'ınıza yanlış bir parolayla bağlanın ve on_connect'in çıplak bir sayı yerine Bad user name or password gibi okunabilir bir neden yazdırdığını kontrol edin.

Taşınan istemcinize test mesajı gönderin

paho-mqtt 2.0'a geçiş büyük ölçüde mekaniktir: yeni bir constructor argümanı, beş callback imzası ve reason code kontrollerine dönüşen birkaç tamsayı kontrolü. VERSION2 ile bir kez yapın, major sürümü sabitleyin; callback'leriniz MQTT 3.1.1 ve MQTT 5'te aynı şekilde davranır. Kütüphanenin tamamını adım adım görmek için Python ile MQTT rehberine göz atın.

Sıkça sorulan sorular

paho-mqtt'deki 'Unsupported callback API version' hatası ne anlama gelir?

paho-mqtt 2.0'dan itibaren mqtt.Client()'ın ilk argümanı callback API sürümüdür. 1.x için yazılmış ve client ID'yi ilk konumsal argüman olarak geçen kod artık bu ValueError'ı fırlatır. Önce mqtt.CallbackAPIVersion.VERSION2'yi, client ID'yi ise client_id= ile geçin.

paho-mqtt 2.x ile eski callback'lerimi kullanmaya devam edebilir miyim?

Evet, geçici olarak. mqtt.CallbackAPIVersion.VERSION1 geçerseniz 1.x callback imzaları çalışmaya devam eder; ancak kütüphane bir DeprecationWarning verir ve sürüm 1'in gelecekteki bir major sürümde kaldırılması planlanmaktadır. Fırsat bulduğunuzda VERSION2'ye geçin.

Güncellemeden sonra 'if rc == 5' kontrolüm neden artık çalışmıyor?

VERSION2 ile on_connect, MQTT 3.1.1'de bile bir ReasonCode nesnesi alır ve 3.1.1 dönüş kodları MQTT 5 reason code'larına çevrilir. 'Not authorized' artık 5 değil, 135'tir (0x87). reason_code.is_failure kullanın ya da ada göre karşılaştırın; örneğin reason_code == 'Not authorized'.