xtquant.xtbson.bson36.codec_options

Tools for specifying BSON codec options.

  1# Copyright 2014-present MongoDB, Inc.
  2#
  3# Licensed under the Apache License, Version 2.0 (the "License");
  4# you may not use this file except in compliance with the License.
  5# You may obtain a copy of the License at
  6#
  7# http://www.apache.org/licenses/LICENSE-2.0
  8#
  9# Unless required by applicable law or agreed to in writing, software
 10# distributed under the License is distributed on an "AS IS" BASIS,
 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12# See the License for the specific language governing permissions and
 13# limitations under the License.
 14
 15"""Tools for specifying BSON codec options."""
 16
 17import abc
 18import datetime
 19import warnings
 20from collections import namedtuple
 21from collections.abc import MutableMapping as _MutableMapping
 22
 23from .binary import (
 24    ALL_UUID_REPRESENTATIONS,
 25    UUID_REPRESENTATION_NAMES,
 26    UuidRepresentation,
 27)
 28
 29
 30def _abstractproperty(func):
 31    return property(abc.abstractmethod(func))
 32
 33
 34_RAW_BSON_DOCUMENT_MARKER = 101
 35
 36
 37def _raw_document_class(document_class):
 38    """Determine if a document_class is a RawBSONDocument class."""
 39    marker = getattr(document_class, "_type_marker", None)
 40    return marker == _RAW_BSON_DOCUMENT_MARKER
 41
 42
 43class TypeEncoder(abc.ABC):
 44    """Base class for defining type codec classes which describe how a
 45    custom type can be transformed to one of the types BSON understands.
 46
 47    Codec classes must implement the ``python_type`` attribute, and the
 48    ``transform_python`` method to support encoding.
 49
 50    See :ref:`custom-type-type-codec` documentation for an example.
 51    """
 52
 53    @_abstractproperty
 54    def python_type(self):
 55        """The Python type to be converted into something serializable."""
 56        pass
 57
 58    @abc.abstractmethod
 59    def transform_python(self, value):
 60        """Convert the given Python object into something serializable."""
 61        pass
 62
 63
 64class TypeDecoder(abc.ABC):
 65    """Base class for defining type codec classes which describe how a
 66    BSON type can be transformed to a custom type.
 67
 68    Codec classes must implement the ``bson_type`` attribute, and the
 69    ``transform_bson`` method to support decoding.
 70
 71    See :ref:`custom-type-type-codec` documentation for an example.
 72    """
 73
 74    @_abstractproperty
 75    def bson_type(self):
 76        """The BSON type to be converted into our own type."""
 77        pass
 78
 79    @abc.abstractmethod
 80    def transform_bson(self, value):
 81        """Convert the given BSON value into our own type."""
 82        pass
 83
 84
 85class TypeCodec(TypeEncoder, TypeDecoder):
 86    """Base class for defining type codec classes which describe how a
 87    custom type can be transformed to/from one of the types :mod:`bson`
 88    can already encode/decode.
 89
 90    Codec classes must implement the ``python_type`` attribute, and the
 91    ``transform_python`` method to support encoding, as well as the
 92    ``bson_type`` attribute, and the ``transform_bson`` method to support
 93    decoding.
 94
 95    See :ref:`custom-type-type-codec` documentation for an example.
 96    """
 97
 98    pass
 99
100
101class TypeRegistry(object):
102    """Encapsulates type codecs used in encoding and / or decoding BSON, as
103    well as the fallback encoder. Type registries cannot be modified after
104    instantiation.
105
106    ``TypeRegistry`` can be initialized with an iterable of type codecs, and
107    a callable for the fallback encoder::
108
109      >>> from .codec_options import TypeRegistry
110      >>> type_registry = TypeRegistry([Codec1, Codec2, Codec3, ...],
111      ...                              fallback_encoder)
112
113    See :ref:`custom-type-type-registry` documentation for an example.
114
115    :Parameters:
116      - `type_codecs` (optional): iterable of type codec instances. If
117        ``type_codecs`` contains multiple codecs that transform a single
118        python or BSON type, the transformation specified by the type codec
119        occurring last prevails. A TypeError will be raised if one or more
120        type codecs modify the encoding behavior of a built-in :mod:`bson`
121        type.
122      - `fallback_encoder` (optional): callable that accepts a single,
123        unencodable python value and transforms it into a type that
124        :mod:`bson` can encode. See :ref:`fallback-encoder-callable`
125        documentation for an example.
126    """
127
128    def __init__(self, type_codecs=None, fallback_encoder=None):
129        self.__type_codecs = list(type_codecs or [])
130        self._fallback_encoder = fallback_encoder
131        self._encoder_map = {}
132        self._decoder_map = {}
133
134        if self._fallback_encoder is not None:
135            if not callable(fallback_encoder):
136                raise TypeError("fallback_encoder %r is not a callable" % (fallback_encoder))
137
138        for codec in self.__type_codecs:
139            is_valid_codec = False
140            if isinstance(codec, TypeEncoder):
141                self._validate_type_encoder(codec)
142                is_valid_codec = True
143                self._encoder_map[codec.python_type] = codec.transform_python
144            if isinstance(codec, TypeDecoder):
145                is_valid_codec = True
146                self._decoder_map[codec.bson_type] = codec.transform_bson
147            if not is_valid_codec:
148                raise TypeError(
149                    "Expected an instance of %s, %s, or %s, got %r instead"
150                    % (TypeEncoder.__name__, TypeDecoder.__name__, TypeCodec.__name__, codec)
151                )
152
153    def _validate_type_encoder(self, codec):
154        from . import _BUILT_IN_TYPES
155
156        for pytype in _BUILT_IN_TYPES:
157            if issubclass(codec.python_type, pytype):
158                err_msg = (
159                    "TypeEncoders cannot change how built-in types are "
160                    "encoded (encoder %s transforms type %s)" % (codec, pytype)
161                )
162                raise TypeError(err_msg)
163
164    def __repr__(self):
165        return "%s(type_codecs=%r, fallback_encoder=%r)" % (
166            self.__class__.__name__,
167            self.__type_codecs,
168            self._fallback_encoder,
169        )
170
171    def __eq__(self, other):
172        if not isinstance(other, type(self)):
173            return NotImplemented
174        return (
175            (self._decoder_map == other._decoder_map)
176            and (self._encoder_map == other._encoder_map)
177            and (self._fallback_encoder == other._fallback_encoder)
178        )
179
180
181_options_base = namedtuple(
182    "CodecOptions",
183    (
184        "document_class",
185        "tz_aware",
186        "uuid_representation",
187        "unicode_decode_error_handler",
188        "tzinfo",
189        "type_registry",
190    ),
191)
192
193
194class CodecOptions(_options_base):
195    """Encapsulates options used encoding and / or decoding BSON.
196
197    The `document_class` option is used to define a custom type for use
198    decoding BSON documents. Access to the underlying raw BSON bytes for
199    a document is available using the :class:`~bson.raw_bson.RawBSONDocument`
200    type::
201
202      >>> from .raw_bson import RawBSONDocument
203      >>> from .codec_options import CodecOptions
204      >>> codec_options = CodecOptions(document_class=RawBSONDocument)
205      >>> coll = db.get_collection('test', codec_options=codec_options)
206      >>> doc = coll.find_one()
207      >>> doc.raw
208      '\\x16\\x00\\x00\\x00\\x07_id\\x00[0\\x165\\x91\\x10\\xea\\x14\\xe8\\xc5\\x8b\\x93\\x00'
209
210    The document class can be any type that inherits from
211    :class:`~collections.abc.MutableMapping`::
212
213      >>> class AttributeDict(dict):
214      ...     # A dict that supports attribute access.
215      ...     def __getattr__(self, key):
216      ...         return self[key]
217      ...     def __setattr__(self, key, value):
218      ...         self[key] = value
219      ...
220      >>> codec_options = CodecOptions(document_class=AttributeDict)
221      >>> coll = db.get_collection('test', codec_options=codec_options)
222      >>> doc = coll.find_one()
223      >>> doc._id
224      ObjectId('5b3016359110ea14e8c58b93')
225
226    See :doc:`/examples/datetimes` for examples using the `tz_aware` and
227    `tzinfo` options.
228
229    See :doc:`examples/uuid` for examples using the `uuid_representation`
230    option.
231
232    :Parameters:
233      - `document_class`: BSON documents returned in queries will be decoded
234        to an instance of this class. Must be a subclass of
235        :class:`~collections.abc.MutableMapping`. Defaults to :class:`dict`.
236      - `tz_aware`: If ``True``, BSON datetimes will be decoded to timezone
237        aware instances of :class:`~datetime.datetime`. Otherwise they will be
238        naive. Defaults to ``False``.
239      - `uuid_representation`: The BSON representation to use when encoding
240        and decoding instances of :class:`~uuid.UUID`. Defaults to
241        :data:`~bson.binary.UuidRepresentation.UNSPECIFIED`. New
242        applications should consider setting this to
243        :data:`~bson.binary.UuidRepresentation.STANDARD` for cross language
244        compatibility. See :ref:`handling-uuid-data-example` for details.
245      - `unicode_decode_error_handler`: The error handler to apply when
246        a Unicode-related error occurs during BSON decoding that would
247        otherwise raise :exc:`UnicodeDecodeError`. Valid options include
248        'strict', 'replace', 'backslashreplace', 'surrogateescape', and
249        'ignore'. Defaults to 'strict'.
250      - `tzinfo`: A :class:`~datetime.tzinfo` subclass that specifies the
251        timezone to/from which :class:`~datetime.datetime` objects should be
252        encoded/decoded.
253      - `type_registry`: Instance of :class:`TypeRegistry` used to customize
254        encoding and decoding behavior.
255
256    .. versionchanged:: 4.0
257       The default for `uuid_representation` was changed from
258       :const:`~bson.binary.UuidRepresentation.PYTHON_LEGACY` to
259       :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`.
260
261    .. versionadded:: 3.8
262       `type_registry` attribute.
263
264    .. warning:: Care must be taken when changing
265       `unicode_decode_error_handler` from its default value ('strict').
266       The 'replace' and 'ignore' modes should not be used when documents
267       retrieved from the server will be modified in the client application
268       and stored back to the server.
269    """
270
271    def __new__(
272        cls,
273        document_class=dict,
274        tz_aware=False,
275        uuid_representation=UuidRepresentation.UNSPECIFIED,
276        unicode_decode_error_handler="strict",
277        tzinfo=None,
278        type_registry=None,
279    ):
280        if not (issubclass(document_class, _MutableMapping) or _raw_document_class(document_class)):
281            raise TypeError(
282                "document_class must be dict, bson.son.SON, "
283                "bson.raw_bson.RawBSONDocument, or a "
284                "sublass of collections.abc.MutableMapping"
285            )
286        if not isinstance(tz_aware, bool):
287            raise TypeError("tz_aware must be True or False")
288        if uuid_representation not in ALL_UUID_REPRESENTATIONS:
289            raise ValueError(
290                "uuid_representation must be a value " "from .binary.UuidRepresentation"
291            )
292        if not isinstance(unicode_decode_error_handler, (str, None)):
293            raise ValueError("unicode_decode_error_handler must be a string " "or None")
294        if tzinfo is not None:
295            if not isinstance(tzinfo, datetime.tzinfo):
296                raise TypeError("tzinfo must be an instance of datetime.tzinfo")
297            if not tz_aware:
298                raise ValueError("cannot specify tzinfo without also setting tz_aware=True")
299
300        type_registry = type_registry or TypeRegistry()
301
302        if not isinstance(type_registry, TypeRegistry):
303            raise TypeError("type_registry must be an instance of TypeRegistry")
304
305        return tuple.__new__(
306            cls,
307            (
308                document_class,
309                tz_aware,
310                uuid_representation,
311                unicode_decode_error_handler,
312                tzinfo,
313                type_registry,
314            ),
315        )
316
317    def _arguments_repr(self):
318        """Representation of the arguments used to create this object."""
319        document_class_repr = "dict" if self.document_class is dict else repr(self.document_class)
320
321        uuid_rep_repr = UUID_REPRESENTATION_NAMES.get(
322            self.uuid_representation, self.uuid_representation
323        )
324
325        return (
326            "document_class=%s, tz_aware=%r, uuid_representation=%s, "
327            "unicode_decode_error_handler=%r, tzinfo=%r, "
328            "type_registry=%r"
329            % (
330                document_class_repr,
331                self.tz_aware,
332                uuid_rep_repr,
333                self.unicode_decode_error_handler,
334                self.tzinfo,
335                self.type_registry,
336            )
337        )
338
339    def _options_dict(self):
340        """Dictionary of the arguments used to create this object."""
341        # TODO: PYTHON-2442 use _asdict() instead
342        return {
343            "document_class": self.document_class,
344            "tz_aware": self.tz_aware,
345            "uuid_representation": self.uuid_representation,
346            "unicode_decode_error_handler": self.unicode_decode_error_handler,
347            "tzinfo": self.tzinfo,
348            "type_registry": self.type_registry,
349        }
350
351    def __repr__(self):
352        return "%s(%s)" % (self.__class__.__name__, self._arguments_repr())
353
354    def with_options(self, **kwargs):
355        """Make a copy of this CodecOptions, overriding some options::
356
357            >>> from .codec_options import DEFAULT_CODEC_OPTIONS
358            >>> DEFAULT_CODEC_OPTIONS.tz_aware
359            False
360            >>> options = DEFAULT_CODEC_OPTIONS.with_options(tz_aware=True)
361            >>> options.tz_aware
362            True
363
364        .. versionadded:: 3.5
365        """
366        opts = self._options_dict()
367        opts.update(kwargs)
368        return CodecOptions(**opts)
369
370
371DEFAULT_CODEC_OPTIONS = CodecOptions()
372
373
374def _parse_codec_options(options):
375    """Parse BSON codec options."""
376    kwargs = {}
377    for k in set(options) & {
378        "document_class",
379        "tz_aware",
380        "uuidrepresentation",
381        "unicode_decode_error_handler",
382        "tzinfo",
383        "type_registry",
384    }:
385        if k == "uuidrepresentation":
386            kwargs["uuid_representation"] = options[k]
387        else:
388            kwargs[k] = options[k]
389    return CodecOptions(**kwargs)
class TypeEncoder(abc.ABC):
44class TypeEncoder(abc.ABC):
45    """Base class for defining type codec classes which describe how a
46    custom type can be transformed to one of the types BSON understands.
47
48    Codec classes must implement the ``python_type`` attribute, and the
49    ``transform_python`` method to support encoding.
50
51    See :ref:`custom-type-type-codec` documentation for an example.
52    """
53
54    @_abstractproperty
55    def python_type(self):
56        """The Python type to be converted into something serializable."""
57        pass
58
59    @abc.abstractmethod
60    def transform_python(self, value):
61        """Convert the given Python object into something serializable."""
62        pass

Base class for defining type codec classes which describe how a custom type can be transformed to one of the types BSON understands.

Codec classes must implement the python_type attribute, and the transform_python method to support encoding.

See :ref:custom-type-type-codec documentation for an example.

python_type

The Python type to be converted into something serializable.

@abc.abstractmethod
def transform_python(self, value):
59    @abc.abstractmethod
60    def transform_python(self, value):
61        """Convert the given Python object into something serializable."""
62        pass

Convert the given Python object into something serializable.

class TypeDecoder(abc.ABC):
65class TypeDecoder(abc.ABC):
66    """Base class for defining type codec classes which describe how a
67    BSON type can be transformed to a custom type.
68
69    Codec classes must implement the ``bson_type`` attribute, and the
70    ``transform_bson`` method to support decoding.
71
72    See :ref:`custom-type-type-codec` documentation for an example.
73    """
74
75    @_abstractproperty
76    def bson_type(self):
77        """The BSON type to be converted into our own type."""
78        pass
79
80    @abc.abstractmethod
81    def transform_bson(self, value):
82        """Convert the given BSON value into our own type."""
83        pass

Base class for defining type codec classes which describe how a BSON type can be transformed to a custom type.

Codec classes must implement the bson_type attribute, and the transform_bson method to support decoding.

See :ref:custom-type-type-codec documentation for an example.

bson_type

The BSON type to be converted into our own type.

@abc.abstractmethod
def transform_bson(self, value):
80    @abc.abstractmethod
81    def transform_bson(self, value):
82        """Convert the given BSON value into our own type."""
83        pass

Convert the given BSON value into our own type.

class TypeCodec(TypeEncoder, TypeDecoder):
86class TypeCodec(TypeEncoder, TypeDecoder):
87    """Base class for defining type codec classes which describe how a
88    custom type can be transformed to/from one of the types :mod:`bson`
89    can already encode/decode.
90
91    Codec classes must implement the ``python_type`` attribute, and the
92    ``transform_python`` method to support encoding, as well as the
93    ``bson_type`` attribute, and the ``transform_bson`` method to support
94    decoding.
95
96    See :ref:`custom-type-type-codec` documentation for an example.
97    """
98
99    pass

Base class for defining type codec classes which describe how a custom type can be transformed to/from one of the types bson can already encode/decode.

Codec classes must implement the python_type attribute, and the transform_python method to support encoding, as well as the bson_type attribute, and the transform_bson method to support decoding.

See :ref:custom-type-type-codec documentation for an example.

class TypeRegistry:
102class TypeRegistry(object):
103    """Encapsulates type codecs used in encoding and / or decoding BSON, as
104    well as the fallback encoder. Type registries cannot be modified after
105    instantiation.
106
107    ``TypeRegistry`` can be initialized with an iterable of type codecs, and
108    a callable for the fallback encoder::
109
110      >>> from .codec_options import TypeRegistry
111      >>> type_registry = TypeRegistry([Codec1, Codec2, Codec3, ...],
112      ...                              fallback_encoder)
113
114    See :ref:`custom-type-type-registry` documentation for an example.
115
116    :Parameters:
117      - `type_codecs` (optional): iterable of type codec instances. If
118        ``type_codecs`` contains multiple codecs that transform a single
119        python or BSON type, the transformation specified by the type codec
120        occurring last prevails. A TypeError will be raised if one or more
121        type codecs modify the encoding behavior of a built-in :mod:`bson`
122        type.
123      - `fallback_encoder` (optional): callable that accepts a single,
124        unencodable python value and transforms it into a type that
125        :mod:`bson` can encode. See :ref:`fallback-encoder-callable`
126        documentation for an example.
127    """
128
129    def __init__(self, type_codecs=None, fallback_encoder=None):
130        self.__type_codecs = list(type_codecs or [])
131        self._fallback_encoder = fallback_encoder
132        self._encoder_map = {}
133        self._decoder_map = {}
134
135        if self._fallback_encoder is not None:
136            if not callable(fallback_encoder):
137                raise TypeError("fallback_encoder %r is not a callable" % (fallback_encoder))
138
139        for codec in self.__type_codecs:
140            is_valid_codec = False
141            if isinstance(codec, TypeEncoder):
142                self._validate_type_encoder(codec)
143                is_valid_codec = True
144                self._encoder_map[codec.python_type] = codec.transform_python
145            if isinstance(codec, TypeDecoder):
146                is_valid_codec = True
147                self._decoder_map[codec.bson_type] = codec.transform_bson
148            if not is_valid_codec:
149                raise TypeError(
150                    "Expected an instance of %s, %s, or %s, got %r instead"
151                    % (TypeEncoder.__name__, TypeDecoder.__name__, TypeCodec.__name__, codec)
152                )
153
154    def _validate_type_encoder(self, codec):
155        from . import _BUILT_IN_TYPES
156
157        for pytype in _BUILT_IN_TYPES:
158            if issubclass(codec.python_type, pytype):
159                err_msg = (
160                    "TypeEncoders cannot change how built-in types are "
161                    "encoded (encoder %s transforms type %s)" % (codec, pytype)
162                )
163                raise TypeError(err_msg)
164
165    def __repr__(self):
166        return "%s(type_codecs=%r, fallback_encoder=%r)" % (
167            self.__class__.__name__,
168            self.__type_codecs,
169            self._fallback_encoder,
170        )
171
172    def __eq__(self, other):
173        if not isinstance(other, type(self)):
174            return NotImplemented
175        return (
176            (self._decoder_map == other._decoder_map)
177            and (self._encoder_map == other._encoder_map)
178            and (self._fallback_encoder == other._fallback_encoder)
179        )

Encapsulates type codecs used in encoding and / or decoding BSON, as well as the fallback encoder. Type registries cannot be modified after instantiation.

TypeRegistry can be initialized with an iterable of type codecs, and a callable for the fallback encoder::

>>> from .codec_options import TypeRegistry
>>> type_registry = TypeRegistry([Codec1, Codec2, Codec3, ...],
...                              fallback_encoder)

See :ref:custom-type-type-registry documentation for an example.

:Parameters:

  • type_codecs (optional): iterable of type codec instances. If type_codecs contains multiple codecs that transform a single python or BSON type, the transformation specified by the type codec occurring last prevails. A TypeError will be raised if one or more type codecs modify the encoding behavior of a built-in bson type.
  • fallback_encoder (optional): callable that accepts a single, unencodable python value and transforms it into a type that bson can encode. See :ref:fallback-encoder-callable documentation for an example.
TypeRegistry(type_codecs=None, fallback_encoder=None)
129    def __init__(self, type_codecs=None, fallback_encoder=None):
130        self.__type_codecs = list(type_codecs or [])
131        self._fallback_encoder = fallback_encoder
132        self._encoder_map = {}
133        self._decoder_map = {}
134
135        if self._fallback_encoder is not None:
136            if not callable(fallback_encoder):
137                raise TypeError("fallback_encoder %r is not a callable" % (fallback_encoder))
138
139        for codec in self.__type_codecs:
140            is_valid_codec = False
141            if isinstance(codec, TypeEncoder):
142                self._validate_type_encoder(codec)
143                is_valid_codec = True
144                self._encoder_map[codec.python_type] = codec.transform_python
145            if isinstance(codec, TypeDecoder):
146                is_valid_codec = True
147                self._decoder_map[codec.bson_type] = codec.transform_bson
148            if not is_valid_codec:
149                raise TypeError(
150                    "Expected an instance of %s, %s, or %s, got %r instead"
151                    % (TypeEncoder.__name__, TypeDecoder.__name__, TypeCodec.__name__, codec)
152                )
class CodecOptions(CodecOptions):
195class CodecOptions(_options_base):
196    """Encapsulates options used encoding and / or decoding BSON.
197
198    The `document_class` option is used to define a custom type for use
199    decoding BSON documents. Access to the underlying raw BSON bytes for
200    a document is available using the :class:`~bson.raw_bson.RawBSONDocument`
201    type::
202
203      >>> from .raw_bson import RawBSONDocument
204      >>> from .codec_options import CodecOptions
205      >>> codec_options = CodecOptions(document_class=RawBSONDocument)
206      >>> coll = db.get_collection('test', codec_options=codec_options)
207      >>> doc = coll.find_one()
208      >>> doc.raw
209      '\\x16\\x00\\x00\\x00\\x07_id\\x00[0\\x165\\x91\\x10\\xea\\x14\\xe8\\xc5\\x8b\\x93\\x00'
210
211    The document class can be any type that inherits from
212    :class:`~collections.abc.MutableMapping`::
213
214      >>> class AttributeDict(dict):
215      ...     # A dict that supports attribute access.
216      ...     def __getattr__(self, key):
217      ...         return self[key]
218      ...     def __setattr__(self, key, value):
219      ...         self[key] = value
220      ...
221      >>> codec_options = CodecOptions(document_class=AttributeDict)
222      >>> coll = db.get_collection('test', codec_options=codec_options)
223      >>> doc = coll.find_one()
224      >>> doc._id
225      ObjectId('5b3016359110ea14e8c58b93')
226
227    See :doc:`/examples/datetimes` for examples using the `tz_aware` and
228    `tzinfo` options.
229
230    See :doc:`examples/uuid` for examples using the `uuid_representation`
231    option.
232
233    :Parameters:
234      - `document_class`: BSON documents returned in queries will be decoded
235        to an instance of this class. Must be a subclass of
236        :class:`~collections.abc.MutableMapping`. Defaults to :class:`dict`.
237      - `tz_aware`: If ``True``, BSON datetimes will be decoded to timezone
238        aware instances of :class:`~datetime.datetime`. Otherwise they will be
239        naive. Defaults to ``False``.
240      - `uuid_representation`: The BSON representation to use when encoding
241        and decoding instances of :class:`~uuid.UUID`. Defaults to
242        :data:`~bson.binary.UuidRepresentation.UNSPECIFIED`. New
243        applications should consider setting this to
244        :data:`~bson.binary.UuidRepresentation.STANDARD` for cross language
245        compatibility. See :ref:`handling-uuid-data-example` for details.
246      - `unicode_decode_error_handler`: The error handler to apply when
247        a Unicode-related error occurs during BSON decoding that would
248        otherwise raise :exc:`UnicodeDecodeError`. Valid options include
249        'strict', 'replace', 'backslashreplace', 'surrogateescape', and
250        'ignore'. Defaults to 'strict'.
251      - `tzinfo`: A :class:`~datetime.tzinfo` subclass that specifies the
252        timezone to/from which :class:`~datetime.datetime` objects should be
253        encoded/decoded.
254      - `type_registry`: Instance of :class:`TypeRegistry` used to customize
255        encoding and decoding behavior.
256
257    .. versionchanged:: 4.0
258       The default for `uuid_representation` was changed from
259       :const:`~bson.binary.UuidRepresentation.PYTHON_LEGACY` to
260       :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`.
261
262    .. versionadded:: 3.8
263       `type_registry` attribute.
264
265    .. warning:: Care must be taken when changing
266       `unicode_decode_error_handler` from its default value ('strict').
267       The 'replace' and 'ignore' modes should not be used when documents
268       retrieved from the server will be modified in the client application
269       and stored back to the server.
270    """
271
272    def __new__(
273        cls,
274        document_class=dict,
275        tz_aware=False,
276        uuid_representation=UuidRepresentation.UNSPECIFIED,
277        unicode_decode_error_handler="strict",
278        tzinfo=None,
279        type_registry=None,
280    ):
281        if not (issubclass(document_class, _MutableMapping) or _raw_document_class(document_class)):
282            raise TypeError(
283                "document_class must be dict, bson.son.SON, "
284                "bson.raw_bson.RawBSONDocument, or a "
285                "sublass of collections.abc.MutableMapping"
286            )
287        if not isinstance(tz_aware, bool):
288            raise TypeError("tz_aware must be True or False")
289        if uuid_representation not in ALL_UUID_REPRESENTATIONS:
290            raise ValueError(
291                "uuid_representation must be a value " "from .binary.UuidRepresentation"
292            )
293        if not isinstance(unicode_decode_error_handler, (str, None)):
294            raise ValueError("unicode_decode_error_handler must be a string " "or None")
295        if tzinfo is not None:
296            if not isinstance(tzinfo, datetime.tzinfo):
297                raise TypeError("tzinfo must be an instance of datetime.tzinfo")
298            if not tz_aware:
299                raise ValueError("cannot specify tzinfo without also setting tz_aware=True")
300
301        type_registry = type_registry or TypeRegistry()
302
303        if not isinstance(type_registry, TypeRegistry):
304            raise TypeError("type_registry must be an instance of TypeRegistry")
305
306        return tuple.__new__(
307            cls,
308            (
309                document_class,
310                tz_aware,
311                uuid_representation,
312                unicode_decode_error_handler,
313                tzinfo,
314                type_registry,
315            ),
316        )
317
318    def _arguments_repr(self):
319        """Representation of the arguments used to create this object."""
320        document_class_repr = "dict" if self.document_class is dict else repr(self.document_class)
321
322        uuid_rep_repr = UUID_REPRESENTATION_NAMES.get(
323            self.uuid_representation, self.uuid_representation
324        )
325
326        return (
327            "document_class=%s, tz_aware=%r, uuid_representation=%s, "
328            "unicode_decode_error_handler=%r, tzinfo=%r, "
329            "type_registry=%r"
330            % (
331                document_class_repr,
332                self.tz_aware,
333                uuid_rep_repr,
334                self.unicode_decode_error_handler,
335                self.tzinfo,
336                self.type_registry,
337            )
338        )
339
340    def _options_dict(self):
341        """Dictionary of the arguments used to create this object."""
342        # TODO: PYTHON-2442 use _asdict() instead
343        return {
344            "document_class": self.document_class,
345            "tz_aware": self.tz_aware,
346            "uuid_representation": self.uuid_representation,
347            "unicode_decode_error_handler": self.unicode_decode_error_handler,
348            "tzinfo": self.tzinfo,
349            "type_registry": self.type_registry,
350        }
351
352    def __repr__(self):
353        return "%s(%s)" % (self.__class__.__name__, self._arguments_repr())
354
355    def with_options(self, **kwargs):
356        """Make a copy of this CodecOptions, overriding some options::
357
358            >>> from .codec_options import DEFAULT_CODEC_OPTIONS
359            >>> DEFAULT_CODEC_OPTIONS.tz_aware
360            False
361            >>> options = DEFAULT_CODEC_OPTIONS.with_options(tz_aware=True)
362            >>> options.tz_aware
363            True
364
365        .. versionadded:: 3.5
366        """
367        opts = self._options_dict()
368        opts.update(kwargs)
369        return CodecOptions(**opts)

Encapsulates options used encoding and / or decoding BSON.

The document_class option is used to define a custom type for use decoding BSON documents. Access to the underlying raw BSON bytes for a document is available using the ~bson.raw_bson.RawBSONDocument type::

>>> from .raw_bson import RawBSONDocument
>>> from .codec_options import CodecOptions
>>> codec_options = CodecOptions(document_class=RawBSONDocument)
>>> coll = db.get_collection('test', codec_options=codec_options)
>>> doc = coll.find_one()
>>> doc.raw
'\x16\x00\x00\x00\x07_id\x00[0\x165\x91\x10\xea\x14\xe8\xc5\x8b\x93\x00'

The document class can be any type that inherits from ~collections.abc.MutableMapping::

>>> class AttributeDict(dict):
...     # A dict that supports attribute access.
...     def __getattr__(self, key):
...         return self[key]
...     def __setattr__(self, key, value):
...         self[key] = value
...
>>> codec_options = CodecOptions(document_class=AttributeDict)
>>> coll = db.get_collection('test', codec_options=codec_options)
>>> doc = coll.find_one()
>>> doc._id
ObjectId('5b3016359110ea14e8c58b93')

See :doc:/examples/datetimes for examples using the tz_aware and tzinfo options.

See :doc:examples/uuid for examples using the uuid_representation option.

:Parameters:

  • document_class: BSON documents returned in queries will be decoded to an instance of this class. Must be a subclass of ~collections.abc.MutableMapping. Defaults to dict.
  • tz_aware: If True, BSON datetimes will be decoded to timezone aware instances of ~datetime.datetime. Otherwise they will be naive. Defaults to False.
  • uuid_representation: The BSON representation to use when encoding and decoding instances of ~uuid.UUID. Defaults to ~bson.binary.UuidRepresentation.UNSPECIFIED. New applications should consider setting this to ~bson.binary.UuidRepresentation.STANDARD for cross language compatibility. See :ref:handling-uuid-data-example for details.
  • unicode_decode_error_handler: The error handler to apply when a Unicode-related error occurs during BSON decoding that would otherwise raise UnicodeDecodeError. Valid options include 'strict', 'replace', 'backslashreplace', 'surrogateescape', and 'ignore'. Defaults to 'strict'.
  • tzinfo: A ~datetime.tzinfo subclass that specifies the timezone to/from which ~datetime.datetime objects should be encoded/decoded.
  • type_registry: Instance of TypeRegistry used to customize encoding and decoding behavior.

Changed in version 4.0: The default for uuid_representation was changed from ~bson.binary.UuidRepresentation.PYTHON_LEGACY to ~bson.binary.UuidRepresentation.UNSPECIFIED.

New in version 3.8: type_registry attribute.

Care must be taken when changing

unicode_decode_error_handler from its default value ('strict'). The 'replace' and 'ignore' modes should not be used when documents retrieved from the server will be modified in the client application and stored back to the server.

def with_options(self, **kwargs):
355    def with_options(self, **kwargs):
356        """Make a copy of this CodecOptions, overriding some options::
357
358            >>> from .codec_options import DEFAULT_CODEC_OPTIONS
359            >>> DEFAULT_CODEC_OPTIONS.tz_aware
360            False
361            >>> options = DEFAULT_CODEC_OPTIONS.with_options(tz_aware=True)
362            >>> options.tz_aware
363            True
364
365        .. versionadded:: 3.5
366        """
367        opts = self._options_dict()
368        opts.update(kwargs)
369        return CodecOptions(**opts)

Make a copy of this CodecOptions, overriding some options::

>>> from .codec_options import DEFAULT_CODEC_OPTIONS
>>> DEFAULT_CODEC_OPTIONS.tz_aware
False
>>> options = DEFAULT_CODEC_OPTIONS.with_options(tz_aware=True)
>>> options.tz_aware
True

New in version 3.5.

document_class

Alias for field number 0

tz_aware

Alias for field number 1

uuid_representation

Alias for field number 2

unicode_decode_error_handler

Alias for field number 3

tzinfo

Alias for field number 4

type_registry

Alias for field number 5

Inherited Members
builtins.tuple
index
count
DEFAULT_CODEC_OPTIONS = CodecOptions(document_class=dict, tz_aware=False, uuid_representation=UuidRepresentation.UNSPECIFIED, unicode_decode_error_handler='strict', tzinfo=None, type_registry=TypeRegistry(type_codecs=[], fallback_encoder=None))