From bf5456e380c1436e0d9b52b8b4603530e1ae2e4c Mon Sep 17 00:00:00 2001 From: Vera Xia Date: Fri, 21 Nov 2025 13:46:21 -0800 Subject: [PATCH 01/12] bind sdk metrics --- awscrt/iot_metrics.py | 25 ++++++++++++++++++ awscrt/mqtt.py | 18 ++++++++++++- awscrt/mqtt5.py | 16 +++++++++++- source/mqtt5_client.c | 15 ++++++++++- source/mqtt_client_connection.c | 46 +++++++++++++++++++++++++++++++-- test/test_mqtt.py | 7 +++-- test/test_mqtt5.py | 7 +++-- 7 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 awscrt/iot_metrics.py diff --git a/awscrt/iot_metrics.py b/awscrt/iot_metrics.py new file mode 100644 index 000000000..7d34312b1 --- /dev/null +++ b/awscrt/iot_metrics.py @@ -0,0 +1,25 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0. + +""" +IoT SDK Metrics + +This module provides data structures and utilities for IoT SDK metrics collection +that are embedded in MQTT CONNECT packet username fields. +""" + +from dataclasses import dataclass +from typing import Dict, Optional +from awscrt import __version__ + + +@dataclass +class SdkMetrics: + """ + Configuration for IoT SDK metrics that are embedded in MQTT username field. + + Args: + library_name (str): The SDK library name (e.g., "IoTDeviceSDK/Python") + + """ + library_name: str = "IoTDeviceSDK/Python" diff --git a/awscrt/mqtt.py b/awscrt/mqtt.py index 7529168ea..fc1ed928f 100644 --- a/awscrt/mqtt.py +++ b/awscrt/mqtt.py @@ -16,6 +16,7 @@ from awscrt.io import ClientBootstrap, ClientTlsContext, SocketOptions from dataclasses import dataclass from awscrt.mqtt5 import Client as Mqtt5Client +from iot_metrics import SdkMetrics class QoS(IntEnum): @@ -330,6 +331,14 @@ class Connection(NativeResource): proxy_options (Optional[awscrt.http.HttpProxyOptions]): Optional proxy options for all connections. + + enable_metrics (bool): If true, enable IoT SDK metrics in CONNECT packet username field, otherwise, disabled. + Default to True. You may set it to false if you are not using AWS IoT services, and + using a custom authentication mechanism. + + metrics (Optional[SdkMetrics]): + Configuration for IoT SDK metrics that are embedded in MQTT username field. + If None is provided, default SdkMetrics configuration is used. """ def __init__(self, @@ -355,7 +364,9 @@ def __init__(self, proxy_options=None, on_connection_success=None, on_connection_failure=None, - on_connection_closed=None + on_connection_closed=None, + enable_metrics=True, + metrics=None, ): assert isinstance(client, Client) or isinstance(client, Mqtt5Client) @@ -369,6 +380,7 @@ def __init__(self, assert callable(on_connection_success) or on_connection_success is None assert callable(on_connection_failure) or on_connection_failure is None assert callable(on_connection_closed) or on_connection_closed is None + assert isinstance(metrics, SdkMetrics) or metrics is None if reconnect_min_timeout_secs > reconnect_max_timeout_secs: raise ValueError("'reconnect_min_timeout_secs' cannot exceed 'reconnect_max_timeout_secs'") @@ -408,6 +420,10 @@ def __init__(self, self.password = password self.socket_options = socket_options if socket_options else SocketOptions() self.proxy_options = proxy_options if proxy_options else websocket_proxy_options + if enable_metrics: + self.metrics = metrics if metrics else SdkMetrics() + else: + self.metrics = None self._binding = _awscrt.mqtt_client_connection_new( self, diff --git a/awscrt/mqtt5.py b/awscrt/mqtt5.py index 7c5e4f31f..97675ad02 100644 --- a/awscrt/mqtt5.py +++ b/awscrt/mqtt5.py @@ -15,6 +15,7 @@ from dataclasses import dataclass from collections.abc import Sequence from inspect import signature +from awscrt.iot_metrics import SdkMetrics class QoS(IntEnum): @@ -1158,6 +1159,9 @@ class ConnectPacket: will_delay_interval_sec (int): A time interval, in seconds, that the server should wait (for a session reconnection) before sending the will message associated with the connection's session. If omitted or None, the server will send the will when the associated session is destroyed. If the session is destroyed before a will delay interval has elapsed, then the will must be sent at the time of session destruction. will (PublishPacket): The definition of a message to be published when the connection's session is destroyed by the server or when the will delay interval has elapsed, whichever comes first. If None, then nothing will be sent. user_properties (Sequence[UserProperty]): List of MQTT5 user properties included with the packet. + enable_metrics (bool): If true, enable IoT SDK metrics in CONNECT packet username field, otherwise, disabled. Default to True. You may set it to false if you are not using AWS IoT services, and using a custom authentication mechanism. + metrics (Optional[SdkMetrics]): Configuration for IoT SDK metrics that are embedded in MQTT username field. If None is provided, default SdkMetrics configuration is used. + """ keep_alive_interval_sec: int = None client_id: str = None @@ -1171,6 +1175,8 @@ class ConnectPacket: will_delay_interval_sec: int = None will: PublishPacket = None user_properties: 'Sequence[UserProperty]' = None + enable_metrics: bool = True + metrics: SdkMetrics = None class WebsocketHandshakeTransformArgs: @@ -1728,7 +1734,6 @@ class Client(NativeResource): """ def __init__(self, client_options: ClientOptions): - super().__init__() core = _ClientCore(client_options) @@ -1746,6 +1751,12 @@ def __init__(self, client_options: ClientOptions): if not socket_options: socket_options = SocketOptions() + # Handle metrics configuration + if connect_options.enable_metrics: + self.metrics = connect_options.metrics if connect_options.metrics else SdkMetrics() + else: + self.metrics = None + if not connect_options.will: is_will_none = True will = PublishPacket() @@ -1773,6 +1784,8 @@ def __init__(self, client_options: ClientOptions): connect_options.maximum_packet_size, connect_options.will_delay_interval_sec, connect_options.user_properties, + connect_options.enable_metrics, + self.metrics.library_name if self.metrics else None, is_will_none, will.qos, will.payload, @@ -1797,6 +1810,7 @@ def __init__(self, client_options: ClientOptions): client_options.ack_timeout_sec, client_options.topic_aliasing_options, websocket_is_none, + core) # Store the options for adapter diff --git a/source/mqtt5_client.c b/source/mqtt5_client.c index 243af6a0e..6db096dc9 100644 --- a/source/mqtt5_client.c +++ b/source/mqtt5_client.c @@ -830,6 +830,8 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { PyObject *maximum_packet_size_py; /* optional uint32_t */ PyObject *will_delay_interval_sec_py; /* optional uint32_t */ PyObject *user_properties_py; /* optional */ + PyObject *is_metrics_enabled_py; /* optional PublishPacket */ + struct aws_byte_cursor metrics_library_name; /* optional IoT SDK metrics username */ /* Will */ PyObject *is_will_none_py; /* optional PublishPacket */ @@ -862,7 +864,7 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple( args, - "Os#IOOOOz#Oz#z#OOOOOOOOOz*Oz#OOOz#z*z#OOOOOOOOOOOOOO", + "Os#IOOOOz#Oz#z#OOOOOOOOz#OOz*Oz#OOOz#z*z#OOOOOOOOOOOOOO", /* O */ &self_py, /* s */ &host_name.ptr, /* # */ &host_name.len, @@ -887,6 +889,9 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { /* O */ &maximum_packet_size_py, /* O */ &will_delay_interval_sec_py, /* O */ &user_properties_py, + /* O */ &is_metrics_enabled_py, + /* z */ &metrics_library_name.ptr, + /* # */ &metrics_library_name.len, /* O */ &is_will_none_py, /* O */ &will_qos_val_py, @@ -1217,6 +1222,14 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { } connect_options.user_properties = user_properties_tmp; + /* METRICS */ + struct aws_mqtt_iot_sdk_metrics metrics_tmp; + AWS_ZERO_STRUCT(metrics_tmp); + if (PyObject_IsTrue(is_metrics_enabled_py)) { + metrics_tmp.library_name = metrics_library_name; + connect_options.metrics = &metrics_tmp; + } + /* WILL */ struct aws_mqtt5_packet_publish_view will; diff --git a/source/mqtt_client_connection.c b/source/mqtt_client_connection.c index 78a26057e..60261aab2 100644 --- a/source/mqtt_client_connection.c +++ b/source/mqtt_client_connection.c @@ -437,6 +437,39 @@ static void s_on_connect( PyGILState_Release(state); } +/* If unsuccessful, false is returned and a Python error has been set */ +bool s_set_metrics(struct aws_mqtt_client_connection *connection, PyObject *metrics) { + assert(metrics && (metrics != Py_None)); + + if (connection == NULL) { + return false; + } + + bool success = false; + + PyObject *library_name_py = PyObject_GetAttrString(metrics, "library_name"); + struct aws_byte_cursor library_name = aws_byte_cursor_from_pyunicode(library_name_py); + if (!library_name.ptr) { + PyErr_SetString(PyExc_TypeError, "metrics.library_name must be str type"); + goto done; + } + + struct aws_mqtt_iot_sdk_metrics metrics_struct = { + .library_name = library_name, + }; + + if (aws_mqtt_client_connection_set_metrics(connection, &metrics_struct)) { + PyErr_SetAwsLastError(); + success = false; + } + + success = true; + +done: + Py_DECREF(library_name_py); + return success; +} + /* If unsuccessful, false is returned and a Python error has been set */ bool s_set_will(struct aws_mqtt_client_connection *connection, PyObject *will) { assert(will && (will != Py_None)); @@ -668,9 +701,10 @@ PyObject *aws_py_mqtt_client_connection_connect(PyObject *self, PyObject *args) PyObject *is_clean_session; PyObject *on_connect; PyObject *proxy_options_py; + PyObject *metrics_py; if (!PyArg_ParseTuple( args, - "Os#s#IOOKKHIIOz#z#OOO", + "Os#s#IOOKKHIIOz#z#OOOO", &impl_capsule, &client_id, &client_id_len, @@ -691,7 +725,8 @@ PyObject *aws_py_mqtt_client_connection_connect(PyObject *self, PyObject *args) &password_len, &is_clean_session, &on_connect, - &proxy_options_py)) { + &proxy_options_py, + &metrics_py)) { return NULL; } @@ -773,6 +808,13 @@ PyObject *aws_py_mqtt_client_connection_connect(PyObject *self, PyObject *args) } } + // If metrics is None, we do not set metrics at all. + if (metrics_py != Py_None) { + if (!s_set_metrics(py_connection->native, metrics_py)) { + goto done; + } + } + if (on_connect != Py_None) { Py_INCREF(on_connect); py_connection->on_connect = on_connect; diff --git a/test/test_mqtt.py b/test/test_mqtt.py index f8435316e..9f603caea 100644 --- a/test/test_mqtt.py +++ b/test/test_mqtt.py @@ -4,6 +4,7 @@ from awscrt.io import ClientBootstrap, ClientTlsContext, DefaultHostResolver, EventLoopGroup, Pkcs11Lib, TlsContextOptions from awscrt import http from awscrt.mqtt import Client, Connection, QoS, Will, OnConnectionClosedData, OnConnectionFailureData, OnConnectionSuccessData, ConnectReturnCode +from awscrt.iot_metrics import SdkMetrics from test import test_retry_wrapper, NativeResourceTest from concurrent.futures import Future import os @@ -629,7 +630,8 @@ def _test_mqtt311_direct_connect_basic_auth(self): host_name=input_host_name, port=input_port, username=input_username, - password=input_password) + password=input_password, + enable_metrics=False) connection.connect().result(TIMEOUT) connection.disconnect().result(TIMEOUT) @@ -760,7 +762,8 @@ def sign_function(transform_args, **kwargs): username=input_username, password=input_password, use_websockets=True, - websocket_handshake_transform=sign_function) + websocket_handshake_transform=sign_function, + enable_metrics=False) connection.connect().result(TIMEOUT) connection.disconnect().result(TIMEOUT) diff --git a/test/test_mqtt5.py b/test/test_mqtt5.py index 83ee5928a..6c9ef8f44 100644 --- a/test/test_mqtt5.py +++ b/test/test_mqtt5.py @@ -3,6 +3,7 @@ from concurrent.futures import Future from awscrt import mqtt5, io, http, exceptions +from awscrt.iot_metrics import SdkMetrics from test import test_retry_wrapper, NativeResourceTest import os import unittest @@ -224,7 +225,8 @@ def _test_direct_connect_basic_auth(self): connect_options = mqtt5.ConnectPacket( client_id=create_client_id(), username=input_username, - password=input_password + password=input_password, + enable_metrics=False ) client_options = mqtt5.ClientOptions( host_name=input_host_name, @@ -610,7 +612,8 @@ def test_connect_with_incorrect_basic_authentication_credentials(self): connect_options = mqtt5.ConnectPacket( client_id=create_client_id(), username="bad username", - password="bad password" + password="bad password", + enable_metrics=False ) client_options = mqtt5.ClientOptions( host_name=input_host_name, From d3d6352259e5a5c2db06ac5a6ad282998e35d187 Mon Sep 17 00:00:00 2001 From: Vera Xia Date: Fri, 21 Nov 2025 13:46:48 -0800 Subject: [PATCH 02/12] set aws-c-mqtt to test branch --- crt/aws-c-mqtt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crt/aws-c-mqtt b/crt/aws-c-mqtt index 1d512d927..dc369803a 160000 --- a/crt/aws-c-mqtt +++ b/crt/aws-c-mqtt @@ -1 +1 @@ -Subproject commit 1d512d92709f60b74e2cafa018e69a2e647f28e9 +Subproject commit dc369803a301dd6dff35bb66b0db7e282fa27f29 From 21fe38df8db87542b6a5cd17b89a1d11ed88bd86 Mon Sep 17 00:00:00 2001 From: Vera Xia Date: Fri, 21 Nov 2025 13:48:38 -0800 Subject: [PATCH 03/12] bind sdk metrics --- source/mqtt5_client.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/source/mqtt5_client.c b/source/mqtt5_client.c index 6db096dc9..4b78d10af 100644 --- a/source/mqtt5_client.c +++ b/source/mqtt5_client.c @@ -819,18 +819,18 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { uint32_t port; /* Connect Options */ - struct aws_byte_cursor client_id; /* optional */ - PyObject *keep_alive_interval_sec_py; /* optional uint16_t */ - struct aws_byte_cursor username; /* optional */ - struct aws_byte_cursor password; /* optional */ - PyObject *session_expiry_interval_sec_py; /* optional uint32_t */ - PyObject *request_response_information_py; /* optional bool */ - PyObject *request_problem_information_py; /* optional bool */ - PyObject *receive_maximum_py; /* optional uint16_t */ - PyObject *maximum_packet_size_py; /* optional uint32_t */ - PyObject *will_delay_interval_sec_py; /* optional uint32_t */ - PyObject *user_properties_py; /* optional */ - PyObject *is_metrics_enabled_py; /* optional PublishPacket */ + struct aws_byte_cursor client_id; /* optional */ + PyObject *keep_alive_interval_sec_py; /* optional uint16_t */ + struct aws_byte_cursor username; /* optional */ + struct aws_byte_cursor password; /* optional */ + PyObject *session_expiry_interval_sec_py; /* optional uint32_t */ + PyObject *request_response_information_py; /* optional bool */ + PyObject *request_problem_information_py; /* optional bool */ + PyObject *receive_maximum_py; /* optional uint16_t */ + PyObject *maximum_packet_size_py; /* optional uint32_t */ + PyObject *will_delay_interval_sec_py; /* optional uint32_t */ + PyObject *user_properties_py; /* optional */ + PyObject *is_metrics_enabled_py; /* optional PublishPacket */ struct aws_byte_cursor metrics_library_name; /* optional IoT SDK metrics username */ /* Will */ From 13fe021f933032f73cb7aa712466be7aee89192d Mon Sep 17 00:00:00 2001 From: Vera Xia Date: Fri, 21 Nov 2025 14:09:13 -0800 Subject: [PATCH 04/12] update aws-c-common --- crt/aws-c-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crt/aws-c-common b/crt/aws-c-common index 31578beb2..95515a8b1 160000 --- a/crt/aws-c-common +++ b/crt/aws-c-common @@ -1 +1 @@ -Subproject commit 31578beb2309330fece3fb3a66035a568a2641e7 +Subproject commit 95515a8b1ff40d5bb14f965ca4cbbe99ad1843df From da7ed3106e8171972d40b6961c89770fdbed3b75 Mon Sep 17 00:00:00 2001 From: Vera Xia Date: Fri, 21 Nov 2025 14:21:07 -0800 Subject: [PATCH 05/12] add SdkMetrics in mqtt5 lib --- awscrt/iot_metrics.py | 25 ------------------------- awscrt/mqtt.py | 3 +-- awscrt/mqtt5.py | 13 ++++++++++++- 3 files changed, 13 insertions(+), 28 deletions(-) delete mode 100644 awscrt/iot_metrics.py diff --git a/awscrt/iot_metrics.py b/awscrt/iot_metrics.py deleted file mode 100644 index 7d34312b1..000000000 --- a/awscrt/iot_metrics.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0. - -""" -IoT SDK Metrics - -This module provides data structures and utilities for IoT SDK metrics collection -that are embedded in MQTT CONNECT packet username fields. -""" - -from dataclasses import dataclass -from typing import Dict, Optional -from awscrt import __version__ - - -@dataclass -class SdkMetrics: - """ - Configuration for IoT SDK metrics that are embedded in MQTT username field. - - Args: - library_name (str): The SDK library name (e.g., "IoTDeviceSDK/Python") - - """ - library_name: str = "IoTDeviceSDK/Python" diff --git a/awscrt/mqtt.py b/awscrt/mqtt.py index fc1ed928f..84f7895ac 100644 --- a/awscrt/mqtt.py +++ b/awscrt/mqtt.py @@ -15,8 +15,7 @@ from awscrt.http import HttpProxyOptions, HttpRequest from awscrt.io import ClientBootstrap, ClientTlsContext, SocketOptions from dataclasses import dataclass -from awscrt.mqtt5 import Client as Mqtt5Client -from iot_metrics import SdkMetrics +from awscrt.mqtt5 import Client as Mqtt5Client, SdkMetrics class QoS(IntEnum): diff --git a/awscrt/mqtt5.py b/awscrt/mqtt5.py index 97675ad02..6a8ff692d 100644 --- a/awscrt/mqtt5.py +++ b/awscrt/mqtt5.py @@ -15,7 +15,18 @@ from dataclasses import dataclass from collections.abc import Sequence from inspect import signature -from awscrt.iot_metrics import SdkMetrics + + +@dataclass +class SdkMetrics: + """ + Configuration for IoT SDK metrics that are embedded in MQTT username field. + + Args: + library_name (str): The SDK library name (e.g., "IoTDeviceSDK/Python") + + """ + library_name: str = "IoTDeviceSDK/Python" class QoS(IntEnum): From c167b4342cc596c0e2668334fbb5841c52c5d5c1 Mon Sep 17 00:00:00 2001 From: Vera Xia Date: Tue, 23 Dec 2025 09:50:11 -0800 Subject: [PATCH 06/12] move metrics to client option --- awscrt/mqtt.py | 11 ++-- awscrt/mqtt5.py | 10 ++-- crt/aws-c-mqtt | 2 +- source/mqtt5_client.c | 31 ++++++----- test/test_mqtt5.py | 121 +++++++++++++++++++++++++++++++++++++++--- 5 files changed, 140 insertions(+), 35 deletions(-) diff --git a/awscrt/mqtt.py b/awscrt/mqtt.py index 84f7895ac..ed2fc248a 100644 --- a/awscrt/mqtt.py +++ b/awscrt/mqtt.py @@ -334,10 +334,6 @@ class Connection(NativeResource): enable_metrics (bool): If true, enable IoT SDK metrics in CONNECT packet username field, otherwise, disabled. Default to True. You may set it to false if you are not using AWS IoT services, and using a custom authentication mechanism. - - metrics (Optional[SdkMetrics]): - Configuration for IoT SDK metrics that are embedded in MQTT username field. - If None is provided, default SdkMetrics configuration is used. """ def __init__(self, @@ -365,7 +361,6 @@ def __init__(self, on_connection_failure=None, on_connection_closed=None, enable_metrics=True, - metrics=None, ): assert isinstance(client, Client) or isinstance(client, Mqtt5Client) @@ -379,7 +374,6 @@ def __init__(self, assert callable(on_connection_success) or on_connection_success is None assert callable(on_connection_failure) or on_connection_failure is None assert callable(on_connection_closed) or on_connection_closed is None - assert isinstance(metrics, SdkMetrics) or metrics is None if reconnect_min_timeout_secs > reconnect_max_timeout_secs: raise ValueError("'reconnect_min_timeout_secs' cannot exceed 'reconnect_max_timeout_secs'") @@ -420,7 +414,7 @@ def __init__(self, self.socket_options = socket_options if socket_options else SocketOptions() self.proxy_options = proxy_options if proxy_options else websocket_proxy_options if enable_metrics: - self.metrics = metrics if metrics else SdkMetrics() + self.metrics = SdkMetrics() else: self.metrics = None @@ -539,7 +533,8 @@ def on_connect(error_code, return_code, session_present): self.password, self.clean_session, on_connect, - self.proxy_options + self.proxy_options, + self.metrics ) except Exception as e: diff --git a/awscrt/mqtt5.py b/awscrt/mqtt5.py index 6a8ff692d..fab77c51d 100644 --- a/awscrt/mqtt5.py +++ b/awscrt/mqtt5.py @@ -1170,9 +1170,7 @@ class ConnectPacket: will_delay_interval_sec (int): A time interval, in seconds, that the server should wait (for a session reconnection) before sending the will message associated with the connection's session. If omitted or None, the server will send the will when the associated session is destroyed. If the session is destroyed before a will delay interval has elapsed, then the will must be sent at the time of session destruction. will (PublishPacket): The definition of a message to be published when the connection's session is destroyed by the server or when the will delay interval has elapsed, whichever comes first. If None, then nothing will be sent. user_properties (Sequence[UserProperty]): List of MQTT5 user properties included with the packet. - enable_metrics (bool): If true, enable IoT SDK metrics in CONNECT packet username field, otherwise, disabled. Default to True. You may set it to false if you are not using AWS IoT services, and using a custom authentication mechanism. - metrics (Optional[SdkMetrics]): Configuration for IoT SDK metrics that are embedded in MQTT username field. If None is provided, default SdkMetrics configuration is used. - + """ keep_alive_interval_sec: int = None client_id: str = None @@ -1186,8 +1184,6 @@ class ConnectPacket: will_delay_interval_sec: int = None will: PublishPacket = None user_properties: 'Sequence[UserProperty]' = None - enable_metrics: bool = True - metrics: SdkMetrics = None class WebsocketHandshakeTransformArgs: @@ -1355,6 +1351,8 @@ class ClientOptions: on_lifecycle_event_connection_success_fn (Callable[[LifecycleConnectSuccessData],]): Callback for Lifecycle Event Connection Success. on_lifecycle_event_connection_failure_fn (Callable[[LifecycleConnectFailureData],]): Callback for Lifecycle Event Connection Failure. on_lifecycle_event_disconnection_fn (Callable[[LifecycleDisconnectData],]): Callback for Lifecycle Event Disconnection. + enable_metrics (bool): If true, enable IoT SDK metrics in CONNECT packet username field, otherwise, disabled. Default to True. You may set it to false if you are not using AWS IoT services, and using a custom authentication mechanism. + """ host_name: str port: int = None @@ -1381,6 +1379,7 @@ class ClientOptions: on_lifecycle_event_connection_success_fn: Callable[[LifecycleConnectSuccessData], None] = None on_lifecycle_event_connection_failure_fn: Callable[[LifecycleConnectFailureData], None] = None on_lifecycle_event_disconnection_fn: Callable[[LifecycleDisconnectData], None] = None + enable_metrics: bool = True def _check_callback(callback): @@ -1409,6 +1408,7 @@ def __init__(self, client_options: ClientOptions): self._on_lifecycle_connection_failure_cb = _check_callback( client_options.on_lifecycle_event_connection_failure_fn) self._on_lifecycle_disconnection_cb = _check_callback(client_options.on_lifecycle_event_disconnection_fn) + self._enable_metrics = client_options.enable_metrics def _ws_handshake_transform(self, http_request_binding, http_headers_binding, native_userdata): if self._ws_handshake_transform_cb is None: diff --git a/crt/aws-c-mqtt b/crt/aws-c-mqtt index dc369803a..154e9c5dd 160000 --- a/crt/aws-c-mqtt +++ b/crt/aws-c-mqtt @@ -1 +1 @@ -Subproject commit dc369803a301dd6dff35bb66b0db7e282fa27f29 +Subproject commit 154e9c5dd19ec6d8d4cd690492d2a029f13e2035 diff --git a/source/mqtt5_client.c b/source/mqtt5_client.c index 4b78d10af..7a0661665 100644 --- a/source/mqtt5_client.c +++ b/source/mqtt5_client.c @@ -830,7 +830,9 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { PyObject *maximum_packet_size_py; /* optional uint32_t */ PyObject *will_delay_interval_sec_py; /* optional uint32_t */ PyObject *user_properties_py; /* optional */ - PyObject *is_metrics_enabled_py; /* optional PublishPacket */ + + /* Metrics */ + PyObject *is_metrics_enabled_py; /* optional enable metrics */ struct aws_byte_cursor metrics_library_name; /* optional IoT SDK metrics username */ /* Will */ @@ -864,7 +866,7 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple( args, - "Os#IOOOOz#Oz#z#OOOOOOOOz#OOz*Oz#OOOz#z*z#OOOOOOOOOOOOOO", + "Os#IOOOOz#Oz#z#OOOOOOOOOz*Oz#OOOz#z*z#OOz#OOOOOOOOOOOOO", /* O */ &self_py, /* s */ &host_name.ptr, /* # */ &host_name.len, @@ -889,9 +891,6 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { /* O */ &maximum_packet_size_py, /* O */ &will_delay_interval_sec_py, /* O */ &user_properties_py, - /* O */ &is_metrics_enabled_py, - /* z */ &metrics_library_name.ptr, - /* # */ &metrics_library_name.len, /* O */ &is_will_none_py, /* O */ &will_qos_val_py, @@ -909,6 +908,12 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { /* # */ &will_content_type.len, /* O */ &will_user_properties_py, + + /* Metrics */ + /* O */ &is_metrics_enabled_py, + /* z */ &metrics_library_name.ptr, + /* # */ &metrics_library_name.len, + /* O */ &session_behavior_py, /* O */ &extended_validation_and_flow_control_options_py, /* O */ &offline_queue_behavior_py, @@ -1222,14 +1227,6 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { } connect_options.user_properties = user_properties_tmp; - /* METRICS */ - struct aws_mqtt_iot_sdk_metrics metrics_tmp; - AWS_ZERO_STRUCT(metrics_tmp); - if (PyObject_IsTrue(is_metrics_enabled_py)) { - metrics_tmp.library_name = metrics_library_name; - connect_options.metrics = &metrics_tmp; - } - /* WILL */ struct aws_mqtt5_packet_publish_view will; @@ -1292,6 +1289,14 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { connect_options.will = &will; } + /* METRICS */ + struct aws_mqtt_iot_sdk_metrics metrics_tmp; + AWS_ZERO_STRUCT(metrics_tmp); + if (PyObject_IsTrue(is_metrics_enabled_py)) { + metrics_tmp.library_name = metrics_library_name; + client_options.metrics = &metrics_tmp; + } + /* CALLBACKS */ Py_INCREF(client_core_py); diff --git a/test/test_mqtt5.py b/test/test_mqtt5.py index 6c9ef8f44..257b19a60 100644 --- a/test/test_mqtt5.py +++ b/test/test_mqtt5.py @@ -3,7 +3,6 @@ from concurrent.futures import Future from awscrt import mqtt5, io, http, exceptions -from awscrt.iot_metrics import SdkMetrics from test import test_retry_wrapper, NativeResourceTest import os import unittest @@ -204,7 +203,8 @@ def _test_direct_connect_minimum(self): client_options = mqtt5.ClientOptions( host_name=input_host_name, - port=input_port + port=input_port, + enable_metrics=False ) callbacks = Mqtt5TestCallbacks() client = self._create_client(client_options=client_options, callbacks=callbacks) @@ -225,13 +225,13 @@ def _test_direct_connect_basic_auth(self): connect_options = mqtt5.ConnectPacket( client_id=create_client_id(), username=input_username, - password=input_password, - enable_metrics=False + password=input_password ) client_options = mqtt5.ClientOptions( host_name=input_host_name, port=input_port, - connect_options=connect_options + connect_options=connect_options, + enable_metrics=False ) callbacks = Mqtt5TestCallbacks() client = self._create_client(client_options=client_options, callbacks=callbacks) @@ -418,7 +418,8 @@ def _test_websocket_connect_basic_auth(self): client_options = mqtt5.ClientOptions( host_name=input_host_name, port=input_port, - connect_options=connect_options + connect_options=connect_options, + enable_metrics=False ) callbacks = Mqtt5TestCallbacks() client_options.websocket_handshake_transform = callbacks.ws_handshake_transform @@ -612,8 +613,7 @@ def test_connect_with_incorrect_basic_authentication_credentials(self): connect_options = mqtt5.ConnectPacket( client_id=create_client_id(), username="bad username", - password="bad password", - enable_metrics=False + password="bad password" ) client_options = mqtt5.ClientOptions( host_name=input_host_name, @@ -1848,6 +1848,111 @@ def _test_operation_statistics_uc1(self): def test_operation_statistics_uc1(self): test_retry_wrapper(self._test_operation_statistics_uc1) + # ============================================================== + # METRICS TEST CASES + # ============================================================== + + def _test_metrics_enabled_default(self): + input_host_name = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_HOST") + input_cert = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_RSA_CERT") + input_key = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_RSA_KEY") + + tls_ctx_options = io.TlsContextOptions.create_client_with_mtls_from_path( + input_cert, + input_key + ) + client_options = mqtt5.ClientOptions( + host_name=input_host_name, + port=8883 + ) + client_options.connect_options = mqtt5.ConnectPacket(client_id=create_client_id()) + client_options.tls_ctx = io.ClientTlsContext(tls_ctx_options) + callbacks = Mqtt5TestCallbacks() + client = self._create_client(client_options=client_options, callbacks=callbacks) + + # Verify metrics are enabled by default + self.assertTrue(client_options.connect_options.enable_metrics) + self.assertIsNotNone(client.metrics) + self.assertEqual(client.metrics.library_name, "IoTDeviceSDK/Python") + + client.start() + callbacks.future_connection_success.result(TIMEOUT) + client.stop() + callbacks.future_stopped.result(TIMEOUT) + + def test_metrics_enabled_default(self): + test_retry_wrapper(self._test_metrics_enabled_default) + + def _test_metrics_disabled(self): + input_host_name = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_HOST") + input_cert = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_RSA_CERT") + input_key = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_RSA_KEY") + + tls_ctx_options = io.TlsContextOptions.create_client_with_mtls_from_path( + input_cert, + input_key + ) + client_options = mqtt5.ClientOptions( + host_name=input_host_name, + port=8883 + ) + client_options.connect_options = mqtt5.ConnectPacket( + client_id=create_client_id(), + enable_metrics=False + ) + client_options.tls_ctx = io.ClientTlsContext(tls_ctx_options) + callbacks = Mqtt5TestCallbacks() + client = self._create_client(client_options=client_options, callbacks=callbacks) + + # Verify metrics are disabled + self.assertFalse(client_options.connect_options.enable_metrics) + self.assertIsNone(client.metrics) + + client.start() + callbacks.future_connection_success.result(TIMEOUT) + client.stop() + callbacks.future_stopped.result(TIMEOUT) + + def test_metrics_disabled(self): + test_retry_wrapper(self._test_metrics_disabled) + + def _test_metrics_custom_library_name(self): + input_host_name = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_HOST") + input_cert = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_RSA_CERT") + input_key = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_RSA_KEY") + + custom_metrics = mqtt5.SdkMetrics(library_name="CustomSDK/Test") + + tls_ctx_options = io.TlsContextOptions.create_client_with_mtls_from_path( + input_cert, + input_key + ) + client_options = mqtt5.ClientOptions( + host_name=input_host_name, + port=8883 + ) + client_options.connect_options = mqtt5.ConnectPacket( + client_id=create_client_id(), + enable_metrics=True, + metrics=custom_metrics + ) + client_options.tls_ctx = io.ClientTlsContext(tls_ctx_options) + callbacks = Mqtt5TestCallbacks() + client = self._create_client(client_options=client_options, callbacks=callbacks) + + # Verify custom metrics are set + self.assertTrue(client_options.connect_options.enable_metrics) + self.assertIsNotNone(client.metrics) + self.assertEqual(client.metrics.library_name, "CustomSDK/Test") + + client.start() + callbacks.future_connection_success.result(TIMEOUT) + client.stop() + callbacks.future_stopped.result(TIMEOUT) + + def test_metrics_custom_library_name(self): + test_retry_wrapper(self._test_metrics_custom_library_name) + if __name__ == 'main': unittest.main() From 23687217ccf5137465ed9695f1c23bc36743a59e Mon Sep 17 00:00:00 2001 From: Vera Xia Date: Tue, 23 Dec 2025 10:10:51 -0800 Subject: [PATCH 07/12] clean up & lint --- awscrt/mqtt5.py | 2 +- source/mqtt5_client.c | 23 +++++++++++------------ test/test_mqtt.py | 1 - test/test_mqtt5.py | 14 +++++++------- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/awscrt/mqtt5.py b/awscrt/mqtt5.py index fab77c51d..2611fde6f 100644 --- a/awscrt/mqtt5.py +++ b/awscrt/mqtt5.py @@ -1170,7 +1170,7 @@ class ConnectPacket: will_delay_interval_sec (int): A time interval, in seconds, that the server should wait (for a session reconnection) before sending the will message associated with the connection's session. If omitted or None, the server will send the will when the associated session is destroyed. If the session is destroyed before a will delay interval has elapsed, then the will must be sent at the time of session destruction. will (PublishPacket): The definition of a message to be published when the connection's session is destroyed by the server or when the will delay interval has elapsed, whichever comes first. If None, then nothing will be sent. user_properties (Sequence[UserProperty]): List of MQTT5 user properties included with the packet. - + """ keep_alive_interval_sec: int = None client_id: str = None diff --git a/source/mqtt5_client.c b/source/mqtt5_client.c index 7a0661665..90be7554b 100644 --- a/source/mqtt5_client.c +++ b/source/mqtt5_client.c @@ -819,17 +819,17 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { uint32_t port; /* Connect Options */ - struct aws_byte_cursor client_id; /* optional */ - PyObject *keep_alive_interval_sec_py; /* optional uint16_t */ - struct aws_byte_cursor username; /* optional */ - struct aws_byte_cursor password; /* optional */ - PyObject *session_expiry_interval_sec_py; /* optional uint32_t */ - PyObject *request_response_information_py; /* optional bool */ - PyObject *request_problem_information_py; /* optional bool */ - PyObject *receive_maximum_py; /* optional uint16_t */ - PyObject *maximum_packet_size_py; /* optional uint32_t */ - PyObject *will_delay_interval_sec_py; /* optional uint32_t */ - PyObject *user_properties_py; /* optional */ + struct aws_byte_cursor client_id; /* optional */ + PyObject *keep_alive_interval_sec_py; /* optional uint16_t */ + struct aws_byte_cursor username; /* optional */ + struct aws_byte_cursor password; /* optional */ + PyObject *session_expiry_interval_sec_py; /* optional uint32_t */ + PyObject *request_response_information_py; /* optional bool */ + PyObject *request_problem_information_py; /* optional bool */ + PyObject *receive_maximum_py; /* optional uint16_t */ + PyObject *maximum_packet_size_py; /* optional uint32_t */ + PyObject *will_delay_interval_sec_py; /* optional uint32_t */ + PyObject *user_properties_py; /* optional */ /* Metrics */ PyObject *is_metrics_enabled_py; /* optional enable metrics */ @@ -908,7 +908,6 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { /* # */ &will_content_type.len, /* O */ &will_user_properties_py, - /* Metrics */ /* O */ &is_metrics_enabled_py, /* z */ &metrics_library_name.ptr, diff --git a/test/test_mqtt.py b/test/test_mqtt.py index 9f603caea..4040601d1 100644 --- a/test/test_mqtt.py +++ b/test/test_mqtt.py @@ -4,7 +4,6 @@ from awscrt.io import ClientBootstrap, ClientTlsContext, DefaultHostResolver, EventLoopGroup, Pkcs11Lib, TlsContextOptions from awscrt import http from awscrt.mqtt import Client, Connection, QoS, Will, OnConnectionClosedData, OnConnectionFailureData, OnConnectionSuccessData, ConnectReturnCode -from awscrt.iot_metrics import SdkMetrics from test import test_retry_wrapper, NativeResourceTest from concurrent.futures import Future import os diff --git a/test/test_mqtt5.py b/test/test_mqtt5.py index 257b19a60..69d4e6c9b 100644 --- a/test/test_mqtt5.py +++ b/test/test_mqtt5.py @@ -1869,12 +1869,12 @@ def _test_metrics_enabled_default(self): client_options.tls_ctx = io.ClientTlsContext(tls_ctx_options) callbacks = Mqtt5TestCallbacks() client = self._create_client(client_options=client_options, callbacks=callbacks) - + # Verify metrics are enabled by default self.assertTrue(client_options.connect_options.enable_metrics) self.assertIsNotNone(client.metrics) self.assertEqual(client.metrics.library_name, "IoTDeviceSDK/Python") - + client.start() callbacks.future_connection_success.result(TIMEOUT) client.stop() @@ -1903,11 +1903,11 @@ def _test_metrics_disabled(self): client_options.tls_ctx = io.ClientTlsContext(tls_ctx_options) callbacks = Mqtt5TestCallbacks() client = self._create_client(client_options=client_options, callbacks=callbacks) - + # Verify metrics are disabled self.assertFalse(client_options.connect_options.enable_metrics) self.assertIsNone(client.metrics) - + client.start() callbacks.future_connection_success.result(TIMEOUT) client.stop() @@ -1922,7 +1922,7 @@ def _test_metrics_custom_library_name(self): input_key = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_RSA_KEY") custom_metrics = mqtt5.SdkMetrics(library_name="CustomSDK/Test") - + tls_ctx_options = io.TlsContextOptions.create_client_with_mtls_from_path( input_cert, input_key @@ -1939,12 +1939,12 @@ def _test_metrics_custom_library_name(self): client_options.tls_ctx = io.ClientTlsContext(tls_ctx_options) callbacks = Mqtt5TestCallbacks() client = self._create_client(client_options=client_options, callbacks=callbacks) - + # Verify custom metrics are set self.assertTrue(client_options.connect_options.enable_metrics) self.assertIsNotNone(client.metrics) self.assertEqual(client.metrics.library_name, "CustomSDK/Test") - + client.start() callbacks.future_connection_success.result(TIMEOUT) client.stop() From 03e5d98d2d6ef4a10184b8fa180456e5f18c35f5 Mon Sep 17 00:00:00 2001 From: Vera Xia Date: Tue, 23 Dec 2025 10:24:56 -0800 Subject: [PATCH 08/12] update test to use client option --- test/test_mqtt5.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_mqtt5.py b/test/test_mqtt5.py index 69d4e6c9b..3c6f24d7e 100644 --- a/test/test_mqtt5.py +++ b/test/test_mqtt5.py @@ -1871,7 +1871,7 @@ def _test_metrics_enabled_default(self): client = self._create_client(client_options=client_options, callbacks=callbacks) # Verify metrics are enabled by default - self.assertTrue(client_options.connect_options.enable_metrics) + self.assertTrue(client_options.enable_metrics) self.assertIsNotNone(client.metrics) self.assertEqual(client.metrics.library_name, "IoTDeviceSDK/Python") @@ -1905,7 +1905,7 @@ def _test_metrics_disabled(self): client = self._create_client(client_options=client_options, callbacks=callbacks) # Verify metrics are disabled - self.assertFalse(client_options.connect_options.enable_metrics) + self.assertFalse(client_options.enable_metrics) self.assertIsNone(client.metrics) client.start() @@ -1941,7 +1941,7 @@ def _test_metrics_custom_library_name(self): client = self._create_client(client_options=client_options, callbacks=callbacks) # Verify custom metrics are set - self.assertTrue(client_options.connect_options.enable_metrics) + self.assertTrue(client_options.enable_metrics) self.assertIsNotNone(client.metrics) self.assertEqual(client.metrics.library_name, "CustomSDK/Test") From 5f3868c042f7142b9795ba8377a737aad17a26a0 Mon Sep 17 00:00:00 2001 From: Vera Xia Date: Tue, 23 Dec 2025 10:57:09 -0800 Subject: [PATCH 09/12] add metrics for adapter --- awscrt/mqtt5.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/awscrt/mqtt5.py b/awscrt/mqtt5.py index 2611fde6f..735e58cfb 100644 --- a/awscrt/mqtt5.py +++ b/awscrt/mqtt5.py @@ -1721,7 +1721,8 @@ def __init__( ping_timeout_ms: int, keep_alive_secs: int, ack_timeout_secs: int, - clean_session: int): + clean_session: int, + enable_metrics: bool): self.host_name = host_name self.port = port self.client_id = "" if client_id is None else client_id @@ -1732,6 +1733,7 @@ def __init__( self.keep_alive_secs: int = 1200 if keep_alive_secs is None else keep_alive_secs self.ack_timeout_secs: int = 0 if ack_timeout_secs is None else ack_timeout_secs self.clean_session: bool = True if clean_session is None else clean_session + self.enable_metrics: bool = True if enable_metrics is None else enable_metrics class Client(NativeResource): @@ -1763,8 +1765,8 @@ def __init__(self, client_options: ClientOptions): socket_options = SocketOptions() # Handle metrics configuration - if connect_options.enable_metrics: - self.metrics = connect_options.metrics if connect_options.metrics else SdkMetrics() + if client_options.enable_metrics: + self.metrics = SdkMetrics() else: self.metrics = None @@ -1795,7 +1797,7 @@ def __init__(self, client_options: ClientOptions): connect_options.maximum_packet_size, connect_options.will_delay_interval_sec, connect_options.user_properties, - connect_options.enable_metrics, + client_options.enable_metrics, self.metrics.library_name if self.metrics else None, is_will_none, will.qos, @@ -1836,7 +1838,8 @@ def __init__(self, client_options: ClientOptions): keep_alive_secs=connect_options.keep_alive_interval_sec, ack_timeout_secs=client_options.ack_timeout_sec, clean_session=( - client_options.session_behavior < ClientSessionBehaviorType.REJOIN_ALWAYS if client_options.session_behavior else True)) + client_options.session_behavior < ClientSessionBehaviorType.REJOIN_ALWAYS if client_options.session_behavior else True), + enable_metrics=client_options.enable_metrics) def start(self): """Notifies the MQTT5 client that you want it maintain connectivity to the configured endpoint. @@ -2068,5 +2071,6 @@ def new_connection(self, on_connection_interrupted=None, on_connection_resumed=N use_websockets=False, websocket_proxy_options=None, websocket_handshake_transform=None, - proxy_options=None + proxy_options=None, + enable_metrics=self.adapter_options.enable_metrics ) From ec40b817362b6b463d3f0c66be7c301f5f6b8cd4 Mon Sep 17 00:00:00 2001 From: Vera Xia Date: Tue, 23 Dec 2025 11:14:16 -0800 Subject: [PATCH 10/12] fix param order --- awscrt/mqtt5.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/awscrt/mqtt5.py b/awscrt/mqtt5.py index 735e58cfb..1d03401a9 100644 --- a/awscrt/mqtt5.py +++ b/awscrt/mqtt5.py @@ -1797,8 +1797,6 @@ def __init__(self, client_options: ClientOptions): connect_options.maximum_packet_size, connect_options.will_delay_interval_sec, connect_options.user_properties, - client_options.enable_metrics, - self.metrics.library_name if self.metrics else None, is_will_none, will.qos, will.payload, @@ -1811,6 +1809,8 @@ def __init__(self, client_options: ClientOptions): will.correlation_data_bytes or will.correlation_data, will.content_type, will.user_properties, + client_options.enable_metrics, + self.metrics.library_name if self.metrics else None, client_options.session_behavior, client_options.extended_validation_and_flow_control_options, client_options.offline_queue_behavior, @@ -1823,7 +1823,6 @@ def __init__(self, client_options: ClientOptions): client_options.ack_timeout_sec, client_options.topic_aliasing_options, websocket_is_none, - core) # Store the options for adapter From fb81ecc3608aa9038ef0c213e4b1f0af0e68265d Mon Sep 17 00:00:00 2001 From: Vera Xia Date: Tue, 23 Dec 2025 11:21:07 -0800 Subject: [PATCH 11/12] remove custom metrics test --- test/test_mqtt5.py | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/test/test_mqtt5.py b/test/test_mqtt5.py index 3c6f24d7e..1b4650dfb 100644 --- a/test/test_mqtt5.py +++ b/test/test_mqtt5.py @@ -1916,43 +1916,6 @@ def _test_metrics_disabled(self): def test_metrics_disabled(self): test_retry_wrapper(self._test_metrics_disabled) - def _test_metrics_custom_library_name(self): - input_host_name = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_HOST") - input_cert = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_RSA_CERT") - input_key = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_RSA_KEY") - - custom_metrics = mqtt5.SdkMetrics(library_name="CustomSDK/Test") - - tls_ctx_options = io.TlsContextOptions.create_client_with_mtls_from_path( - input_cert, - input_key - ) - client_options = mqtt5.ClientOptions( - host_name=input_host_name, - port=8883 - ) - client_options.connect_options = mqtt5.ConnectPacket( - client_id=create_client_id(), - enable_metrics=True, - metrics=custom_metrics - ) - client_options.tls_ctx = io.ClientTlsContext(tls_ctx_options) - callbacks = Mqtt5TestCallbacks() - client = self._create_client(client_options=client_options, callbacks=callbacks) - - # Verify custom metrics are set - self.assertTrue(client_options.enable_metrics) - self.assertIsNotNone(client.metrics) - self.assertEqual(client.metrics.library_name, "CustomSDK/Test") - - client.start() - callbacks.future_connection_success.result(TIMEOUT) - client.stop() - callbacks.future_stopped.result(TIMEOUT) - - def test_metrics_custom_library_name(self): - test_retry_wrapper(self._test_metrics_custom_library_name) - if __name__ == 'main': unittest.main() From 0435c7f0f35b04f5babdff7ff78d61fd809b2113 Mon Sep 17 00:00:00 2001 From: Vera Xia Date: Tue, 23 Dec 2025 12:01:13 -0800 Subject: [PATCH 12/12] update test test_metrics_disabled --- test/test_mqtt5.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_mqtt5.py b/test/test_mqtt5.py index 1b4650dfb..55e11d045 100644 --- a/test/test_mqtt5.py +++ b/test/test_mqtt5.py @@ -1894,11 +1894,11 @@ def _test_metrics_disabled(self): ) client_options = mqtt5.ClientOptions( host_name=input_host_name, - port=8883 + port=8883, + enable_metrics=False ) client_options.connect_options = mqtt5.ConnectPacket( - client_id=create_client_id(), - enable_metrics=False + client_id=create_client_id() ) client_options.tls_ctx = io.ClientTlsContext(tls_ctx_options) callbacks = Mqtt5TestCallbacks()