Skip to content

Commit 6e1f18e

Browse files
committed
Prior to merge.
1 parent b35bc12 commit 6e1f18e

File tree

2 files changed

+50
-26
lines changed

2 files changed

+50
-26
lines changed

README.md

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ The following types are natively supported.
4848
* `bytes` Binary data.
4949
* `float` IEEE-754 single or double precision controlled by a dump option.
5050
* `tuple` By default tuples are de-serialised as lists, but this can be
51-
overridden by a load option. The extension module provides proper support
52-
wherby `list` and `tuple` objects unpack unchanged.
51+
overridden by a load option. An extension module provides proper support
52+
whereby `list` and `tuple` objects unpack unchanged.
5353
* `list` Termed "array" in MessagePack documentation.
5454
* `dict` Termed "map" in MessagePack docs.
5555

@@ -86,8 +86,8 @@ when packing extended built-ins.
8686

8787
### 1.2.2 Asynchronous unpacking
8888

89-
This now has an option for an asynchronous iterator. The `observer` object can
90-
now be any callable (a callback or a class).
89+
Unpacking is now via an asynchronous iterator. The `observer` object can now be
90+
any callable (a callback function/method or a class).
9191

9292
## 1.3 Compression performance
9393

@@ -116,14 +116,12 @@ This implementation is based on the following well proven MessagePack repo
116116
under Python 2 and Python 3. With trivial adaptations it will run under
117117
[MicroPython](https://micropython.org/) but at a high cost in RAM consumption.
118118
This version was adapted from that codebase and optimised to minimise RAM usage
119-
when run on microcontrollers. Consumption is about 12KiB measured on STM32.
120-
Using frozen bytecode this reduces to about 3.5KiB. This was tested with the
121-
`asyntest.py` demo, comparing the free RAM with that available running a
122-
similar script which exchanges uncompressed data. The difference was taken to
123-
be the library overhead of running compression and asynchronous decompression.
119+
when run on microcontrollers. When using frozen bytecode or romfs RAM use can be
120+
as low as 2192 bytes for `dumps` and 1008 bytes for `loads` - see
121+
[section 12](./README.md#12-measurement-of-ram-usage).
124122

125123
This version is a subset of the original. Support was removed for features
126-
thought unnecessary for microcontroller use. The principal example is that of
124+
thought problematic for microcontroller use. The principal example is that of
127125
timestamps. MicroPython does not support the `datetime` module. There are also
128126
issues with platforms differing in their time handling, notably the epoch. On a
129127
microcontroller it is simple to send the integer result from `time.time()` or
@@ -135,7 +133,6 @@ Supported types are fully compliant with a subset of the latest
135133
In particular, it supports the new binary, UTF-8 string and application-defined
136134
ext types. As stated above, timestamps are unsupported.
137135

138-
139136
This MicroPython version uses various techniques to minimise RAM use including
140137
"lazy imports": a module is only imported on first usage. For example an
141138
application that only de-serialises data using synchronous code will not import
@@ -152,7 +149,7 @@ foreign language party.
152149
The MessagePack specification does not distinguish between mutable and
153150
immutable types. Consequently the non-extended module will be unable to
154151
distinguish `tuple` and `list` items, also `bytes` and `bytearray`. The
155-
extension module addresses this.
152+
extension modules address this.
156153

157154
# 3. Installation
158155

@@ -175,7 +172,7 @@ The following files are installed by `mpremote`:
175172
7. `umsgpack/mpk_set.py` Extends support to `set`.
176173
8. `umsgpack/mpk_tuple.py` Extends support to `tuple`.
177174
9. `asyntest.py` Demo of asynchronous use of MessagePack.
178-
10. `user_class.py` Demo of a user defined class that is serialisable by messagePack.
175+
10. `user_class.py` Demo of a user defined class that is serialisable by MessagePack.
179176

180177
In a minimal installation only items 1-3 are required.
181178

@@ -350,21 +347,20 @@ class Complex:
350347
return complex(*struct.unpack(">ff", data))
351348
```
352349
The decorator takes two args, the extension type and the type to be handled.
353-
A class defined with the two-arg decorator must provide the following methods:
350+
A class defined with the two-arg decorator must provide the following static
351+
methods:
354352

355353
* `packb` Takes two args, an instance of the type to be serialised and a
356354
`dict` of pack options. Returns a `bytes` instance containing the serialised
357-
object. The method can optionally access `.options`.
358-
* `unpackb` Defined as a static method, this accepts a `bytes` instance of
359-
packed data and a `dict` of unpack options. Returns a new instance of the
360-
unpacked data type.
355+
object. The method can optionally access `.options`.
356+
* `unpackb` This accepts a `bytes` instance of packed data and a `dict` of
357+
unpack options. Returns a new instance of the unpacked data type.
361358

362-
The options comprise any keyword args supplied to `dump(s)` and `load(s)`
359+
The options comprise the keyword args supplied to `dump(s)` and `load(s)`
363360
respectively.
364361

365-
Typically this packing and unpacking is done using the `struct` module, but in
366-
some simple cases it may be done by `umsgpack` itself. For example
367-
`mpk_set.py`:
362+
Typically packing and unpacking is done using the `struct` module, but in some
363+
simple cases it may be done by `umsgpack` itself. For example `mpk_set.py`:
368364
```py
369365
@umsgpack.ext_serializable(0x51, set)
370366
class Set:
@@ -558,8 +554,9 @@ method.
558554
On packing the `builtins` and `custom` dictionaries are used to locate the
559555
appropriate packer with its `packb` method. Packers are never instantiated.
560556

561-
In both type of extensions the decorator populates a global `packers` dictionary: the key is the `ext_type` and the value is the packer class or user class. This
562-
class is used on unpacking to run the `unpackb` static method.
557+
In both type of extensions the decorator populates a global `packers`
558+
dictionary: the key is the `ext_type` and the value is the packer class or user
559+
class. This class is used on unpacking to run the `unpackb` static method.
563560

564561
This mechanism implies that the names of a packer class and the module
565562
containing it are arbitrary.
@@ -577,6 +574,33 @@ they can convert between the supported data type and one natively supported,
577574
and use `umsgpack` itself. See `mpk_set.py` which converts a `set` to a `list`
578575
and _vice versa_.
579576

577+
# 12. Measurement of RAM usage
578+
579+
This test used an RP2040 with precompiled code in romfs. In each case the code
580+
below was placed in `main.py`, the board re-booted, and the result observed.
581+
582+
Pack:
583+
```py
584+
import gc, umsgpack
585+
gc.collect()
586+
a = gc.mem_free()
587+
umsgpack.dumps(1)
588+
gc.collect()
589+
b = gc.mem_free()
590+
print(a-b)
591+
```
592+
Unpack:
593+
```py
594+
import gc, umsgpack
595+
gc.collect()
596+
a = gc.mem_free()
597+
umsgpack.loads(b'\x01')
598+
gc.collect()
599+
b = gc.mem_free()
600+
print(a-b)
601+
```
602+
Results were pack: 2192 bytes, unpack 1008 bytes.
603+
580604
## Acknowledgements
581605

582606
This project was inspired by

umsgpack/mp_dump.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def _pack_map(obj, fp, options):
171171

172172

173173
def _utype(obj):
174-
raise UnsupportedTypeException(f"{type(obj)}")
174+
raise UnsupportedTypeException("{}".format(str(type(obj))))
175175

176176

177177
# ***** Interface to __init__.py *****
@@ -192,7 +192,7 @@ def mpdump(obj, fp, options):
192192
# Run the Packer and prepend MessagePack header
193193
_pack_ext(v, pk.packb(obj, options), fp)
194194
except AttributeError:
195-
raise NotImplementedError(f"Class {repr(obj.__class__)} invalid packb()")
195+
raise NotImplementedError("Class {} invalid packb()".format(repr(obj.__class__)))
196196
return
197197
except StopIteration:
198198
pass

0 commit comments

Comments
 (0)