Python 3 updates

- Simplify super()
- Remove object inheritance
- Simplify package namespacing
- Remove BinaryWrapper
- Remove python 2 code paths
This commit is contained in:
Andrew Rabert 2018-11-28 11:32:54 -05:00
parent e9bbe21d3c
commit fd3f8b3008
9 changed files with 24 additions and 89 deletions

View file

@ -33,6 +33,3 @@ Bundled Transcoders
.. autoclass:: MsgPackTranscoder
:members:
.. autoclass:: BinaryWrapper
:members:

View file

@ -51,7 +51,11 @@ setuptools.setup(
'Topic :: Software Development :: Libraries',
'Topic :: Software Development :: Libraries :: Python Modules'
],
packages=setuptools.find_packages(),
packages=[
'sprockets',
'sprockets.mixins',
'sprockets.mixins.mediatype'
],
install_requires=install_requires,
tests_require=tests_require,
extras_require={

View file

@ -1 +0,0 @@
__import__('pkg_resources').declare_namespace(__name__)

View file

@ -1 +0,0 @@
__import__('pkg_resources').declare_namespace(__name__)

View file

@ -12,7 +12,7 @@ except ImportError as error: # pragma no cover
def _error_closure(*args, **kwargs):
raise error
class ErrorClosureClass(object):
class ErrorClosureClass:
def __init__(self, *args, **kwargs):
raise error

View file

@ -43,7 +43,7 @@ SETTINGS_KEY = 'sprockets.mixins.mediatype.ContentSettings'
_warning_issued = False
class ContentSettings(object):
class ContentSettings:
"""
Content selection settings.
@ -276,7 +276,7 @@ def set_default_content_type(application, content_type, encoding=None):
settings.default_encoding = encoding
class ContentMixin(object):
class ContentMixin:
"""
Mix this in to add some content handling methods.
@ -298,7 +298,7 @@ class ContentMixin(object):
"""
def initialize(self):
super(ContentMixin, self).initialize()
super().initialize()
self._request_body = None
self._best_response_match = None
self._logger = getattr(self, 'logger', logger)

View file

@ -10,7 +10,7 @@ Basic content handlers.
from tornado import escape
class BinaryContentHandler(object):
class BinaryContentHandler:
"""
Pack and unpack binary types.
@ -58,7 +58,7 @@ class BinaryContentHandler(object):
return self._unpack(data_bytes)
class TextContentHandler(object):
class TextContentHandler:
"""
Transcodes between textual and object representations.

View file

@ -7,7 +7,6 @@ Bundled media type transcoders.
"""
import base64
import json
import sys
import uuid
import collections
@ -20,28 +19,6 @@ except ImportError:
from sprockets.mixins.mediatype import handlers
class BinaryWrapper(bytes):
"""
Ensures that a Python 2 ``str`` is treated as binary.
Since :class:`bytes` is a synonym for :class:`str` in Python 2,
you cannot distinguish between something that should be binary
and something that should be encoded as a string. This is a
problem in formats `such as msgpack`_ where binary data and
strings are encoded differently. The :class:`MsgPackTranscoder`
accomodates this by trying to UTF-8 encode a :class:`str` instance
and falling back to binary encoding if the transcode fails.
You can avoid this by wrapping binary content in an instance of
this class. The transcoder will then treat it as a binary payload
instead of trying to detect whether it is a string or not.
.. _such as msgpack: http://msgpack.org
"""
pass
class JSONTranscoder(handlers.TextContentHandler):
"""
JSON transcoder instance.
@ -73,7 +50,7 @@ class JSONTranscoder(handlers.TextContentHandler):
def __init__(self, content_type='application/json',
default_encoding='utf-8'):
super(JSONTranscoder, self).__init__(content_type, self.dumps,
super().__init__(content_type, self.dumps,
self.loads, default_encoding)
self.dump_options = {
'default': self.dump_object,
@ -128,13 +105,6 @@ class JSONTranscoder(handlers.TextContentHandler):
| :class:`uuid.UUID` | Same as ``str(value)`` |
+----------------------------+---------------------------------------+
.. warning::
:class:`bytes` instances are treated as character strings by the
standard JSON module in Python 2.7 so the *default* object hook
is never called. In other words, :class:`bytes` values will not
be serialized as Base64 strings in Python 2.7.
"""
if isinstance(obj, uuid.UUID):
return str(obj)
@ -160,17 +130,14 @@ class MsgPackTranscoder(handlers.BinaryContentHandler):
.. _msgpack format: http://msgpack.org/index.html
"""
if sys.version_info[0] < 3:
PACKABLE_TYPES = (bool, int, float, long)
else:
PACKABLE_TYPES = (bool, int, float)
PACKABLE_TYPES = (bool, int, float)
def __init__(self, content_type='application/msgpack'):
if umsgpack is None:
raise RuntimeError('Cannot import MsgPackTranscoder, '
'umsgpack is not available')
super(MsgPackTranscoder, self).__init__(content_type, self.packb,
super().__init__(content_type, self.packb,
self.unpackb)
def packb(self, data):
@ -214,8 +181,6 @@ class MsgPackTranscoder(handlers.BinaryContentHandler):
+-------------------------------+-------------------------------+
| :class:`memoryview` | `bin family`_ |
+-------------------------------+-------------------------------+
| :class:`.BinaryWrapper` | `bin family`_ |
+-------------------------------+-------------------------------+
| :class:`collections.Sequence` | `array family`_ |
+-------------------------------+-------------------------------+
| :class:`collections.Set` | `array family`_ |
@ -227,17 +192,6 @@ class MsgPackTranscoder(handlers.BinaryContentHandler):
.. note::
:class:`str` and :class:`bytes` are the same before Python 3.
If you want a value to be treated as a binary value, then you
should wrap it in :class:`.BinaryWrapper` if there is any
chance of running under Python 2.7.
The processing of :class:`str` in Python 2.x attempts to
encode the string as a UTF-8 stream. If the ``encode`` succeeds,
then the string is encoded according to the `str family`_.
If ``encode`` fails, then the string is encoded according to
the `bin family`_ .
.. _nil byte: https://github.com/msgpack/msgpack/blob/
0b8f5ac67cdd130f4d4d4fe6afb839b989fdb86a/spec.md#formats-nil
.. _true byte: https://github.com/msgpack/msgpack/blob/
@ -277,16 +231,6 @@ class MsgPackTranscoder(handlers.BinaryContentHandler):
if hasattr(datum, 'isoformat'):
datum = datum.isoformat()
if sys.version_info[0] < 3 and isinstance(datum, (str, unicode)):
if isinstance(datum, str) and not isinstance(datum, BinaryWrapper):
# try to decode this into a string to make the common
# case work. If we fail, then send along the bytes.
try:
datum = datum.decode('utf-8')
except UnicodeDecodeError:
pass
return datum
if isinstance(datum, (bytes, str)):
return datum

View file

@ -4,7 +4,6 @@ import json
import os
import pickle
import struct
import sys
import unittest
import uuid
@ -28,7 +27,7 @@ class UTC(datetime.tzinfo):
return 'UTC'
class Context(object):
class Context:
"""Super simple class to call setattr on"""
def __init__(self):
self.settings = {}
@ -111,16 +110,16 @@ class GetRequestBodyTests(testing.AsyncHTTPTestCase):
def test_that_request_with_unhandled_type_results_in_415(self):
response = self.fetch(
'/', method='POST', headers={'Content-Type': 'application/xml'},
body=(u'<request><name>value</name>'
u'<embedded><utf8>\u2731</utf8></embedded>'
u'</request>').encode('utf-8'))
body=('<request><name>value</name>'
'<embedded><utf8>\u2731</utf8></embedded>'
'</request>').encode('utf-8'))
self.assertEqual(response.code, 415)
def test_that_msgpack_request_returns_default_type(self):
body = {
'name': 'value',
'embedded': {
'utf8': u'\u2731'
'utf8': '\u2731'
}
}
response = self.fetch('/', method='POST', body=umsgpack.packb(body),
@ -140,7 +139,7 @@ class GetRequestBodyTests(testing.AsyncHTTPTestCase):
class JSONTranscoderTests(unittest.TestCase):
def setUp(self):
super(JSONTranscoderTests, self).setUp()
super().setUp()
self.transcoder = transcoders.JSONTranscoder()
def test_that_uuids_are_dumped_as_strings(self):
@ -161,13 +160,6 @@ class JSONTranscoderTests(unittest.TestCase):
self.assertEqual(dumped.replace(' ', ''),
'{"now":"%s"}' % obj['now'].isoformat())
@unittest.skipIf(sys.version_info[0] == 2, 'bytes unsupported on python 2')
def test_that_bytes_are_base64_encoded(self):
bin = bytes(os.urandom(127))
dumped = self.transcoder.dumps({'bin': bin})
self.assertEqual(
dumped, '{"bin":"%s"}' % base64.b64encode(bin).decode('ASCII'))
def test_that_bytearrays_are_base64_encoded(self):
bin = bytearray(os.urandom(127))
dumped = self.transcoder.dumps({'bin': bin})
@ -226,7 +218,7 @@ class ContentSettingsTests(unittest.TestCase):
class ContentFunctionTests(unittest.TestCase):
def setUp(self):
super(ContentFunctionTests, self).setUp()
super().setUp()
self.context = Context()
def test_that_add_binary_content_type_creates_binary_handler(self):
@ -281,11 +273,11 @@ class ContentFunctionTests(unittest.TestCase):
class MsgPackTranscoderTests(unittest.TestCase):
def setUp(self):
super(MsgPackTranscoderTests, self).setUp()
super().setUp()
self.transcoder = transcoders.MsgPackTranscoder()
def test_that_strings_are_dumped_as_strings(self):
dumped = self.transcoder.packb(u'foo')
dumped = self.transcoder.packb('foo')
self.assertEqual(self.transcoder.unpackb(dumped), 'foo')
self.assertEqual(dumped, pack_string('foo'))
@ -373,6 +365,6 @@ class MsgPackTranscoderTests(unittest.TestCase):
def test_that_utf8_values_can_be_forced_to_bytes(self):
data = b'a ascii value'
dumped = self.transcoder.packb(transcoders.BinaryWrapper(data))
dumped = self.transcoder.packb(data)
self.assertEqual(self.transcoder.unpackb(dumped), data)
self.assertEqual(dumped, pack_bytes(data))