Skip to content

Commit

Permalink
PROTON-2323: [Python] Remove all Python 2 compatibility code
Browse files Browse the repository at this point in the history
Obviously, from this point the proton library will no longer work with
Python 2.
  • Loading branch information
astitcher committed May 13, 2021
1 parent bee2418 commit 8c0f668
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 163 deletions.
1 change: 0 additions & 1 deletion python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ endif()
set (pysrc-generated cproton.py)
set (pysrc
proton/__init__.py
proton/_compat.py
proton/_common.py
proton/_condition.py
proton/_data.py
Expand Down
72 changes: 0 additions & 72 deletions python/proton/_compat.py

This file was deleted.

42 changes: 8 additions & 34 deletions python/proton/_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,11 @@
pn_data_put_string, pn_data_put_symbol, pn_data_put_timestamp, pn_data_put_ubyte, pn_data_put_uint, \
pn_data_put_ulong, pn_data_put_ushort, pn_data_put_uuid, pn_data_rewind, pn_data_type, pn_data_widen, pn_error_text

from . import _compat
from ._common import Constant
from ._exceptions import DataException, EXCEPTIONS

#
# Hacks to provide Python2 <---> Python3 compatibility
#
# The results are
# | |long|unicode|
# |Python2|long|unicode|
# |Python3| int| str|
try:
long()
except NameError:
long = int
try:
unicode()
except NameError:
unicode = str
long = int
unicode = str


class UnmappedType:
Expand Down Expand Up @@ -644,8 +630,8 @@ def type_name(amqptype):
"""
Return a string name for an AMQP type.
:param type: Numeric Proton AMQP type (`enum pn_type_t`)
:type type: integer
:param amqptype: Numeric Proton AMQP type (`enum pn_type_t`)
:type amqptype: integer
:rtype: String describing the AMQP type with numeric value `amqptype`
"""
return Data.type_names[amqptype]
Expand Down Expand Up @@ -1000,7 +986,7 @@ def put_long(self, l):
Puts a signed long value.
:param l: an integral value in the range :math:`-(2^{63})` to :math:`2^{63} - 1` inclusive.
:type ul: ``int``, ``long``
:type l: ``int``, ``long``
:raise: * ``AssertionError`` if parameter is out of the range :math:`-(2^{63})` to :math:`2^{63} - 1` inclusive.
* :exc:`DataException` if there is a Proton error.
"""
Expand Down Expand Up @@ -1296,7 +1282,7 @@ def get_char(self):
:return: If the current node is a char, its value, 0 otherwise.
:rtype: :class:`char`
"""
return char(_compat.unichr(pn_data_get_char(self._data)))
return char(chr(pn_data_get_char(self._data)))

def get_ulong(self):
"""
Expand Down Expand Up @@ -1636,21 +1622,9 @@ def put_py_array(self, a):
Array: put_py_array,
AnnotationDict: put_dict,
PropertyDict: put_dict,
SymbolList: put_sequence
SymbolList: put_sequence,
memoryview: put_memoryview
}
# for Python 3.x, long is merely an alias for int, but for Python 2.x
# we need to add an explicit int since it is a different type
if int not in put_mappings:
put_mappings[int] = put_int
# Python >=3.0 has 'memoryview', <=2.5 has 'buffer', >=2.6 has both.
try:
put_mappings[memoryview] = put_memoryview
except NameError:
pass
try:
put_mappings[buffer] = put_buffer
except NameError:
pass
get_mappings = {
NULL: lambda s: None,
BOOL: get_bool,
Expand Down
5 changes: 2 additions & 3 deletions python/proton/_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import select
import time

from ._compat import socket_errno

PN_INVALID_SOCKET = -1

Expand Down Expand Up @@ -62,7 +61,7 @@ def connect(addr):
try:
s.connect(addr[4])
except socket.error as e:
if socket_errno(e) not in (errno.EINPROGRESS, errno.EWOULDBLOCK, errno.EAGAIN):
if e.errno not in (errno.EINPROGRESS, errno.EWOULDBLOCK, errno.EAGAIN):
raise
return s

Expand Down Expand Up @@ -158,7 +157,7 @@ def select_inner(timeout):
try:
r, w, ex = select_inner(timeout)
except select.error as e:
if socket_errno(e) != errno.EINTR:
if e.errno != errno.EINTR:
raise
r, w, ex = ([], [], [])

Expand Down
54 changes: 20 additions & 34 deletions python/proton/_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,19 @@
pn_message_get_content_type, pn_message_get_creation_time, pn_message_get_delivery_count, \
pn_message_get_expiry_time, pn_message_get_group_id, pn_message_get_group_sequence, pn_message_get_priority, \
pn_message_get_reply_to, pn_message_get_reply_to_group_id, pn_message_get_subject, pn_message_get_ttl, \
pn_message_get_user_id, pn_message_id, pn_message_instructions, pn_message_is_durable, pn_message_is_first_acquirer, \
pn_message_is_inferred, pn_message_properties, pn_message_set_address, pn_message_set_content_encoding, \
pn_message_set_content_type, pn_message_set_creation_time, pn_message_set_delivery_count, pn_message_set_durable, \
pn_message_set_expiry_time, pn_message_set_first_acquirer, pn_message_set_group_id, pn_message_set_group_sequence, \
pn_message_set_inferred, pn_message_set_priority, pn_message_set_reply_to, pn_message_set_reply_to_group_id, \
pn_message_set_subject, pn_message_set_ttl, pn_message_set_user_id

from . import _compat
pn_message_get_user_id, pn_message_id, pn_message_instructions, pn_message_is_durable, \
pn_message_is_first_acquirer, pn_message_is_inferred, pn_message_properties, pn_message_set_address, \
pn_message_set_content_encoding, pn_message_set_content_type, pn_message_set_creation_time, \
pn_message_set_delivery_count, pn_message_set_durable, pn_message_set_expiry_time, pn_message_set_first_acquirer, \
pn_message_set_group_id, pn_message_set_group_sequence, pn_message_set_inferred, pn_message_set_priority, \
pn_message_set_reply_to, pn_message_set_reply_to_group_id, pn_message_set_subject, \
pn_message_set_ttl, pn_message_set_user_id

from ._common import isinteger, millis2secs, secs2millis, unicode2utf8, utf82unicode
from ._data import char, Data, decimal128, symbol, ulong, AnnotationDict
from ._data import char, Data, symbol, ulong, AnnotationDict
from ._endpoints import Link
from ._exceptions import EXCEPTIONS, MessageException

#
# Hack to provide Python2 <---> Python3 compatibility
try:
unicode()
except NameError:
unicode = str


class Message(object):
"""The :py:class:`Message` class is a mutable holder of message content.
Expand Down Expand Up @@ -72,7 +65,7 @@ def __init__(self, body=None, **kwargs):
self.annotations = None
self.properties = None
self.body = body
for k, v in _compat.iteritems(kwargs):
for k, v in kwargs.items():
getattr(self, k) # Raise exception if it's not a valid attribute.
setattr(self, k, v)

Expand All @@ -89,35 +82,27 @@ def _check(self, err):
return err

def _check_property_keys(self):
'''
"""
AMQP allows only string keys for properties. This function checks that this requirement is met
and raises a MessageException if not. However, in certain cases, conversions to string are
automatically performed:
1. When a key is a user-defined (non-AMQP) subclass of unicode/str (py2/py3).
AMQP types symbol and char, although derived from unicode/str, are not converted,
1. When a key is a user-defined (non-AMQP) subclass of str.
AMQP types symbol and char, although derived from str, are not converted,
and result in an exception.
2. In Py2, when a key is binary. This is a convenience for Py2 users that encode
string literals without using the u'' prefix. In Py3, this is not the case, and
using a binary key will result in an error. AMQP type decimal128, which is derived
from binary, will not be converted, and will result in an exception.
'''
"""
# We cannot make changes to the dict while iterating, so we
# must save and make the changes afterwards
changed_keys = []
for k in self.properties.keys():
if isinstance(k, unicode):
# Py2 & Py3 strings and their subclasses
if isinstance(k, str):
# strings and their subclasses
if type(k) is symbol or type(k) is char:
# Exclude symbol and char
raise MessageException('Application property key is not string type: key=%s %s' % (str(k), type(k)))
if type(k) is not unicode:
if type(k) is not str:
# Only for string subclasses, convert to string
changed_keys.append((k, unicode(k)))
elif isinstance(k, str) and not (type(k) is decimal128):
# Py2 only: If key is binary string then convert to unicode. Exclude bytes subclass decimal128.
changed_keys.append((k, k.decode('utf-8')))
changed_keys.append((k, str(k)))
else:
# Anything else: raise exception
raise MessageException('Application property key is not string type: key=%s %s' % (str(k), type(k)))
Expand Down Expand Up @@ -222,7 +207,8 @@ def _set_priority(self, value):
higher priority. The number of available priorities depends
on the implementation, but AMQP defines the default priority as
the value ``4``. See the
`OASIS AMQP 1.0 standard <http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-header>`_
`OASIS AMQP 1.0 standard
<http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-header>`_
for more details on message priority.
:type: ``int``
Expand Down
11 changes: 7 additions & 4 deletions python/proton/_reactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import logging
import re
import os
import queue
import time
import traceback
import uuid
Expand All @@ -46,9 +47,6 @@

from ._io import IO

from . import _compat
from ._compat import queue


_logger = logging.getLogger("proton")

Expand Down Expand Up @@ -217,7 +215,12 @@ def _check_errors(self):
for exc, value, tb in self.errors[:-1]:
traceback.print_exception(exc, value, tb)
exc, value, tb = self.errors[-1]
_compat.raise_(exc, value, tb)
if value is None:
value = exc()
if tb is None:
raise value
else:
raise value.with_traceback(tb)

def process(self):
# result = pn_reactor_process(self._impl)
Expand Down
4 changes: 2 additions & 2 deletions python/proton/_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import socket

from ._compat import urlparse, urlunparse, quote, unquote
from urllib.parse import urlparse, urlunparse, quote, unquote


class Url(object):
Expand Down Expand Up @@ -168,7 +168,7 @@ def _parse_host_port(nl):
port = None
if not host:
host = None
return (host, port)
return host, port

@property
def path(self):
Expand Down
8 changes: 6 additions & 2 deletions python/tests/proton_tests/codec.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from uuid import uuid4

from proton import *
from proton._compat import raise_

from . import common

Expand Down Expand Up @@ -131,7 +130,12 @@ def put(self, putter, v):
putter(v)
except Exception:
etype, value, trace = sys.exc_info()
raise_(etype, etype("%s(%r): %s" % (putter.__name__, v, value)), trace)
v = etype("%s(%r): %s" % (putter.__name__, v, value))
if trace is None:
raise v
else:
raise v.with_traceback(trace)

return putter

# (bits, signed) for each integer type
Expand Down
17 changes: 6 additions & 11 deletions python/tests/proton_tests/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,21 +110,14 @@ def testReplyToGroupId(self):
self._test_str("reply_to_group_id")


try:
long()
except NameError:
long = int
try:
unicode()
except NameError:
unicode = str
long = int
unicode = str


class CodecTest(Test):

def testProperties(self):
self.msg.properties = {}
self.msg.properties['key'] = 'value'
self.msg.properties = {'key': 'value'}
data = self.msg.encode()

msg2 = Message()
Expand Down Expand Up @@ -199,7 +192,9 @@ def testBinaryPropertyKey(self):
for k in msg2.properties:
assert type(k) is unicode, 'non-string key %s %s' % (k, type(k))

def testAnnotationsSymbolicAndUlongKey(self, a={symbol('one'): 1, 'two': 2, ulong(3): 'three'}):
def testAnnotationsSymbolicAndUlongKey(self, a=None):
if a is None:
a = {symbol('one'): 1, 'two': 2, ulong(3): 'three'}
self.msg.annotations = a
data = self.msg.encode()

Expand Down

0 comments on commit 8c0f668

Please sign in to comment.