transit-python 0.8.321__cp310-abi3-win_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- transit/__init__.py +16 -0
- transit/_accel.py +27 -0
- transit/_native.pyd +0 -0
- transit/class_hash.py +52 -0
- transit/constants.py +23 -0
- transit/decoder.py +185 -0
- transit/helpers.py +41 -0
- transit/read_handlers.py +183 -0
- transit/reader.py +137 -0
- transit/rolling_cache.py +104 -0
- transit/sosjson.py +129 -0
- transit/transit_types.py +267 -0
- transit/write_handlers.py +353 -0
- transit/writer.py +443 -0
- transit_python-0.8.321.dist-info/METADATA +302 -0
- transit_python-0.8.321.dist-info/RECORD +19 -0
- transit_python-0.8.321.dist-info/WHEEL +5 -0
- transit_python-0.8.321.dist-info/licenses/LICENSE +202 -0
- transit_python-0.8.321.dist-info/top_level.txt +1 -0
transit/writer.py
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
## Copyright 2014 Cognitect. All Rights Reserved.
|
|
2
|
+
## Copyright 2026 Vendekagon Labs LLC.
|
|
3
|
+
##
|
|
4
|
+
## Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
## you may not use this file except in compliance with the License.
|
|
6
|
+
## You may obtain a copy of the License at
|
|
7
|
+
##
|
|
8
|
+
## http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
##
|
|
10
|
+
## Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
## distributed under the License is distributed on an "AS-IS" BASIS,
|
|
12
|
+
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
## See the License for the specific language governing permissions and
|
|
14
|
+
## limitations under the License.
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import re
|
|
18
|
+
|
|
19
|
+
from transit.constants import SUB, ESC, RES, MAP_AS_ARR, QUOTE
|
|
20
|
+
from transit.rolling_cache import RollingCache
|
|
21
|
+
from transit.write_handlers import WriteHandler
|
|
22
|
+
from transit.transit_types import TaggedValue
|
|
23
|
+
from transit.helpers import require_msgpack
|
|
24
|
+
from transit._accel import FORMATS, native
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Writer:
|
|
28
|
+
"""The top-level object for writing out Python objects and converting them
|
|
29
|
+
to Transit data. During initialization, you must specify the protocol used
|
|
30
|
+
for marshalling the data- json or msgpack (which requires the msgpack
|
|
31
|
+
package: pip install 'transit-python[msgpack]'). You must also specify the io
|
|
32
|
+
source used for writing (a file descriptor). You may optionally pass in
|
|
33
|
+
an options dictionary that will be forwarded onto the Marshaler.
|
|
34
|
+
The cache is enabled by default.
|
|
35
|
+
"""
|
|
36
|
+
def __init__(self, io, protocol="json", opts=None):
|
|
37
|
+
opts = {"cache_enabled": True} if opts is None else opts
|
|
38
|
+
self.io = io
|
|
39
|
+
self.protocol = protocol
|
|
40
|
+
self.customized = False
|
|
41
|
+
self.marshaler = None
|
|
42
|
+
if protocol == "msgpack" and native is not None:
|
|
43
|
+
try:
|
|
44
|
+
self.marshaler = MsgPackMarshaler(io, opts=opts)
|
|
45
|
+
except ImportError:
|
|
46
|
+
pass # the native extension writes msgpack without the package
|
|
47
|
+
elif protocol == "json":
|
|
48
|
+
self.marshaler = JsonMarshaler(io, opts=opts)
|
|
49
|
+
elif protocol == "json_verbose":
|
|
50
|
+
self.marshaler = VerboseJsonMarshaler(io, opts=opts)
|
|
51
|
+
elif protocol == "msgpack":
|
|
52
|
+
self.marshaler = MsgPackMarshaler(io, opts=opts)
|
|
53
|
+
else:
|
|
54
|
+
raise ValueError("'" + protocol + "' is not a supported protocol. " +
|
|
55
|
+
"Protocol must be 'json', 'json_verbose', or 'msgpack'.")
|
|
56
|
+
|
|
57
|
+
def write(self, obj):
|
|
58
|
+
"""Given a Python object, marshal it into Transit data and write it to
|
|
59
|
+
the 'io' source.
|
|
60
|
+
"""
|
|
61
|
+
if native is not None and not self.customized:
|
|
62
|
+
try:
|
|
63
|
+
out = native.dumps(obj, FORMATS[self.protocol])
|
|
64
|
+
except native.Unsupported:
|
|
65
|
+
pass # a type the extension doesn't handle: pure Python
|
|
66
|
+
else:
|
|
67
|
+
self.io.write(out)
|
|
68
|
+
self.io.flush()
|
|
69
|
+
return
|
|
70
|
+
if self.marshaler is None:
|
|
71
|
+
require_msgpack()
|
|
72
|
+
self.marshaler.marshal_top(obj)
|
|
73
|
+
|
|
74
|
+
def register(self, obj_type, handler_class):
|
|
75
|
+
"""Register custom converters for object types present in your
|
|
76
|
+
application. This allows you to extend Transit to encode new types.
|
|
77
|
+
You must specify the obj type to be encoded, and the handler class
|
|
78
|
+
that should be used by the Marshaler during write-time.
|
|
79
|
+
"""
|
|
80
|
+
self.customized = True
|
|
81
|
+
if self.marshaler is None:
|
|
82
|
+
require_msgpack()
|
|
83
|
+
self.marshaler.register(obj_type, handler_class)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def flatten_map(m):
|
|
87
|
+
"""Expand a dictionary's items into a flat list
|
|
88
|
+
"""
|
|
89
|
+
# This is the fastest way to do this in Python
|
|
90
|
+
return [item for t in m.items() for item in t]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def re_fn(pat):
|
|
94
|
+
compiled = re.compile(pat)
|
|
95
|
+
def re_inner_fn(value):
|
|
96
|
+
return compiled.match(value)
|
|
97
|
+
|
|
98
|
+
return re_inner_fn
|
|
99
|
+
|
|
100
|
+
is_escapable = re_fn("^" + re.escape(SUB) + "|" + ESC + "|" + RES)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def escape(s):
|
|
104
|
+
if is_escapable(s):
|
|
105
|
+
return ESC+s
|
|
106
|
+
else:
|
|
107
|
+
return s
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class Marshaler:
|
|
111
|
+
"""The base Marshaler from which all Marshalers inherit.
|
|
112
|
+
|
|
113
|
+
The Marshaler specifies how to emit Transit data given encodeable Python
|
|
114
|
+
objects. The end of this process is specialized by other Marshalers to
|
|
115
|
+
covert the final result into an on-the-wire payload (JSON or MsgPack).
|
|
116
|
+
"""
|
|
117
|
+
native_map_keys = False
|
|
118
|
+
|
|
119
|
+
def __init__(self, opts=None):
|
|
120
|
+
self.opts = {} if opts is None else opts
|
|
121
|
+
self._init_handlers()
|
|
122
|
+
|
|
123
|
+
def _init_handlers(self):
|
|
124
|
+
self.handlers = WriteHandler()
|
|
125
|
+
|
|
126
|
+
def are_stringable_keys(self, m):
|
|
127
|
+
"""Test whether the keys within a map are stringable - a simple map,
|
|
128
|
+
that can be optimized and whose keys can be cached
|
|
129
|
+
"""
|
|
130
|
+
for x in m.keys():
|
|
131
|
+
if len(self.handlers[x].tag(x)) != 1:
|
|
132
|
+
return False
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
def emit_nil(self, _, as_map_key, cache):
|
|
136
|
+
return self.emit_string(ESC, "_", "", True, cache) if as_map_key else self.emit_object(None)
|
|
137
|
+
|
|
138
|
+
def emit_string(self, prefix, tag, string, as_map_key, cache):
|
|
139
|
+
encoded = cache.encode(prefix + tag + string, as_map_key)
|
|
140
|
+
# TODO: Remove this optimization for the time being - it breaks cache
|
|
141
|
+
#if "cache_enabled" in self.opts and is_cacheable(encoded, as_map_key):
|
|
142
|
+
# return self.emit_object(cache.value_to_key[encoded], as_map_key)
|
|
143
|
+
return self.emit_object(encoded, as_map_key)
|
|
144
|
+
|
|
145
|
+
def emit_boolean(self, b, as_map_key, cache):
|
|
146
|
+
return self.emit_string(ESC, "?", b, True, cache) if as_map_key else self.emit_object(b)
|
|
147
|
+
|
|
148
|
+
def emit_int(self, tag, i, rep, as_map_key, cache):
|
|
149
|
+
if isinstance(rep, int) and i <= self.opts["max_int"] and i >= self.opts["min_int"]:
|
|
150
|
+
return self.emit_object(i, as_map_key)
|
|
151
|
+
else:
|
|
152
|
+
return self.emit_string(ESC, tag, str(rep), as_map_key, cache)
|
|
153
|
+
|
|
154
|
+
def emit_double(self, d, as_map_key, cache):
|
|
155
|
+
return self.emit_string(ESC, "d", d, True, cache) if as_map_key else self.emit_object(d)
|
|
156
|
+
|
|
157
|
+
def emit_array(self, a, _, cache):
|
|
158
|
+
self.emit_array_start(len(a))
|
|
159
|
+
for x in a:
|
|
160
|
+
self.marshal(x, False, cache)
|
|
161
|
+
self.emit_array_end()
|
|
162
|
+
|
|
163
|
+
def emit_map(self, m, _, cache):# use map as object from above, have to overwrite default parser.
|
|
164
|
+
self.emit_map_start(len(m))
|
|
165
|
+
for k, v in m.items():
|
|
166
|
+
self.marshal(k, True, cache)
|
|
167
|
+
self.marshal(v, False, cache)
|
|
168
|
+
self.emit_map_end()
|
|
169
|
+
|
|
170
|
+
def emit_cmap(self, m, _, cache):
|
|
171
|
+
self.emit_map_start(1)
|
|
172
|
+
self.emit_string(ESC, "#", "cmap", True, cache)
|
|
173
|
+
self.marshal(flatten_map(m), False, cache)
|
|
174
|
+
self.emit_map_end()
|
|
175
|
+
|
|
176
|
+
def emit_tagged(self, tag, rep, cache):
|
|
177
|
+
self.emit_array_start(2)
|
|
178
|
+
self.emit_string(ESC, "#", tag, False, cache)
|
|
179
|
+
self.marshal(rep, False, cache)
|
|
180
|
+
self.emit_array_end()
|
|
181
|
+
|
|
182
|
+
def emit_encoded(self, tag, handler, obj, as_map_key, cache):
|
|
183
|
+
rep = handler.rep(obj)
|
|
184
|
+
if len(tag) == 1:
|
|
185
|
+
if isinstance(rep, str):
|
|
186
|
+
self.emit_string(ESC, tag, rep, as_map_key, cache)
|
|
187
|
+
elif as_map_key or self.opts["prefer_strings"]:
|
|
188
|
+
rep = handler.string_rep(obj)
|
|
189
|
+
if isinstance(rep, str):
|
|
190
|
+
self.emit_string(ESC, tag, rep, as_map_key, cache)
|
|
191
|
+
else:
|
|
192
|
+
raise AssertionError("Cannot be encoded as string: " + str({"tag": tag,
|
|
193
|
+
"rep": rep,
|
|
194
|
+
"obj": obj}))
|
|
195
|
+
else:
|
|
196
|
+
self.emit_tagged(tag, rep, cache)
|
|
197
|
+
elif as_map_key:
|
|
198
|
+
raise AssertionError("Cannot be used as a map key: " + str({"tag": tag,
|
|
199
|
+
"rep": rep,
|
|
200
|
+
"obj": obj}))
|
|
201
|
+
else:
|
|
202
|
+
self.emit_tagged(tag, rep, cache)
|
|
203
|
+
|
|
204
|
+
def marshal(self, obj, as_map_key, cache):
|
|
205
|
+
"""Marshal an individual obj, potentially as part of another container
|
|
206
|
+
object (like a list/dictionary/etc). Specify if this object is a key
|
|
207
|
+
to a map/dict, and pass in the current cache being used.
|
|
208
|
+
This method should only be called by a top-level marshalling call
|
|
209
|
+
and should not be considered an entry-point for integration.
|
|
210
|
+
"""
|
|
211
|
+
handler = self.handlers[obj]
|
|
212
|
+
tag = handler.tag(obj)
|
|
213
|
+
f = marshal_dispatch.get(tag)
|
|
214
|
+
if as_map_key and self.native_map_keys and tag in ("_", "?", "i", "d"):
|
|
215
|
+
# msgpack map keys can be any type: like transit-java, write
|
|
216
|
+
# nil, booleans, ints and floats as themselves
|
|
217
|
+
as_map_key = False
|
|
218
|
+
|
|
219
|
+
if f:
|
|
220
|
+
f(self, obj, handler.string_rep(obj) if as_map_key else handler.rep(obj), as_map_key, cache)
|
|
221
|
+
else:
|
|
222
|
+
self.emit_encoded(tag, handler, obj, as_map_key, cache)
|
|
223
|
+
|
|
224
|
+
def marshal_top(self, obj, cache=None):
|
|
225
|
+
"""Given a complete object that needs to be marshaled into Transit
|
|
226
|
+
data, and optionally a cache, dispatch accordingly, and flush the data
|
|
227
|
+
directly into the IO stream.
|
|
228
|
+
"""
|
|
229
|
+
if cache is None:
|
|
230
|
+
cache = RollingCache()
|
|
231
|
+
|
|
232
|
+
handler = self.handlers[obj]
|
|
233
|
+
|
|
234
|
+
tag = handler.tag(obj)
|
|
235
|
+
if tag:
|
|
236
|
+
if len(tag) == 1:
|
|
237
|
+
self.marshal(TaggedValue(QUOTE, obj), False, cache)
|
|
238
|
+
else:
|
|
239
|
+
self.marshal(obj, False, cache)
|
|
240
|
+
self.flush()
|
|
241
|
+
else:
|
|
242
|
+
raise AssertionError("Handler must provide a non-nil tag: " + str(handler))
|
|
243
|
+
|
|
244
|
+
def dispatch_map(self, rep, as_map_key, cache):
|
|
245
|
+
"""Used to determine and dipatch the writing of a map - a simple
|
|
246
|
+
map with strings as keys, or a complex map, whose keys are also
|
|
247
|
+
compound types.
|
|
248
|
+
"""
|
|
249
|
+
if self.are_stringable_keys(rep):
|
|
250
|
+
return self.emit_map(rep, as_map_key, cache)
|
|
251
|
+
return self.emit_cmap(rep, as_map_key, cache)
|
|
252
|
+
|
|
253
|
+
def register(self, obj_type, handler_class):
|
|
254
|
+
"""Register custom converters for object types present in your
|
|
255
|
+
application. This allows you to extend Transit to encode new types.
|
|
256
|
+
You must specify the obj type to be encoded, and the handler class
|
|
257
|
+
that should be used by this marshaller.
|
|
258
|
+
"""
|
|
259
|
+
self.handlers[obj_type] = handler_class
|
|
260
|
+
|
|
261
|
+
marshal_dispatch = {"_": lambda self, obj, rep, as_map_key, cache: self.emit_nil(rep, as_map_key, cache),
|
|
262
|
+
"?": lambda self, obj, rep, as_map_key, cache: self.emit_boolean(rep, as_map_key, cache),
|
|
263
|
+
"s": lambda self, obj, rep, as_map_key, cache: self.emit_string("", "", escape(rep), as_map_key, cache),
|
|
264
|
+
"i": lambda self, i, rep, as_map_key, cache: self.emit_int("i", i, rep, as_map_key, cache),
|
|
265
|
+
"n": lambda self, i, rep, as_map_key, cache: self.emit_int("n", i, rep, as_map_key, cache),
|
|
266
|
+
"d": lambda self, obj, rep, as_map_key, cache: self.emit_double(rep, as_map_key, cache),
|
|
267
|
+
"'": lambda self, obj, rep, _, cache: self.emit_tagged("'", rep, cache),
|
|
268
|
+
"array": lambda self, obj, rep, as_map_key, cache: self.emit_array(rep, as_map_key, cache),
|
|
269
|
+
"map": lambda self, obj, rep, as_map_key, cache: self.dispatch_map(rep, as_map_key, cache)}
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class MsgPackMarshaler(Marshaler):
|
|
273
|
+
"""The Marshaler tailor to MsgPack. To use this Marshaler, specify the
|
|
274
|
+
'msgpack' protocol when creating a Writer.
|
|
275
|
+
"""
|
|
276
|
+
native_map_keys = True
|
|
277
|
+
MSGPACK_MAX_INT = pow(2, 63) - 1
|
|
278
|
+
MSGPACK_MIN_INT = -pow(2, 63)
|
|
279
|
+
|
|
280
|
+
default_opts = {"prefer_strings": False,
|
|
281
|
+
"max_int": MSGPACK_MAX_INT,
|
|
282
|
+
"min_int": MSGPACK_MIN_INT}
|
|
283
|
+
|
|
284
|
+
def __init__(self, io, opts=None):
|
|
285
|
+
self.io = io
|
|
286
|
+
self.packer = require_msgpack().Packer(autoreset=False)
|
|
287
|
+
nopts = MsgPackMarshaler.default_opts.copy()
|
|
288
|
+
nopts.update(opts or {})
|
|
289
|
+
Marshaler.__init__(self, nopts)
|
|
290
|
+
|
|
291
|
+
def emit_cmap(self, m, _, cache):
|
|
292
|
+
# transit-java writes cmaps as ["~#cmap", [...]] in msgpack (and as
|
|
293
|
+
# {"~#cmap": [...]} in JSON)
|
|
294
|
+
self.emit_tagged("cmap", flatten_map(m), cache)
|
|
295
|
+
|
|
296
|
+
def emit_array_start(self, size):
|
|
297
|
+
self.packer.pack_array_header(size)
|
|
298
|
+
|
|
299
|
+
def emit_array_end(self):
|
|
300
|
+
pass
|
|
301
|
+
|
|
302
|
+
def emit_map_start(self, size):
|
|
303
|
+
self.packer.pack_map_header(size)
|
|
304
|
+
|
|
305
|
+
def emit_map_end(self):
|
|
306
|
+
pass
|
|
307
|
+
|
|
308
|
+
def emit_object(self, obj, as_map_key=False):
|
|
309
|
+
self.packer.pack(obj)
|
|
310
|
+
|
|
311
|
+
def flush(self):
|
|
312
|
+
self.io.write(self.packer.bytes())
|
|
313
|
+
self.io.flush()
|
|
314
|
+
self.packer.reset()
|
|
315
|
+
|
|
316
|
+
class JsonMarshaler(Marshaler):
|
|
317
|
+
"""The Marshaler tailor to JSON. To use this Marshaler, specify the
|
|
318
|
+
'json' protocol when creating a Writer.
|
|
319
|
+
"""
|
|
320
|
+
JSON_MAX_INT = pow(2, 53) - 1
|
|
321
|
+
JSON_MIN_INT = -pow(2, 53) + 1
|
|
322
|
+
|
|
323
|
+
default_opts = {"prefer_strings": True,
|
|
324
|
+
"max_int": JSON_MAX_INT,
|
|
325
|
+
"min_int": JSON_MIN_INT}
|
|
326
|
+
|
|
327
|
+
def __init__(self, io, opts=None):
|
|
328
|
+
self.io = io
|
|
329
|
+
nopts = JsonMarshaler.default_opts.copy()
|
|
330
|
+
nopts.update(opts or {})
|
|
331
|
+
self.started = [True]
|
|
332
|
+
self.is_key = [None]
|
|
333
|
+
Marshaler.__init__(self, nopts)
|
|
334
|
+
|
|
335
|
+
def flush(self):
|
|
336
|
+
# Each top level value is a separate JSON text; reset separator state
|
|
337
|
+
# so writing a stream of values doesn't emit a leading comma.
|
|
338
|
+
self.started = [True]
|
|
339
|
+
self.is_key = [None]
|
|
340
|
+
self.io.flush()
|
|
341
|
+
|
|
342
|
+
def push_level(self):
|
|
343
|
+
self.started.append(True)
|
|
344
|
+
self.is_key.append(None)
|
|
345
|
+
|
|
346
|
+
def pop_level(self):
|
|
347
|
+
self.started.pop()
|
|
348
|
+
self.is_key.pop()
|
|
349
|
+
|
|
350
|
+
def push_map(self):
|
|
351
|
+
self.started.append(True)
|
|
352
|
+
self.is_key.append(True)
|
|
353
|
+
|
|
354
|
+
def write_sep(self):
|
|
355
|
+
if self.started[-1]:
|
|
356
|
+
self.started[-1] = False
|
|
357
|
+
else:
|
|
358
|
+
last = self.is_key[-1]
|
|
359
|
+
if last:
|
|
360
|
+
self.io.write(":")
|
|
361
|
+
self.is_key[-1] = False
|
|
362
|
+
elif last is False:
|
|
363
|
+
self.io.write(",")
|
|
364
|
+
self.is_key[-1] = True
|
|
365
|
+
else:
|
|
366
|
+
self.io.write(",")
|
|
367
|
+
|
|
368
|
+
def emit_array_start(self, size):
|
|
369
|
+
self.write_sep()
|
|
370
|
+
self.io.write("[")
|
|
371
|
+
self.push_level()
|
|
372
|
+
|
|
373
|
+
def emit_array_end(self):
|
|
374
|
+
self.pop_level()
|
|
375
|
+
self.io.write("]")
|
|
376
|
+
|
|
377
|
+
def emit_map(self, m, _, cache):
|
|
378
|
+
"""Emits array as per default JSON spec."""
|
|
379
|
+
self.emit_array_start(None)
|
|
380
|
+
self.emit_object(MAP_AS_ARR)
|
|
381
|
+
for k, v in m.items():
|
|
382
|
+
self.marshal(k, True, cache)
|
|
383
|
+
self.marshal(v, False, cache)
|
|
384
|
+
self.emit_array_end()
|
|
385
|
+
|
|
386
|
+
def emit_map_start(self, size):
|
|
387
|
+
self.write_sep()
|
|
388
|
+
self.io.write("{")
|
|
389
|
+
self.push_map()
|
|
390
|
+
|
|
391
|
+
def emit_map_end(self):
|
|
392
|
+
self.pop_level()
|
|
393
|
+
self.io.write("}")
|
|
394
|
+
|
|
395
|
+
def emit_object(self, obj, as_map_key=False):
|
|
396
|
+
tp = type(obj)
|
|
397
|
+
self.write_sep()
|
|
398
|
+
if tp is str:
|
|
399
|
+
self.io.write(json.dumps(obj, ensure_ascii=False))
|
|
400
|
+
elif tp is int or tp is float:
|
|
401
|
+
self.io.write(str(obj))
|
|
402
|
+
elif tp is bool:
|
|
403
|
+
self.io.write("true" if obj else "false")
|
|
404
|
+
elif obj is None:
|
|
405
|
+
self.io.write("null")
|
|
406
|
+
else:
|
|
407
|
+
raise AssertionError("Don't know how to encode: " + str(obj) + " of type: " + str(type(obj)))
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class VerboseSettings:
|
|
411
|
+
"""Mixin for JsonMarshaler that adds support for Verbose output/input.
|
|
412
|
+
Verbosity is only suggest for debuging/inspecting purposes.
|
|
413
|
+
"""
|
|
414
|
+
@staticmethod
|
|
415
|
+
def _verbose_handlers(handlers):
|
|
416
|
+
for k, v in handlers.items():
|
|
417
|
+
if hasattr(v, "verbose_handler"):
|
|
418
|
+
handlers[k] = v.verbose_handler()
|
|
419
|
+
return handlers
|
|
420
|
+
|
|
421
|
+
def _init_handlers(self):
|
|
422
|
+
self.handlers = self._verbose_handlers(WriteHandler())
|
|
423
|
+
|
|
424
|
+
def emit_string(self, prefix, tag, string, as_map_key, cache):
|
|
425
|
+
return self.emit_object(prefix + tag + string, as_map_key)
|
|
426
|
+
|
|
427
|
+
def emit_map(self, m, _, cache):
|
|
428
|
+
self.emit_map_start(len(m))
|
|
429
|
+
for k, v in m.items():
|
|
430
|
+
self.marshal(k, True, cache)
|
|
431
|
+
self.marshal(v, False, cache)
|
|
432
|
+
self.emit_map_end()
|
|
433
|
+
|
|
434
|
+
def emit_tagged(self, tag, rep, cache):
|
|
435
|
+
self.emit_map_start(1)
|
|
436
|
+
self.emit_object(ESC + "#" + tag, True)
|
|
437
|
+
self.marshal(rep, False, cache)
|
|
438
|
+
self.emit_map_end()
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
class VerboseJsonMarshaler(VerboseSettings, JsonMarshaler):
|
|
442
|
+
"""JsonMarshaler class with VerboseSettings mixin."""
|
|
443
|
+
pass # all from inheritance and mixin
|