Skip to content

Commit 1054fa9

Browse files
committed
Complex honours float precision.
1 parent 6b40f03 commit 1054fa9

File tree

6 files changed

+61
-81
lines changed

6 files changed

+61
-81
lines changed

asyntest.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ def __call__(self, data: bytes) -> None:
5656
self.n = 0
5757

5858

59-
# def stream_observer(data: bytes):
60-
# print(f"{data}")
59+
# Alternative callable observer
60+
def stream_observer(data: bytes):
61+
print(f"{data}")
6162

6263

6364
async def receiver():

umsgpack/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626

2727
__version__ = (0, 2, 0)
2828

29+
# Auto-detect system float precision
30+
float_precision = "single" if len(str(1 / 3)) < 13 else "double"
31+
2932
# ABC for classes which handle extended Python built-in classes.
3033
class Packer:
3134
def __init__(self, s: object, options: dict):

umsgpack/as_loader.py

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(self, fp, **options):
2727
self.allow_invalid_utf8 = options.get("allow_invalid_utf8")
2828
self.use_ordered_dict = options.get("use_ordered_dict")
2929
self.use_tuple = options.get("use_tuple")
30-
self.observer = options.get("observer")
30+
self.observer = options.get("observer", lambda _: None)
3131
self.options = options # For ext types
3232

3333
def __aiter__(self): # async iterator interface
@@ -38,16 +38,14 @@ async def __anext__(self):
3838

3939
async def _re(self, n):
4040
d = await self.fp.readexactly(n)
41-
if self.observer:
42-
self.observer(d)
41+
self.observer(d)
4342
return d
4443

4544
async def _re0(self, s, n):
4645
d = await self._re(n)
4746
return struct.unpack(s, d)[0]
4847

49-
async def _unpack_integer(self, code):
50-
ic = ord(code)
48+
async def _unpack_integer(self, ic, code):
5149
if (ic & 0xE0) == 0xE0:
5250
return struct.unpack("b", code)[0]
5351
if (ic & 0x80) == 0x00:
@@ -60,16 +58,14 @@ async def _unpack_integer(self, code):
6058
ALoader._fail()
6159
return await self._re0(s.strip(), 1 << (ic & 3))
6260

63-
async def _unpack_float(self, code):
64-
ic = ord(code)
61+
async def _unpack_float(self, ic, code):
6562
if ic == 0xCA:
6663
return await self._re0(">f", 4)
6764
if ic == 0xCB:
6865
return await self._re0(">d", 8)
6966
ALoader._fail()
7067

71-
async def _unpack_string(self, code):
72-
ic = ord(code)
68+
async def _unpack_string(self, ic, code):
7369
if (ic & 0xE0) == 0xA0:
7470
length = ic & ~0xE0
7571
elif ic == 0xD9:
@@ -89,8 +85,7 @@ async def _unpack_string(self, code):
8985
return data # MP Remove InvalidString class: subclass of built-in class
9086
raise InvalidStringException("unpacked string is invalid utf-8")
9187

92-
async def _unpack_binary(self, code):
93-
ic = ord(code)
88+
async def _unpack_binary(self, ic, code):
9489
if ic == 0xC4:
9590
length = await self._re0("B", 1)
9691
elif ic == 0xC5:
@@ -102,8 +97,7 @@ async def _unpack_binary(self, code):
10297

10398
return await self._re(length)
10499

105-
async def _unpack_ext(self, code):
106-
ic = ord(code)
100+
async def _unpack_ext(self, ic, code):
107101
n = b"\xd4\xd5\xd6\xd7\xd8".find(code)
108102
length = 0 if n < 0 else 1 << n
109103
if not length:
@@ -129,8 +123,7 @@ async def _unpack_ext(self, code):
129123

130124
raise UnsupportedTypeException(f"ext_type: 0x{ext_type:0X}")
131125

132-
async def _unpack_array(self, code):
133-
ic = ord(code)
126+
async def _unpack_array(self, ic, code):
134127
if (ic & 0xF0) == 0x90:
135128
length = ic & ~0xF0
136129
elif ic == 0xDC:
@@ -144,8 +137,7 @@ async def _unpack_array(self, code):
144137
l.append(await self.aload())
145138
return tuple(l) if self.use_tuple else l
146139

147-
async def _unpack_map(self, code):
148-
ic = ord(code)
140+
async def _unpack_map(self, ic, code):
149141
if (ic & 0xF0) == 0x80:
150142
length = ic & ~0xF0
151143
elif ic == 0xDE:
@@ -171,48 +163,42 @@ async def _unpack_map(self, code):
171163
raise DuplicateKeyException(f'"{str(k)}" ({type(k)})')
172164

173165
# Unpack value
174-
v = await self.aload()
175-
176-
try:
177-
d[k] = v
178-
except TypeError:
179-
raise UnhashableKeyException(f'"{str(k)}"')
166+
d[k] = await self.aload()
180167
return d
181168

182-
# API
183-
# await aloader_instance.aload()
184-
# or async for obj in aloader_instance:
185169
async def aload(self):
186170
code = await self._re(1)
187171
ic = ord(code)
188172
if (ic <= 0x7F) or (0xCC <= ic <= 0xD3) or (0xE0 <= ic <= 0xFF):
189-
return await self._unpack_integer(code)
173+
return await self._unpack_integer(ic, code)
190174
if ic <= 0xC9:
191175
if ic <= 0xC3:
192176
if ic <= 0x8F:
193-
return await self._unpack_map(code)
177+
return await self._unpack_map(ic, code)
194178
if ic <= 0x9F:
195-
return await self._unpack_array(code)
179+
return await self._unpack_array(ic, code)
196180
if ic <= 0xBF:
197-
return await self._unpack_string(code)
181+
return await self._unpack_string(ic, code)
198182
if ic == 0xC1:
199183
raise ReservedCodeException("got reserved code: 0xc1")
200184
return (None, 0, False, True)[ic - 0xC0]
201185
if ic <= 0xC6:
202-
return await self._unpack_binary(code)
203-
return self._unpack_ext(code)
186+
return await self._unpack_binary(ic, code)
187+
return self._unpack_ext(ic, code)
204188
if ic <= 0xCB:
205-
return await self._unpack_float(code)
189+
return await self._unpack_float(ic, code)
206190
if ic <= 0xD8:
207-
return await self._unpack_ext(code)
191+
return await self._unpack_ext(ic, code)
208192
if ic <= 0xDB:
209-
return await self._unpack_string(code)
193+
return await self._unpack_string(ic, code)
210194
if ic <= 0xDD:
211-
return await self._unpack_array(code)
212-
return await self._unpack_map(code)
195+
return await self._unpack_array(ic, code)
196+
return await self._unpack_map(ic, code)
213197

198+
# API
199+
# await aloader_instance.load()
200+
# or async for obj in aloader_instance:
214201
async def load(self):
215202
rv = await self.aload()
216-
if self.observer:
217-
self.observer(b"") # Mark end of data
203+
self.observer(b"") # Mark end of data
218204
return rv

umsgpack/mp_dump.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@
1111
import io
1212
from . import *
1313

14-
builtins
15-
# Auto-detect system float precision
16-
_float_precision = "single" if len(str(1 / 3)) < 13 else "double"
17-
1814

1915
def _fail(): # Debug code should never be called.
2016
raise Exception("Logic error")
@@ -65,7 +61,7 @@ def _pack_boolean(obj, fp, _):
6561

6662

6763
def _pack_float(obj, fp, options):
68-
fpr = options.get("force_float_precision", _float_precision)
64+
fpr = options.get("force_float_precision", float_precision)
6965
if fpr == "double":
7066
fp.write(b"\xcb")
7167
fp.write(struct.pack(">d", obj))

umsgpack/mp_load.py

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ def _re0(s, fp, n):
3939
return struct.unpack(s, _read_except(fp, n))[0]
4040

4141

42-
def _unpack_integer(code, fp):
43-
ic = ord(code)
42+
def _unpack_integer(ic, code, fp):
4443
if (ic & 0xE0) == 0xE0:
4544
return struct.unpack("b", code)[0]
4645
if (ic & 0x80) == 0x00:
@@ -54,17 +53,15 @@ def _unpack_integer(code, fp):
5453
return _re0(s.strip(), fp, 1 << (ic & 3))
5554

5655

57-
def _unpack_float(code, fp):
58-
ic = ord(code)
56+
def _unpack_float(ic, code, fp):
5957
if ic == 0xCA:
6058
return _re0(">f", fp, 4)
6159
if ic == 0xCB:
6260
return _re0(">d", fp, 8)
6361
_fail()
6462

6563

66-
def _unpack_string(code, fp, options):
67-
ic = ord(code)
64+
def _unpack_string(ic, code, fp, options):
6865
if (ic & 0xE0) == 0xA0:
6966
length = ic & ~0xE0
7067
elif ic == 0xD9:
@@ -85,8 +82,7 @@ def _unpack_string(code, fp, options):
8582
raise InvalidStringException("unpacked string is invalid utf-8")
8683

8784

88-
def _unpack_binary(code, fp):
89-
ic = ord(code)
85+
def _unpack_binary(ic, code, fp):
9086
if ic == 0xC4:
9187
length = _re0("B", fp, 1)
9288
elif ic == 0xC5:
@@ -99,8 +95,7 @@ def _unpack_binary(code, fp):
9995
return _read_except(fp, length)
10096

10197

102-
def _unpack_ext(code, fp, options):
103-
ic = ord(code)
98+
def _unpack_ext(ic, code, fp, options):
10499
n = b"\xd4\xd5\xd6\xd7\xd8".find(code)
105100
length = 0 if n < 0 else 1 << n
106101
if not length:
@@ -126,8 +121,7 @@ def _unpack_ext(code, fp, options):
126121
raise UnsupportedTypeException(f"ext_type: 0x{ext_type:0X}")
127122

128123

129-
def _unpack_array(code, fp, options):
130-
ic = ord(code)
124+
def _unpack_array(ic, code, fp, options):
131125
if (ic & 0xF0) == 0x90:
132126
length = ic & ~0xF0
133127
elif ic == 0xDC:
@@ -146,8 +140,7 @@ def _deep_list_to_tuple(obj):
146140
return obj
147141

148142

149-
def _unpack_map(code, fp, options):
150-
ic = ord(code)
143+
def _unpack_map(ic, code, fp, options):
151144
if (ic & 0xF0) == 0x80:
152145
length = ic & ~0xF0
153146
elif ic == 0xDE:
@@ -171,45 +164,39 @@ def _unpack_map(code, fp, options):
171164
raise UnhashableKeyException(f'"{str(k)}"')
172165
if k in d:
173166
raise DuplicateKeyException(f'"{str(k)}" ({type(k)})')
174-
175167
# Unpack value
176-
v = mpload(fp, options)
177-
178-
try:
179-
d[k] = v
180-
except TypeError:
181-
raise UnhashableKeyException(f'"{str(k)}"')
168+
d[k] = mpload(fp, options)
182169
return d
183170

184171

185172
def mpload(fp, options):
186173
code = _read_except(fp, 1)
187174
ic = ord(code)
188175
if (ic <= 0x7F) or (0xCC <= ic <= 0xD3) or (0xE0 <= ic <= 0xFF):
189-
return _unpack_integer(code, fp)
176+
return _unpack_integer(ic, code, fp)
190177
if ic <= 0xC9:
191178
if ic <= 0xC3:
192179
if ic <= 0x8F:
193-
return _unpack_map(code, fp, options)
180+
return _unpack_map(ic, code, fp, options)
194181
if ic <= 0x9F:
195-
return _unpack_array(code, fp, options)
182+
return _unpack_array(ic, code, fp, options)
196183
if ic <= 0xBF:
197-
return _unpack_string(code, fp, options)
184+
return _unpack_string(ic, code, fp, options)
198185
if ic == 0xC1:
199186
raise ReservedCodeException("got 0xc1")
200187
return (None, 0, False, True)[ic - 0xC0]
201188
if ic <= 0xC6:
202-
return _unpack_binary(code, fp)
203-
return _unpack_ext(code, fp, options)
189+
return _unpack_binary(ic, code, fp)
190+
return _unpack_ext(ic, code, fp, options)
204191
if ic <= 0xCB:
205-
return _unpack_float(code, fp)
192+
return _unpack_float(ic, code, fp)
206193
if ic <= 0xD8:
207-
return _unpack_ext(code, fp, options)
194+
return _unpack_ext(ic, code, fp, options)
208195
if ic <= 0xDB:
209-
return _unpack_string(code, fp, options)
196+
return _unpack_string(ic, code, fp, options)
210197
if ic <= 0xDD:
211-
return _unpack_array(code, fp, options)
212-
return _unpack_map(code, fp, options)
198+
return _unpack_array(ic, code, fp, options)
199+
return _unpack_map(ic, code, fp, options)
213200

214201

215202
# API

umsgpack/mpk_complex.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
# mpk_complex.py Extend MessagePack to support complex numbers.
22

33
# Copyright (c) 2021-2025 Peter Hinch Released under the MIT License see LICENSE.
4+
# force_float_precision: on 32-bit platforms forcing double precision produces
5+
# the correct output on dump. Load does not, because the complex object has native
6+
# precision.
47

58
import umsgpack
69
import struct
7-
from . import Packer
10+
from . import Packer, float_precision
811

912

1013
@umsgpack.ext_serializable(0x50, complex)
1114
class Complex(Packer):
1215
def __init__(self, s, options):
1316
super().__init__(s, options)
17+
self.dp = options.get("force_float_precision", float_precision) == "double"
18+
self.fs = ">bdd" if self.dp else ">bff"
1419

1520
def packb(self):
16-
return struct.pack(">ff", self.s.real, self.s.imag)
21+
return struct.pack(self.fs, self.dp, self.s.real, self.s.imag)
1722

1823
@staticmethod
1924
def unpackb(data, options):
20-
return complex(*struct.unpack(">ff", data))
25+
dp = struct.unpack("b", data)[0]
26+
fs = ">dd" if dp else ">ff"
27+
return complex(*struct.unpack_from(fs, data, 1))

0 commit comments

Comments
 (0)