#!/usr/bin/python -OOOO # vim: set fileencoding=utf8 shiftwidth=4 tabstop=4 textwidth=80 foldmethod=marker : # Copyright (c) 2010, Kou Man Tong. All rights reserved. # Copyright (c) 2015, Ayun Park. All rights reserved. # For licensing, see LICENSE file included in the package. """ Base codec functions for bson. """ import struct import warnings from datetime import datetime from abc import ABCMeta, abstractmethod from uuid import UUID from decimal import Decimal try: from io import BytesIO as StringIO except ImportError: from cStringIO import StringIO import calendar from dateutil.tz import tzutc from binascii import b2a_hex from six import integer_types, iterkeys, text_type, PY3 from six.moves import xrange utc = tzutc() class MissingClassDefinition(ValueError): def __init__(self, class_name): super(MissingClassDefinition, self).__init__("No class definition for class %s" % (class_name,)) class UnknownSerializerError(ValueError): def __init__(self, key, value): super(UnknownSerializerError, self).__init__("Unable to serialize: key '%s' value: %s type: %s" % (key,value, type(value))) class MissingTimezoneWarning(RuntimeWarning): def __init__(self, *args): args = list(args) if len(args) < 1: args.append("Input datetime object has no tzinfo, assuming UTC.") super(MissingTimezoneWarning, self).__init__(*args) class TraversalStep(object): def __init__(self, parent, key): self.parent = parent self.key = key class BSONCoding(object): __metaclass__ = ABCMeta @abstractmethod def bson_encode(self): pass @abstractmethod def bson_init(self, raw_values): pass classes = {} def import_class(cls): if not issubclass(cls, BSONCoding): return global classes classes[cls.__name__] = cls def import_classes(*args): for cls in args: import_class(cls) def import_classes_from_modules(*args): for module in args: for item in module.__dict__: if hasattr(item, "__new__") and hasattr(item, "__name__"): import_class(item) def encode_object(obj, traversal_stack, generator_func, on_unknown=None): values = obj.bson_encode() class_name = obj.__class__.__name__ values["$$__CLASS_NAME__$$"] = class_name return encode_document(values, traversal_stack, obj, generator_func, on_unknown) def encode_object_element(name, value, traversal_stack, generator_func, on_unknown): return b"\x03" + encode_cstring(name) + \ encode_object(value, traversal_stack, generator_func=generator_func, on_unknown=on_unknown) class _EmptyClass(object): pass def decode_object(raw_values): global classes class_name = raw_values["$$__CLASS_NAME__$$"] try: cls = classes[class_name] except KeyError: raise MissingClassDefinition(class_name) retval = _EmptyClass() retval.__class__ = cls alt_retval = retval.bson_init(raw_values) return alt_retval or retval def encode_string(value): value = value.encode("utf-8") length = len(value) return struct.pack("= value > 0x7fffffff: buf.write(encode_int64_element(name, value)) elif value > 0x7FFFFFFFFFFFFFFF: if value > 0xFFFFFFFFFFFFFFFF: raise Exception("BSON format supports only int value < %s" % 0xFFFFFFFFFFFFFFFF) buf.write(encode_uint64_element(name, value)) else: buf.write(encode_int32_element(name, value)) elif isinstance(value, float): buf.write(encode_double_element(name, value)) elif _is_string(value): buf.write(encode_string_element(name, value)) elif isinstance(value, str) or isinstance(value, bytes): buf.write(encode_binary_element(name, value)) elif isinstance(value, UUID): buf.write(encode_binary_element(name, value.bytes, binary_subtype=4)) elif isinstance(value, datetime): buf.write(encode_utc_datetime_element(name, value)) elif value is None: buf.write(encode_none_element(name, value)) elif isinstance(value, dict): buf.write(encode_document_element(name, value, traversal_stack, generator_func, on_unknown)) elif isinstance(value, list) or isinstance(value, tuple): buf.write(encode_array_element(name, value, traversal_stack, generator_func, on_unknown)) elif isinstance(value, BSONCoding): buf.write(encode_object_element(name, value, traversal_stack, generator_func, on_unknown)) elif isinstance(value, Decimal): buf.write(encode_double_element(name, float(value))) else: if on_unknown is not None: encode_value(name, on_unknown(value), buf, traversal_stack, generator_func, on_unknown) else: raise UnknownSerializerError(name, value) def encode_document(obj, traversal_stack, traversal_parent=None, generator_func=None, on_unknown=None): buf = StringIO() key_iter = iterkeys(obj) if generator_func is not None: key_iter = generator_func(obj, traversal_stack) for name in key_iter: value = obj[name] traversal_stack.append(TraversalStep(traversal_parent or obj, name)) encode_value(name, value, buf, traversal_stack, generator_func, on_unknown) traversal_stack.pop() e_list = buf.getvalue() e_list_length = len(e_list) return struct.pack("