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
|
|
|
|