|
3 | 3 | # See the License at http://www.apache.org/licenses/LICENSE-2.0 |
4 | 4 | # Distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND. |
5 | 5 |
|
6 | | -import base64 |
7 | 6 | import decimal |
8 | | -from base64 import b85decode as _b85decode |
9 | 7 | from dataclasses import asdict |
10 | 8 | from dataclasses import dataclass |
11 | 9 | from dataclasses import field |
|
17 | 15 | from typing import Tuple |
18 | 16 |
|
19 | 17 | import orjson |
| 18 | +import pybase64 as base64 |
20 | 19 |
|
21 | 20 |
|
22 | 21 | def orjson_default(obj): |
23 | 22 | if type(obj) is decimal.Decimal: |
24 | 23 | return {"__decimal__": str(obj)} |
25 | 24 | if type(obj) is bytes: |
26 | | - return {"__bytes__": base64.b85encode(obj).decode("utf-8")} |
| 25 | + return {"__bytes__": base64.b64encode(obj).decode("utf-8")} |
27 | 26 | raise TypeError(f"Type not serializable: {type(obj)}") |
28 | 27 |
|
29 | 28 |
|
30 | 29 | def decode_object(obj): |
31 | | - """ |
32 | | - Decode an object that was encoded with orjson_default. |
33 | | -
|
34 | | - This is part of an optimization path to avoid loading files, so is written to be fast |
35 | | - over readability. It uses a stack to traverse the object structure and decode it in place. |
36 | | -
|
37 | | - Before this stack-based approach, the recursive version was the third slowest function |
38 | | - call in performance tests, so this is a significant improvement. |
39 | | - """ |
40 | | - stack = [(None, None, obj)] # (parent, key/index, child) |
41 | | - root = None |
42 | | - |
43 | | - while stack: |
44 | | - parent, key, item = stack.pop() |
45 | | - |
46 | | - t = type(item) |
47 | | - |
48 | | - if t is dict: |
49 | | - if "__decimal__" in item: |
50 | | - val = _Decimal(item["__decimal__"]) |
51 | | - elif "__bytes__" in item: |
52 | | - val = _b85decode(item["__bytes__"]) |
53 | | - else: |
54 | | - val = {} |
55 | | - if parent is not None: |
56 | | - parent[key] = val |
57 | | - else: |
58 | | - root = val |
59 | | - for k in reversed(list(item.keys())): |
60 | | - stack.append((val, k, item[k])) |
61 | | - continue |
62 | | - |
63 | | - elif t is list: |
64 | | - val = [None] * len(item) |
65 | | - if parent is not None: |
66 | | - parent[key] = val |
67 | | - else: |
68 | | - root = val |
69 | | - for i in reversed(range(len(item))): |
70 | | - stack.append((val, i, item[i])) |
71 | | - continue |
72 | | - |
73 | | - else: |
74 | | - val = item |
75 | | - |
76 | | - if parent is not None: |
77 | | - parent[key] = val |
78 | | - else: |
79 | | - root = val |
80 | | - |
81 | | - return root |
| 30 | + _decode = decode_object |
| 31 | + t = type(obj) |
| 32 | + |
| 33 | + if t is dict: |
| 34 | + if "__decimal__" in obj: |
| 35 | + return _Decimal(obj["__decimal__"]) |
| 36 | + if "__bytes__" in obj: |
| 37 | + return base64.b64decode(obj["__bytes__"]) |
| 38 | + return {k: _decode(v) for k, v in obj.items()} |
| 39 | + |
| 40 | + if t is list: |
| 41 | + for i, v in enumerate(obj): |
| 42 | + obj[i] = _decode(v) |
| 43 | + return obj |
| 44 | + |
| 45 | + return obj |
82 | 46 |
|
83 | 47 |
|
84 | 48 | @dataclass |
|
0 commit comments