From 0911053a8a812289882acbe5fd36630a95b35322 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 Sep 2018 17:49:14 +0530 Subject: [PATCH] Make the serialize module cheap to import --- src/calibre/utils/serialize.py | 81 ++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/src/calibre/utils/serialize.py b/src/calibre/utils/serialize.py index 1819e08d85..8d7a3f57ac 100644 --- a/src/calibre/utils/serialize.py +++ b/src/calibre/utils/serialize.py @@ -4,50 +4,55 @@ from __future__ import absolute_import, division, print_function, unicode_literals -import json, base64 -from functools import partial -from datetime import datetime - -import msgpack - -from calibre.utils.iso8601 import parse_iso8601 MSGPACK_MIME = 'application/x-msgpack' CANARY = 'jPoAv3zOyHvQ5JFNYg4hJ9' -def encoded(typ, data, for_json): - if for_json: +def encoded(typ, data, ExtType): + if ExtType is None: return {CANARY: typ, 'v': data} - return msgpack.ExtType(typ, msgpack_dumps(data)) + return ExtType(typ, msgpack_dumps(data)) -def encoder(obj, for_json=False): - if isinstance(obj, datetime): - return encoded(0, unicode(obj.isoformat()), for_json) - if isinstance(obj, (set, frozenset)): - return encoded(1, tuple(obj), for_json) - if getattr(obj, '__calibre_serializable__', False): - from calibre.ebooks.metadata.book.base import Metadata - from calibre.library.field_metadata import FieldMetadata, fm_as_dict - from calibre.db.categories import Tag - if isinstance(obj, Metadata): - from calibre.ebooks.metadata.book.serialize import metadata_as_dict - return encoded( - 2, metadata_as_dict(obj, encode_cover_data=for_json), for_json - ) - elif isinstance(obj, FieldMetadata): - return encoded(3, fm_as_dict(obj), for_json) - elif isinstance(obj, Tag): - return encoded(4, obj.as_dict(), for_json) - raise TypeError('Cannot serialize objects of type {}'.format(type(obj))) +def create_encoder(for_json=False): + from datetime import datetime + ExtType = None + if not for_json: + import msgpack + ExtType = msgpack.ExtType + + def encoder(obj): + if isinstance(obj, datetime): + return encoded(0, unicode(obj.isoformat()), ExtType) + if isinstance(obj, (set, frozenset)): + return encoded(1, tuple(obj), ExtType) + if getattr(obj, '__calibre_serializable__', False): + from calibre.ebooks.metadata.book.base import Metadata + from calibre.library.field_metadata import FieldMetadata, fm_as_dict + from calibre.db.categories import Tag + if isinstance(obj, Metadata): + from calibre.ebooks.metadata.book.serialize import metadata_as_dict + return encoded( + 2, metadata_as_dict(obj, encode_cover_data=for_json), ExtType + ) + elif isinstance(obj, FieldMetadata): + return encoded(3, fm_as_dict(obj), ExtType) + elif isinstance(obj, Tag): + return encoded(4, obj.as_dict(), ExtType) + raise TypeError('Cannot serialize objects of type {}'.format(type(obj))) + + return encoder -msgpack_dumps = partial(msgpack.packb, default=encoder, use_bin_type=True) +def msgpack_dumps(obj): + import msgpack + return msgpack.packb(obj, default=create_encoder(), use_bin_type=True) def json_dumps(data, **kw): - kw['default'] = partial(encoder, for_json=True) + import json + kw['default'] = create_encoder(for_json=True) kw['ensure_ascii'] = False ans = json.dumps(data, **kw) if not isinstance(ans, bytes): @@ -56,6 +61,7 @@ def json_dumps(data, **kw): def decode_metadata(x, for_json): + import base64 from calibre.ebooks.metadata.book.serialize import metadata_from_dict obj = metadata_from_dict(x) if for_json and obj.cover_data and obj.cover_data[1]: @@ -75,8 +81,14 @@ def decode_category_tag(x, for_json): return Tag.from_dict(x) +def decode_datetime(x, fj): + from calibre.utils.iso8601 import parse_iso8601 + return parse_iso8601(x, assume_utc=True) + + decoders = ( - lambda x, fj: parse_iso8601(x, assume_utc=True), lambda x, fj: set(x), + decode_datetime, + lambda x, fj: set(x), decode_metadata, decode_field_metadata, decode_category_tag ) @@ -92,8 +104,11 @@ def msgpack_decoder(code, data): return decoders[code](msgpack_loads(data), False) -msgpack_loads = partial(msgpack.unpackb, encoding='utf-8', ext_hook=msgpack_decoder, raw=False) +def msgpack_loads(dump): + import msgpack + return msgpack.unpackb(dump, ext_hook=msgpack_decoder, raw=False) def json_loads(data): + import json return json.loads(data, object_hook=json_decoder)