Skip to content

Commit

Permalink
Merge pull request eclipse-paho#767 from akx/version-compat-bits
Browse files Browse the repository at this point in the history
Version compatibility bits (remove old compat code, bump requirement to 3.7+)
  • Loading branch information
PierreF authored Dec 20, 2023
2 parents 4f06670 + c92af97 commit a2d01de
Show file tree
Hide file tree
Showing 10 changed files with 26 additions and 84 deletions.
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This document describes the source code for the `Eclipse Paho <http://eclipse.or

This code provides a client class which enable applications to connect to an `MQTT <http://mqtt.org/>`_ 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.

Expand Down
2 changes: 0 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
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
7 changes: 2 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@
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']}

if sys.version_info < (3, 0):
test_requirements += ['mock']

setup(
name='paho-mqtt',
version=__version__,
Expand All @@ -32,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',
Expand All @@ -45,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',
Expand Down
58 changes: 14 additions & 44 deletions src/paho/mqtt/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.')

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -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'))

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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:
Expand Down
18 changes: 4 additions & 14 deletions src/paho/mqtt/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"):
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 0 additions & 2 deletions src/paho/mqtt/reasoncodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 1 addition & 6 deletions src/paho/mqtt/subscribeoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
*******************************************************************
"""

import sys


class MQTTException(Exception):
Expand Down Expand Up @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion tests/test_websocket_integration.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
10 changes: 2 additions & 8 deletions tests/test_websockets.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion tests/testsupport/broker.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import contextlib
import socket
import socketserver
import threading

import pytest
from six.moves import socketserver


class FakeBroker:
Expand Down

0 comments on commit a2d01de

Please sign in to comment.