Completed
Push — master ( 5316ba...5569c2 )
by Satoru
01:10
created

Parser.load_from_string()   B

Complexity

Conditions 5

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 5
c 2
b 1
f 0
dl 0
loc 20
rs 8.5454
1
#
2
# Copyright (C) 2015 - 2017 Satoru SATOH <ssato @ redhat.com>
3
# License: MIT
4
#
5
# Ref. python -c "import bson; help(bson)"
6
#
7
r"""BSON backend:
8
9
- Format to support: BSON, http://bsonspec.org
10
- Requirements: bson in pymongo, https://pypi.python.org/pypi/pymongo/
11
- Development Status: 3 - Alpha
12
- Limitations: It seems that the APIs of bson.decode\* were changed a lot in
13
  the current version (3.3) of python-bson in pymongo and this backend might
14
  not work with it. I don't have a time to test with that latest version yet
15
  and it's only tested with the older one, 3.3.1.
16
- Special options:
17
18
  - All keyword options for :meth:`encode` (dump{s,}) and :meth:`decode`
19
    (load{s,}) of :class:`bson.BSON` except for as_class should just work.
20
21
  - See also: https://api.mongodb.org/python/current/api/bson/
22
23
Changelog:
24
25
.. versionchanged:: 0.8.3
26
27
   - follow changes of options of bson.BSON.{encode,decode} in its upstream and
28
     changed or added some keyword options including ones for bson.CodecOptions
29
30
.. versionchanged:: 0.5.0
31
32
   - utilize as_class keyword argument to allow container objects made directly
33
     on load if C extension is not used and enabled.
34
35
   - _load_opts() was removed because C extension looks forced to be enalbed if
36
     bson.has_c() == True, that is, C extension was built, installed and used.
37
     see also: https://jira.mongodb.org/browse/PYTHON-379
38
39
    .. versionadded:: 0.1.0
40
"""
41
from __future__ import absolute_import
42
43
import bson
44
import anyconfig.backend.base
45
import anyconfig.utils
46
47
48
_CO_OPTIONS = ("document_class", "tz_aware", "uuid_representation",
49
               "unicode_decode_error_handler", "tzinfo")
50
51
52
def _codec_options(**options):
53
    """
54
    bson.BSON.{decode{,_all},encode} can receive bson.CodecOptions.
55
56
    :return: :class:`~bson.CodecOptions`
57
    """
58
    opts = anyconfig.utils.filter_options(_CO_OPTIONS, options)
59
    return bson.CodecOptions(**opts)
60
61
62
class Parser(anyconfig.backend.base.FromStringLoader,
63
             anyconfig.backend.base.ToStringDumper,
64
             anyconfig.backend.base.BinaryFilesMixin):
65
    """
66
    Loader/Dumper of BSON files.
67
    """
68
    _type = "bson"
69
    _extensions = ["bson", "bsn"]  # Temporary.
70
    _load_opts = [] if bson.has_c() else ["codec_options"]
71
    _dump_opts = [] if bson.has_c() else ["check_keys", "codec_options"]
72
    _ordered = not bson.has_c()
73
74
    def _load_options(self, container, **options):
75
        """
76
        :param container: callble to make a container object later
77
        """
78
        if "codec_options" not in options:
79
            options.setdefault("document_class", container)
80
            if any(k in options for k in _CO_OPTIONS):
81
                options["codec_options"] = _codec_options(**options)
82
83
        return anyconfig.utils.filter_options(self._load_opts, options)
84
85
    def load_from_string(self, content, container, **kwargs):
86
        """
87
        Load BSON config from given string `content`.
88
89
        :param content: BSON config content in bytes data string
90
        :param container: callble to make a container object
91
        :param kwargs: optional keyword parameters
92
93
        :return: Dict-like object holding config parameters
94
        """
95
        if self._load_opts:  # indicates that C extension is not used.
96
            objs = bson.decode_all(content, **kwargs)
97
        else:
98
            # .. note::
99
            #    The order of loaded configuration keys may be lost but
100
            #    there is no way to avoid that, AFAIK.
101
            objs = [container(x) for x in bson.decode_all(content)
102
                    if x is not None]
103
104
        return objs[0] if objs else None
105
106
    def dump_to_string(self, data, **options):
107
        """Dump BSON data `data` to a string.
108
109
        :param data: BSON Data to dump
110
        :param options: optional keyword parameters to be sanitized
111
        :return: string represents the configuration
112
        """
113
        if self._dump_opts:
114
            container = self._container_factory(**options)
115
            opts = self._load_options(container, **options)
116
            for key in self._dump_opts:
117
                if options.get(key, False):
118
                    opts[key] = options[key]
119
            return bson.BSON.encode(data, *opts)
120
        else:
121
            return bson.BSON.encode(data)
122
123
# vim:sw=4:ts=4:et:
124