Skip to content

Commit feb0361

Browse files
committed
Merge pull request #219 in LCL/wolframclientforpython from bugfix/381526-memoryview to master
* commit '6e245c5ba0058a3e69ff655fe1268aec804ae137': adding tests using dispatch to implement force_bytes and force_text adding comment for py2 adding a custom rule for memoryview fixing six need to add encoding for py2
2 parents 876ad1d + 6e245c5 commit feb0361

6 files changed

Lines changed: 122 additions & 67 deletions

File tree

wolframclient/deserializers/wxf/wxfparser.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,28 @@
1010
)
1111
from wolframclient.serializers.wxfencoder.streaming import ExactSizeReader, ZipCompressedReader
1212
from wolframclient.utils import six
13+
from wolframclient.utils.dispatch import Dispatch
14+
15+
wxf_input_to_buffer = Dispatch()
16+
17+
@wxf_input_to_buffer.dispatch((six.binary_type, six.buffer_types))
18+
def encode_buffer(wxf_input):
19+
return six.BytesIO(wxf_input)
20+
21+
if six.PY2:
22+
@wxf_input_to_buffer.dispatch(memoryview, replace_existing = True)
23+
def encode_buffer(wxf_input):
24+
return six.BytesIO(wxf_input.tobytes())
25+
26+
@wxf_input_to_buffer.dispatch(object)
27+
def encode_default(wxf_input):
28+
if hasattr(wxf_input, 'read'):
29+
return wxf_input
30+
raise TypeError(
31+
"Class %s neither implements a read method nor is a binary type."
32+
% wxf_input.__class__.__name__
33+
)
34+
1335

1436

1537
class WXFParser(object):
@@ -58,15 +80,8 @@ def __init__(self, wxf_input):
5880
"""WXF parser returning Python object from a WXF encoded byte sequence.
5981
"""
6082
self.context = SerializationContext()
61-
if isinstance(wxf_input, (six.binary_type, six.buffer_types)):
62-
self.reader = six.BytesIO(wxf_input)
63-
elif hasattr(wxf_input, "read"):
64-
self.reader = wxf_input
65-
else:
66-
raise TypeError(
67-
"Class %s neither implements a read method nor is a binary type."
68-
% wxf_input.__class__.__name__
69-
)
83+
self.reader = wxf_input_to_buffer(wxf_input)
84+
7085
version, compress = self.parse_header()
7186
if compress == True:
7287
self.reader = ZipCompressedReader(self.reader)

wolframclient/tests/core_functions.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
# -*- coding: utf-8 -*-
12
from __future__ import absolute_import, print_function, unicode_literals
23

34
from wolframclient.utils import six
45
from wolframclient.utils.dispatch import Dispatch
6+
from wolframclient.utils.encoding import force_bytes, force_text
57
from wolframclient.utils.functional import composition, flatten, iterate, partition, riffle
68
from wolframclient.utils.tests import TestCase as BaseTestCase
79

@@ -38,6 +40,24 @@ def test_partition(self):
3840

3941
self.assertEqual(list(partition([], 3)), [])
4042

43+
def test_force_encoding(self):
44+
45+
self.assertEqual(force_text("aà"), "aà")
46+
self.assertEqual(force_text(abs), "<built-in function abs>")
47+
self.assertEqual(force_text(b"a\xc3\xa0"), "aà")
48+
49+
self.assertEqual(force_text(memoryview(b"abc")), "abc")
50+
self.assertEqual(force_text(bytearray(b"abc")), "abc")
51+
52+
self.assertEqual(force_bytes(b"abc"), b"abc")
53+
self.assertEqual(force_bytes(abs), b"<built-in function abs>")
54+
self.assertEqual(force_bytes("aà"), b"a\xc3\xa0")
55+
56+
self.assertEqual(force_text(force_bytes("aà")), "aà")
57+
58+
self.assertEqual(force_bytes(memoryview(b"abc")), b"abc")
59+
self.assertEqual(force_bytes(bytearray(b"abc")), b"abc")
60+
4161
def test_dispatch(self):
4262

4363
normalizer = Dispatch()

wolframclient/tests/deserializers/wxf_deserialize.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# -*- coding: utf-8 -*-
2+
13
from __future__ import absolute_import, print_function, unicode_literals
24

35
import decimal

wolframclient/tests/serializers/wxf_serialization.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# -*- coding: utf-8 -*-
2+
13
from __future__ import absolute_import, print_function, unicode_literals
24

35
import decimal

wolframclient/utils/encoding.py

Lines changed: 74 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,87 @@
11
from __future__ import absolute_import, print_function, unicode_literals
22

33
from wolframclient.utils import six
4+
from wolframclient.utils.dispatch import Dispatch
45
from wolframclient.utils.functional import map
56

7+
force_text = Dispatch()
68

7-
def force_text(s, encoding="utf-8", errors="strict"):
8-
"""
9-
Similar to smart_text, except that lazy instances are resolved to
10-
strings, rather than kept as lazy objects.
11-
12-
If strings_only is True, don't convert (some) non-string-like objects.
13-
"""
14-
# Handle the common case first for performance reasons.
15-
if isinstance(s, six.text_type):
16-
return s
17-
if not isinstance(s, six.string_types):
18-
if six.PY3:
19-
if isinstance(s, bytes):
20-
s = six.text_type(s, encoding, errors)
21-
else:
22-
s = six.text_type(s)
23-
elif hasattr(s, "__unicode__"):
24-
s = six.text_type(s)
9+
10+
@force_text.dispatch(six.text_type)
11+
def encode(s, encoding="utf-8", errors="strict"):
12+
return s
13+
14+
15+
@force_text.dispatch(six.binary_type, replace_existing=True)
16+
def encode(s, encoding="utf-8", errors="strict"):
17+
return s.decode(encoding, errors)
18+
19+
20+
if not six.PY2:
21+
22+
@force_text.dispatch(object)
23+
def encode(s, encoding="utf-8", errors="strict"):
24+
return six.text_type(s)
25+
26+
27+
else:
28+
29+
@force_text.dispatch(object)
30+
def encode(s, encoding="utf-8", errors="strict"):
31+
if hasattr(s, "__unicode__"):
32+
return six.text_type(s)
2533
else:
26-
s = six.text_type(bytes(s), encoding, errors)
27-
else:
28-
# Note: We use .decode() here, instead of six.text_type(s, encoding,
29-
# errors), so that if s is a SafeBytes, it ends up being a
30-
# SafeText at the end.
31-
s = s.decode(encoding, errors)
34+
return six.text_type(bytes(s), encoding, errors)
35+
36+
37+
force_bytes = Dispatch()
38+
39+
40+
@force_bytes.dispatch(six.string_types)
41+
def encode(s, encoding="utf-8", errors="strict"):
42+
return s.encode(encoding, errors)
43+
44+
45+
@force_bytes.dispatch(six.binary_type, replace_existing=True)
46+
def encode(s, encoding="utf-8", errors="strict"):
3247
return s
3348

3449

35-
def force_bytes(s, encoding="utf-8", errors="strict"):
36-
"""
37-
If strings_only is True, don't convert (some) non-string-like objects.
38-
"""
39-
# Handle the common case first for performance reasons.
40-
if isinstance(s, bytes):
41-
return s
42-
43-
if isinstance(s, six.buffer_types):
44-
return bytes(s)
45-
46-
if not isinstance(s, six.string_types):
47-
try:
48-
if six.PY3:
49-
return six.text_type(s).encode(encoding)
50-
else:
51-
return bytes(s)
52-
except UnicodeEncodeError:
53-
if isinstance(s, Exception):
54-
# An Exception subclass containing non-ASCII data that doesn't
55-
# know how to print itself properly. We shouldn't raise a
56-
# further exception.
57-
return b" ".join(force_bytes(arg, encoding, errors=errors) for arg in s)
58-
return six.text_type(s).encode(encoding, errors)
59-
else:
60-
return s.encode(encoding, errors)
50+
@force_bytes.dispatch(six.buffer_types, replace_existing=True)
51+
def encode(s, encoding="utf-8", errors="strict"):
52+
return six.binary_type(s)
53+
54+
55+
if six.PY2:
56+
57+
@force_bytes.dispatch(memoryview, replace_existing=True)
58+
def encode(s, encoding="utf-8", errors="strict"):
59+
return s.tobytes()
60+
61+
62+
if not six.PY2:
63+
64+
def encode_default(s, encoding="utf-8", errors="strict"):
65+
return six.text_type(s).encode(encoding)
66+
67+
68+
else:
69+
70+
def encode_default(s, encoding="utf-8", errors="strict"):
71+
return six.binary_type(s)
72+
73+
74+
@force_bytes.dispatch(object)
75+
def encode(s, encoding="utf-8", errors="strict"):
76+
try:
77+
return encode_default(s, encoding, errors)
78+
except UnicodeEncodeError:
79+
if isinstance(s, Exception):
80+
# An Exception subclass containing non-ASCII data that doesn't
81+
# know how to print itself properly. We shouldn't raise a
82+
# further exception.
83+
return b" ".join(force_bytes(arg, encoding, errors=errors) for arg in s)
84+
return six.text_type(s).encode(encoding, errors)
6185

6286

6387
def safe_force_text(obj):

wolframclient/utils/six.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
StringIO = io.StringIO
3535
BytesIO = io.BytesIO
3636

37-
memoryview = memoryview
3837
buffer_types = (bytes, bytearray, memoryview)
3938

4039
else:
@@ -49,13 +48,6 @@
4948

5049
StringIO = BytesIO = StringIO.StringIO
5150

52-
# memoryview and buffer are not strictly equivalent, but should be fine for
53-
# django core usage (mainly BinaryField). However, Jython doesn't support
54-
# buffer (see http://bugs.jython.org/issue1521), so we have to be careful.
55-
if JYTHON:
56-
memoryview = memoryview
57-
else:
58-
memoryview = buffer
5951
buffer_types = (bytearray, memoryview, buffer)
6052

6153
iterable_types = [

0 commit comments

Comments
 (0)