From 433d62e3bc8ed2e3725e11c2c55ab1f88ea8683c Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 20 Dec 2023 13:15:34 +0200 Subject: [PATCH 1/4] Remove unnecessary Python version checks Signed-off-by: Aarni Koskela --- requirements.txt | 1 - setup.py | 3 -- src/paho/mqtt/client.py | 58 ++++++++----------------------- src/paho/mqtt/properties.py | 18 +++------- src/paho/mqtt/reasoncodes.py | 2 -- src/paho/mqtt/subscribeoptions.py | 7 +--- tests/test_websockets.py | 10 ++---- 7 files changed, 21 insertions(+), 78 deletions(-) diff --git a/requirements.txt b/requirements.txt index bd0dd2ba..5dc884df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ mock==3.0.5 pylama==7.7.1 -pytest==4.6.6; python_version < '3.0' pytest==5.2.2; python_version >= '3.0' pytest-runner==5.2 tox==3.14.0 diff --git a/setup.py b/setup.py index 1f60be0a..e9580ae2 100644 --- a/setup.py +++ b/setup.py @@ -17,9 +17,6 @@ setup_requirements = ['pytest-runner'] if needs_pytest else [] extra_requirements = {'proxy': ['PySocks']} -if sys.version_info < (3, 0): - test_requirements += ['mock'] - setup( name='paho-mqtt', version=__version__, diff --git a/src/paho/mqtt/client.py b/src/paho/mqtt/client.py index 28454b7b..c329cabe 100644 --- a/src/paho/mqtt/client.py +++ b/src/paho/mqtt/client.py @@ -81,21 +81,10 @@ else: EAGAIN = errno.EAGAIN -# Python 2.7 does not have BlockingIOError. Fall back to IOError -try: - BlockingIOError -except NameError: - BlockingIOError = IOError - MQTTv31 = 3 MQTTv311 = 4 MQTTv5 = 5 -if sys.version_info[0] >= 3: - # define some alias for python2 compatibility - unicode = str - basestring = str - # Message types CONNECT = 0x10 CONNACK = 0x20 @@ -556,7 +545,7 @@ def __init__(self, client_id="", clean_session=None, userdata=None, self._client_id = b"" else: self._client_id = client_id - if isinstance(self._client_id, unicode): + if isinstance(self._client_id, str): self._client_id = self._client_id.encode('utf-8') self._username = None @@ -717,14 +706,8 @@ def tls_set_context(self, context=None): if self._ssl_context is not None: raise ValueError('SSL/TLS has already been configured.') - # Assume that have SSL support, or at least that context input behaves like ssl.SSLContext - # in current versions of Python - if context is None: - if hasattr(ssl, 'create_default_context'): - context = ssl.create_default_context() - else: - raise ValueError('SSL/TLS context must be specified') + context = ssl.create_default_context() self._ssl = True self._ssl_context = context @@ -981,9 +964,6 @@ def connect_async(self, host, port=1883, keepalive=60, bind_address="", bind_por raise ValueError('Invalid port number.') if keepalive < 0: raise ValueError('Keepalive must be >=0.') - if bind_address != "" and bind_address is not None: - if sys.version_info < (2, 7) or (3, 0) < sys.version_info < (3, 2): - raise ValueError('bind_address requires Python 2.7 or 3.2.') if bind_port < 0: raise ValueError('Invalid bind port number.') @@ -1248,7 +1228,7 @@ def publish(self, topic, payload=None, qos=0, retain=False, properties=None): if qos < 0 or qos > 2: raise ValueError('Invalid QoS level.') - if isinstance(payload, unicode): + if isinstance(payload, str): local_payload = payload.encode('utf-8') elif isinstance(payload, (bytes, bytearray)): local_payload = payload @@ -1318,17 +1298,17 @@ def username_pw_set(self, username, password=None): Must be called before connect() to have any effect. Requires a broker that supports MQTT v3.1. - username: The username to authenticate with. Need have no relationship to the client id. Must be unicode + username: The username to authenticate with. Need have no relationship to the client id. Must be str [MQTT-3.1.3-11]. Set to None to reset client back to not using username/password for broker authentication. - password: The password to authenticate with. Optional, set to None if not required. If it is unicode, then it + password: The password to authenticate with. Optional, set to None if not required. If it is str, then it will be encoded as UTF-8. """ # [MQTT-3.1.3-11] User name must be UTF-8 encoded string self._username = None if username is None else username.encode('utf-8') self._password = password - if isinstance(self._password, unicode): + if isinstance(self._password, str): self._password = self._password.encode('utf-8') def enable_bridge_mode(self): @@ -1461,7 +1441,7 @@ def subscribe(self, topic, qos=0, options=None, properties=None): else: topic, qos = topic - if isinstance(topic, basestring): + if isinstance(topic, (bytes, str)): if qos < 0 or qos > 2: raise ValueError('Invalid QoS level.') if self._protocol == MQTTv5: @@ -1493,7 +1473,7 @@ def subscribe(self, topic, qos=0, options=None, properties=None): for t, q in topic: if q < 0 or q > 2: raise ValueError('Invalid QoS level.') - if t is None or len(t) == 0 or not isinstance(t, basestring): + if t is None or len(t) == 0 or not isinstance(t, (bytes, str)): raise ValueError('Invalid topic.') topic_qos_list.append((t.encode('utf-8'), q)) @@ -1529,14 +1509,14 @@ def unsubscribe(self, topic, properties=None): topic_list = None if topic is None: raise ValueError('Invalid topic.') - if isinstance(topic, basestring): + if isinstance(topic, (bytes, str)): if len(topic) == 0: raise ValueError('Invalid topic.') topic_list = [topic.encode('utf-8')] elif isinstance(topic, list): topic_list = [] for t in topic: - if len(t) == 0 or not isinstance(t, basestring): + if len(t) == 0 or not isinstance(t, (bytes, str)): raise ValueError('Invalid topic.') topic_list.append(t.encode('utf-8')) @@ -1693,7 +1673,7 @@ def will_set(self, topic, payload=None, qos=0, retain=False, properties=None): raise ValueError( "The properties argument must be an instance of the Properties class.") - if isinstance(payload, unicode): + if isinstance(payload, str): self._will_payload = payload.encode('utf-8') elif isinstance(payload, (bytes, bytearray)): self._will_payload = payload @@ -2662,15 +2642,15 @@ def _pack_remaining_length(self, packet, remaining_length): return packet def _pack_str16(self, packet, data): - if isinstance(data, unicode): + if isinstance(data, str): data = data.encode('utf-8') packet.extend(struct.pack("!H", len(data))) packet.extend(data) def _send_publish(self, mid, topic, payload=b'', qos=0, retain=False, dup=False, info=None, properties=None): # we assume that topic and payload are already properly encoded - assert not isinstance(topic, unicode) and not isinstance( - payload, unicode) and payload is not None + assert not isinstance(topic, str) and not isinstance( + payload, str) and payload is not None if self._sock is None: return MQTT_ERR_NO_CONN @@ -3283,8 +3263,6 @@ def _handle_suback(self): props, props_len = properties.unpack(packet) reasoncodes = [] for c in packet[props_len:]: - if sys.version_info[0] < 3: - c = ord(c) reasoncodes.append(ReasonCodes(SUBACK >> 4, identifier=c)) else: pack_format = "!" + "B" * len(packet) @@ -3477,8 +3455,6 @@ def _handle_unsuback(self): props, props_len = properties.unpack(packet) reasoncodes = [] for c in packet[props_len:]: - if sys.version_info[0] < 3: - c = ord(c) reasoncodes.append(ReasonCodes(UNSUBACK >> 4, identifier=c)) if len(reasoncodes) == 1: reasoncodes = reasoncodes[0] @@ -3715,12 +3691,6 @@ def _create_socket_connection(self): addr = (self._host, self._port) source = (self._bind_address, self._bind_port) - - if sys.version_info < (2, 7) or (3, 0) < sys.version_info < (3, 2): - # Have to short-circuit here because of unsupported source_address - # param in earlier Python versions. - return socket.create_connection(addr, timeout=self._connect_timeout) - if proxy: return socks.create_connection(addr, timeout=self._connect_timeout, source_address=source, **proxy) else: diff --git a/src/paho/mqtt/properties.py b/src/paho/mqtt/properties.py index dbcf543e..575ca921 100644 --- a/src/paho/mqtt/properties.py +++ b/src/paho/mqtt/properties.py @@ -52,10 +52,8 @@ def readInt32(buf): def writeUTF(data): # data could be a string, or bytes. If string, encode into bytes with utf-8 - if sys.version_info[0] < 3: - data = bytearray(data, 'utf-8') - else: - data = data if type(data) == type(b"") else bytes(data, "utf-8") + if not isinstance(data, bytes): + data = bytes(data, "utf-8") return writeInt16(len(data)) + data @@ -109,10 +107,7 @@ def encode(x): x //= 128 if x > 0: digit |= 0x80 - if sys.version_info[0] >= 3: - buffer += bytes([digit]) - else: - buffer += bytes(chr(digit)) + buffer += bytes([digit]) if x == 0: break return buffer @@ -345,10 +340,7 @@ def writeProperty(self, identifier, type, value): buffer = b"" buffer += VariableByteIntegers.encode(identifier) # identifier if type == self.types.index("Byte"): # value - if sys.version_info[0] < 3: - buffer += chr(value) - else: - buffer += bytes([value]) + buffer += bytes([value]) elif type == self.types.index("Two Byte Integer"): buffer += writeInt16(value) elif type == self.types.index("Four Byte Integer"): @@ -412,8 +404,6 @@ def getNameFromIdent(self, identifier): return rc def unpack(self, buffer): - if sys.version_info[0] < 3: - buffer = bytearray(buffer) self.clear() # deserialize properties into attributes from buffer received from network propslen, VBIlen = VariableByteIntegers.decode(buffer) diff --git a/src/paho/mqtt/reasoncodes.py b/src/paho/mqtt/reasoncodes.py index c42e5ba9..1b54236a 100644 --- a/src/paho/mqtt/reasoncodes.py +++ b/src/paho/mqtt/reasoncodes.py @@ -162,8 +162,6 @@ def set(self, name): def unpack(self, buffer): c = buffer[0] - if sys.version_info[0] < 3: - c = ord(c) name = self.__getName__(self.packetType, c) self.value = self.getId(name) return 1 diff --git a/src/paho/mqtt/subscribeoptions.py b/src/paho/mqtt/subscribeoptions.py index 5b4f0733..a88f4c88 100644 --- a/src/paho/mqtt/subscribeoptions.py +++ b/src/paho/mqtt/subscribeoptions.py @@ -16,7 +16,6 @@ ******************************************************************* """ -import sys class MQTTException(Exception): @@ -74,11 +73,7 @@ def pack(self): retainAsPublished = 1 if self.retainAsPublished else 0 data = [(self.retainHandling << 4) | (retainAsPublished << 3) | (noLocal << 2) | self.QoS] - if sys.version_info[0] >= 3: - buffer = bytes(data) - else: - buffer = bytearray(data) - return buffer + return bytes(data) def unpack(self, buffer): b0 = buffer[0] diff --git a/tests/test_websockets.py b/tests/test_websockets.py index 6aa55f72..12d6ead9 100644 --- a/tests/test_websockets.py +++ b/tests/test_websockets.py @@ -1,10 +1,7 @@ import socket import sys -if sys.version_info < (3, 0): - from mock import Mock -else: - from unittest.mock import Mock +from unittest.mock import Mock import pytest @@ -36,10 +33,7 @@ def iter_response(): it = iter_response() def fakerecv(*args): - if sys.version_info < (3, 0): - return next(it) - else: - return bytes([next(it)]) + return bytes([next(it)]) mocksock = Mock( spec_set=socket.socket, From 4835c36fa67734364e7d85f17c9f6f61950a9b6e Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 20 Dec 2023 13:15:42 +0200 Subject: [PATCH 2/4] Note that Python 3.7+ is required Signed-off-by: Aarni Koskela --- README.rst | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index e05c67b1..92ec9a17 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ This document describes the source code for the `Eclipse Paho `_ broker to publish messages, and to subscribe to topics and receive published messages. It also provides some helper functions to make publishing one off messages to an MQTT server very straightforward. -It supports Python 3.6+. +It supports Python 3.7+. The MQTT protocol is a machine-to-machine (M2M)/"Internet of Things" connectivity protocol. Designed as an extremely lightweight publish/subscribe messaging transport, it is useful for connections with remote locations where a small code footprint is required and/or network bandwidth is at a premium. diff --git a/setup.py b/setup.py index e9580ae2..b2172c3a 100644 --- a/setup.py +++ b/setup.py @@ -29,6 +29,7 @@ package_dir={'': 'src'}, include_package_data=True, install_requires=requirements, + python_requires='>=3.7', license='Eclipse Public License v2.0 / Eclipse Distribution License v1.0', zip_safe=False, keywords='paho', @@ -42,7 +43,6 @@ 'Natural Language :: English', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', From 75baca24b4b3790a111eec100d98c17bea45cc62 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 20 Dec 2023 16:15:11 +0200 Subject: [PATCH 3/4] Remove mock requirement (not required with Python 3) Signed-off-by: Aarni Koskela --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5dc884df..4a81f733 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -mock==3.0.5 pylama==7.7.1 pytest==5.2.2; python_version >= '3.0' pytest-runner==5.2 From c92af978d7fd64c93c5b6d4f67e113b067ebcba5 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 20 Dec 2023 16:15:52 +0200 Subject: [PATCH 4/4] Get rid of `six` test dependency Signed-off-by: Aarni Koskela --- setup.py | 2 +- tests/test_websocket_integration.py | 2 +- tests/testsupport/broker.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index b2172c3a..f00ceb43 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ readme = readme_file.read().decode('utf-8') requirements = [] -test_requirements = ['pytest', 'pylama', 'six'] +test_requirements = ['pytest', 'pylama'] needs_pytest = {'pytest', 'test', 'ptr'}.intersection(sys.argv) setup_requirements = ['pytest-runner'] if needs_pytest else [] extra_requirements = {'proxy': ['PySocks']} diff --git a/tests/test_websocket_integration.py b/tests/test_websocket_integration.py index 86388dc5..0d623ca9 100644 --- a/tests/test_websocket_integration.py +++ b/tests/test_websocket_integration.py @@ -1,10 +1,10 @@ import base64 import hashlib import re +import socketserver from collections import OrderedDict import pytest -from six.moves import socketserver from testsupport.broker import fake_websocket_broker import paho.mqtt.client as client diff --git a/tests/testsupport/broker.py b/tests/testsupport/broker.py index 92b80451..b901a6df 100644 --- a/tests/testsupport/broker.py +++ b/tests/testsupport/broker.py @@ -1,9 +1,9 @@ import contextlib import socket +import socketserver import threading import pytest -from six.moves import socketserver class FakeBroker: