sensu-plugins-mongodb-mrtrotl 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1 -0
  3. data/LICENSE +22 -0
  4. data/README.md +27 -0
  5. data/bin/check-mongodb-metric.rb +144 -0
  6. data/bin/check-mongodb-query-count.rb +267 -0
  7. data/bin/check-mongodb.py +1644 -0
  8. data/bin/check-mongodb.rb +5 -0
  9. data/bin/metrics-mongodb-replication.rb +254 -0
  10. data/bin/metrics-mongodb.rb +133 -0
  11. data/lib/bson/__init__.py +1347 -0
  12. data/lib/bson/__pycache__/__init__.cpython-310.pyc +0 -0
  13. data/lib/bson/__pycache__/_helpers.cpython-310.pyc +0 -0
  14. data/lib/bson/__pycache__/binary.cpython-310.pyc +0 -0
  15. data/lib/bson/__pycache__/code.cpython-310.pyc +0 -0
  16. data/lib/bson/__pycache__/codec_options.cpython-310.pyc +0 -0
  17. data/lib/bson/__pycache__/dbref.cpython-310.pyc +0 -0
  18. data/lib/bson/__pycache__/decimal128.cpython-310.pyc +0 -0
  19. data/lib/bson/__pycache__/errors.cpython-310.pyc +0 -0
  20. data/lib/bson/__pycache__/int64.cpython-310.pyc +0 -0
  21. data/lib/bson/__pycache__/json_util.cpython-310.pyc +0 -0
  22. data/lib/bson/__pycache__/max_key.cpython-310.pyc +0 -0
  23. data/lib/bson/__pycache__/min_key.cpython-310.pyc +0 -0
  24. data/lib/bson/__pycache__/objectid.cpython-310.pyc +0 -0
  25. data/lib/bson/__pycache__/raw_bson.cpython-310.pyc +0 -0
  26. data/lib/bson/__pycache__/regex.cpython-310.pyc +0 -0
  27. data/lib/bson/__pycache__/son.cpython-310.pyc +0 -0
  28. data/lib/bson/__pycache__/timestamp.cpython-310.pyc +0 -0
  29. data/lib/bson/__pycache__/tz_util.cpython-310.pyc +0 -0
  30. data/lib/bson/_cbson.cpython-310-x86_64-linux-gnu.so +0 -0
  31. data/lib/bson/_helpers.py +41 -0
  32. data/lib/bson/binary.py +364 -0
  33. data/lib/bson/code.py +101 -0
  34. data/lib/bson/codec_options.py +414 -0
  35. data/lib/bson/codec_options.pyi +100 -0
  36. data/lib/bson/dbref.py +133 -0
  37. data/lib/bson/decimal128.py +314 -0
  38. data/lib/bson/errors.py +35 -0
  39. data/lib/bson/int64.py +39 -0
  40. data/lib/bson/json_util.py +874 -0
  41. data/lib/bson/max_key.py +55 -0
  42. data/lib/bson/min_key.py +55 -0
  43. data/lib/bson/objectid.py +286 -0
  44. data/lib/bson/py.typed +2 -0
  45. data/lib/bson/raw_bson.py +175 -0
  46. data/lib/bson/regex.py +135 -0
  47. data/lib/bson/son.py +208 -0
  48. data/lib/bson/timestamp.py +124 -0
  49. data/lib/bson/tz_util.py +52 -0
  50. data/lib/gridfs/__init__.py +1015 -0
  51. data/lib/gridfs/__pycache__/__init__.cpython-310.pyc +0 -0
  52. data/lib/gridfs/__pycache__/errors.cpython-310.pyc +0 -0
  53. data/lib/gridfs/__pycache__/grid_file.cpython-310.pyc +0 -0
  54. data/lib/gridfs/errors.py +33 -0
  55. data/lib/gridfs/grid_file.py +907 -0
  56. data/lib/gridfs/py.typed +2 -0
  57. data/lib/pymongo/__init__.py +185 -0
  58. data/lib/pymongo/__pycache__/__init__.cpython-310.pyc +0 -0
  59. data/lib/pymongo/__pycache__/_csot.cpython-310.pyc +0 -0
  60. data/lib/pymongo/__pycache__/aggregation.cpython-310.pyc +0 -0
  61. data/lib/pymongo/__pycache__/auth.cpython-310.pyc +0 -0
  62. data/lib/pymongo/__pycache__/auth_aws.cpython-310.pyc +0 -0
  63. data/lib/pymongo/__pycache__/bulk.cpython-310.pyc +0 -0
  64. data/lib/pymongo/__pycache__/change_stream.cpython-310.pyc +0 -0
  65. data/lib/pymongo/__pycache__/client_options.cpython-310.pyc +0 -0
  66. data/lib/pymongo/__pycache__/client_session.cpython-310.pyc +0 -0
  67. data/lib/pymongo/__pycache__/collation.cpython-310.pyc +0 -0
  68. data/lib/pymongo/__pycache__/collection.cpython-310.pyc +0 -0
  69. data/lib/pymongo/__pycache__/command_cursor.cpython-310.pyc +0 -0
  70. data/lib/pymongo/__pycache__/common.cpython-310.pyc +0 -0
  71. data/lib/pymongo/__pycache__/compression_support.cpython-310.pyc +0 -0
  72. data/lib/pymongo/__pycache__/cursor.cpython-310.pyc +0 -0
  73. data/lib/pymongo/__pycache__/daemon.cpython-310.pyc +0 -0
  74. data/lib/pymongo/__pycache__/database.cpython-310.pyc +0 -0
  75. data/lib/pymongo/__pycache__/driver_info.cpython-310.pyc +0 -0
  76. data/lib/pymongo/__pycache__/encryption.cpython-310.pyc +0 -0
  77. data/lib/pymongo/__pycache__/encryption_options.cpython-310.pyc +0 -0
  78. data/lib/pymongo/__pycache__/errors.cpython-310.pyc +0 -0
  79. data/lib/pymongo/__pycache__/event_loggers.cpython-310.pyc +0 -0
  80. data/lib/pymongo/__pycache__/hello.cpython-310.pyc +0 -0
  81. data/lib/pymongo/__pycache__/helpers.cpython-310.pyc +0 -0
  82. data/lib/pymongo/__pycache__/max_staleness_selectors.cpython-310.pyc +0 -0
  83. data/lib/pymongo/__pycache__/message.cpython-310.pyc +0 -0
  84. data/lib/pymongo/__pycache__/mongo_client.cpython-310.pyc +0 -0
  85. data/lib/pymongo/__pycache__/monitor.cpython-310.pyc +0 -0
  86. data/lib/pymongo/__pycache__/monitoring.cpython-310.pyc +0 -0
  87. data/lib/pymongo/__pycache__/network.cpython-310.pyc +0 -0
  88. data/lib/pymongo/__pycache__/ocsp_cache.cpython-310.pyc +0 -0
  89. data/lib/pymongo/__pycache__/ocsp_support.cpython-310.pyc +0 -0
  90. data/lib/pymongo/__pycache__/operations.cpython-310.pyc +0 -0
  91. data/lib/pymongo/__pycache__/periodic_executor.cpython-310.pyc +0 -0
  92. data/lib/pymongo/__pycache__/pool.cpython-310.pyc +0 -0
  93. data/lib/pymongo/__pycache__/pyopenssl_context.cpython-310.pyc +0 -0
  94. data/lib/pymongo/__pycache__/read_concern.cpython-310.pyc +0 -0
  95. data/lib/pymongo/__pycache__/read_preferences.cpython-310.pyc +0 -0
  96. data/lib/pymongo/__pycache__/response.cpython-310.pyc +0 -0
  97. data/lib/pymongo/__pycache__/results.cpython-310.pyc +0 -0
  98. data/lib/pymongo/__pycache__/saslprep.cpython-310.pyc +0 -0
  99. data/lib/pymongo/__pycache__/server.cpython-310.pyc +0 -0
  100. data/lib/pymongo/__pycache__/server_api.cpython-310.pyc +0 -0
  101. data/lib/pymongo/__pycache__/server_description.cpython-310.pyc +0 -0
  102. data/lib/pymongo/__pycache__/server_selectors.cpython-310.pyc +0 -0
  103. data/lib/pymongo/__pycache__/server_type.cpython-310.pyc +0 -0
  104. data/lib/pymongo/__pycache__/settings.cpython-310.pyc +0 -0
  105. data/lib/pymongo/__pycache__/socket_checker.cpython-310.pyc +0 -0
  106. data/lib/pymongo/__pycache__/srv_resolver.cpython-310.pyc +0 -0
  107. data/lib/pymongo/__pycache__/ssl_context.cpython-310.pyc +0 -0
  108. data/lib/pymongo/__pycache__/ssl_support.cpython-310.pyc +0 -0
  109. data/lib/pymongo/__pycache__/topology.cpython-310.pyc +0 -0
  110. data/lib/pymongo/__pycache__/topology_description.cpython-310.pyc +0 -0
  111. data/lib/pymongo/__pycache__/typings.cpython-310.pyc +0 -0
  112. data/lib/pymongo/__pycache__/uri_parser.cpython-310.pyc +0 -0
  113. data/lib/pymongo/__pycache__/write_concern.cpython-310.pyc +0 -0
  114. data/lib/pymongo/_cmessage.cpython-310-x86_64-linux-gnu.so +0 -0
  115. data/lib/pymongo/_csot.py +118 -0
  116. data/lib/pymongo/aggregation.py +229 -0
  117. data/lib/pymongo/auth.py +549 -0
  118. data/lib/pymongo/auth_aws.py +94 -0
  119. data/lib/pymongo/bulk.py +513 -0
  120. data/lib/pymongo/change_stream.py +457 -0
  121. data/lib/pymongo/client_options.py +302 -0
  122. data/lib/pymongo/client_session.py +1112 -0
  123. data/lib/pymongo/collation.py +224 -0
  124. data/lib/pymongo/collection.py +3204 -0
  125. data/lib/pymongo/command_cursor.py +353 -0
  126. data/lib/pymongo/common.py +984 -0
  127. data/lib/pymongo/compression_support.py +149 -0
  128. data/lib/pymongo/cursor.py +1345 -0
  129. data/lib/pymongo/daemon.py +141 -0
  130. data/lib/pymongo/database.py +1202 -0
  131. data/lib/pymongo/driver_info.py +42 -0
  132. data/lib/pymongo/encryption.py +884 -0
  133. data/lib/pymongo/encryption_options.py +221 -0
  134. data/lib/pymongo/errors.py +365 -0
  135. data/lib/pymongo/event_loggers.py +221 -0
  136. data/lib/pymongo/hello.py +219 -0
  137. data/lib/pymongo/helpers.py +259 -0
  138. data/lib/pymongo/max_staleness_selectors.py +114 -0
  139. data/lib/pymongo/message.py +1440 -0
  140. data/lib/pymongo/mongo_client.py +2144 -0
  141. data/lib/pymongo/monitor.py +440 -0
  142. data/lib/pymongo/monitoring.py +1801 -0
  143. data/lib/pymongo/network.py +311 -0
  144. data/lib/pymongo/ocsp_cache.py +87 -0
  145. data/lib/pymongo/ocsp_support.py +372 -0
  146. data/lib/pymongo/operations.py +507 -0
  147. data/lib/pymongo/periodic_executor.py +183 -0
  148. data/lib/pymongo/pool.py +1660 -0
  149. data/lib/pymongo/py.typed +2 -0
  150. data/lib/pymongo/pyopenssl_context.py +383 -0
  151. data/lib/pymongo/read_concern.py +75 -0
  152. data/lib/pymongo/read_preferences.py +609 -0
  153. data/lib/pymongo/response.py +109 -0
  154. data/lib/pymongo/results.py +217 -0
  155. data/lib/pymongo/saslprep.py +113 -0
  156. data/lib/pymongo/server.py +247 -0
  157. data/lib/pymongo/server_api.py +170 -0
  158. data/lib/pymongo/server_description.py +285 -0
  159. data/lib/pymongo/server_selectors.py +153 -0
  160. data/lib/pymongo/server_type.py +32 -0
  161. data/lib/pymongo/settings.py +159 -0
  162. data/lib/pymongo/socket_checker.py +104 -0
  163. data/lib/pymongo/srv_resolver.py +126 -0
  164. data/lib/pymongo/ssl_context.py +39 -0
  165. data/lib/pymongo/ssl_support.py +99 -0
  166. data/lib/pymongo/topology.py +890 -0
  167. data/lib/pymongo/topology_description.py +639 -0
  168. data/lib/pymongo/typings.py +39 -0
  169. data/lib/pymongo/uri_parser.py +624 -0
  170. data/lib/pymongo/write_concern.py +129 -0
  171. data/lib/pymongo-4.2.0.dist-info/INSTALLER +1 -0
  172. data/lib/pymongo-4.2.0.dist-info/LICENSE +201 -0
  173. data/lib/pymongo-4.2.0.dist-info/METADATA +250 -0
  174. data/lib/pymongo-4.2.0.dist-info/RECORD +167 -0
  175. data/lib/pymongo-4.2.0.dist-info/REQUESTED +0 -0
  176. data/lib/pymongo-4.2.0.dist-info/WHEEL +6 -0
  177. data/lib/pymongo-4.2.0.dist-info/top_level.txt +3 -0
  178. data/lib/sensu-plugins-mongodb/metrics.rb +391 -0
  179. data/lib/sensu-plugins-mongodb/version.rb +9 -0
  180. data/lib/sensu-plugins-mongodb.rb +1 -0
  181. metadata +407 -0
@@ -0,0 +1,874 @@
1
+ # Copyright 2009-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 using Python's :mod:`json` module with BSON documents.
16
+
17
+ This module provides two helper methods `dumps` and `loads` that wrap the
18
+ native :mod:`json` methods and provide explicit BSON conversion to and from
19
+ JSON. :class:`~bson.json_util.JSONOptions` provides a way to control how JSON
20
+ is emitted and parsed, with the default being the Relaxed Extended JSON format.
21
+ :mod:`~bson.json_util` can also generate Canonical or legacy `Extended JSON`_
22
+ when :const:`CANONICAL_JSON_OPTIONS` or :const:`LEGACY_JSON_OPTIONS` is
23
+ provided, respectively.
24
+
25
+ .. _Extended JSON: https://github.com/mongodb/specifications/blob/master/source/extended-json.rst
26
+
27
+ Example usage (deserialization):
28
+
29
+ .. doctest::
30
+
31
+ >>> from bson.json_util import loads
32
+ >>> loads('[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$scope": {}, "$code": "function x() { return 1; }"}}, {"bin": {"$type": "80", "$binary": "AQIDBA=="}}]')
33
+ [{'foo': [1, 2]}, {'bar': {'hello': 'world'}}, {'code': Code('function x() { return 1; }', {})}, {'bin': Binary(b'...', 128)}]
34
+
35
+ Example usage with :const:`RELAXED_JSON_OPTIONS` (the default):
36
+
37
+ .. doctest::
38
+
39
+ >>> from bson import Binary, Code
40
+ >>> from bson.json_util import dumps
41
+ >>> dumps([{'foo': [1, 2]},
42
+ ... {'bar': {'hello': 'world'}},
43
+ ... {'code': Code("function x() { return 1; }")},
44
+ ... {'bin': Binary(b"\x01\x02\x03\x04")}])
45
+ '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }"}}, {"bin": {"$binary": {"base64": "AQIDBA==", "subType": "00"}}}]'
46
+
47
+ Example usage (with :const:`CANONICAL_JSON_OPTIONS`):
48
+
49
+ .. doctest::
50
+
51
+ >>> from bson import Binary, Code
52
+ >>> from bson.json_util import dumps, CANONICAL_JSON_OPTIONS
53
+ >>> dumps([{'foo': [1, 2]},
54
+ ... {'bar': {'hello': 'world'}},
55
+ ... {'code': Code("function x() { return 1; }")},
56
+ ... {'bin': Binary(b"\x01\x02\x03\x04")}],
57
+ ... json_options=CANONICAL_JSON_OPTIONS)
58
+ '[{"foo": [{"$numberInt": "1"}, {"$numberInt": "2"}]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }"}}, {"bin": {"$binary": {"base64": "AQIDBA==", "subType": "00"}}}]'
59
+
60
+ Example usage (with :const:`LEGACY_JSON_OPTIONS`):
61
+
62
+ .. doctest::
63
+
64
+ >>> from bson import Binary, Code
65
+ >>> from bson.json_util import dumps, LEGACY_JSON_OPTIONS
66
+ >>> dumps([{'foo': [1, 2]},
67
+ ... {'bar': {'hello': 'world'}},
68
+ ... {'code': Code("function x() { return 1; }", {})},
69
+ ... {'bin': Binary(b"\x01\x02\x03\x04")}],
70
+ ... json_options=LEGACY_JSON_OPTIONS)
71
+ '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }", "$scope": {}}}, {"bin": {"$binary": "AQIDBA==", "$type": "00"}}]'
72
+
73
+ Alternatively, you can manually pass the `default` to :func:`json.dumps`.
74
+ It won't handle :class:`~bson.binary.Binary` and :class:`~bson.code.Code`
75
+ instances (as they are extended strings you can't provide custom defaults),
76
+ but it will be faster as there is less recursion.
77
+
78
+ .. note::
79
+ If your application does not need the flexibility offered by
80
+ :class:`JSONOptions` and spends a large amount of time in the `json_util`
81
+ module, look to
82
+ `python-bsonjs <https://pypi.python.org/pypi/python-bsonjs>`_ for a nice
83
+ performance improvement. `python-bsonjs` is a fast BSON to MongoDB
84
+ Extended JSON converter for Python built on top of
85
+ `libbson <https://github.com/mongodb/libbson>`_. `python-bsonjs` works best
86
+ with PyMongo when using :class:`~bson.raw_bson.RawBSONDocument`.
87
+ """
88
+
89
+ import base64
90
+ import datetime
91
+ import json
92
+ import math
93
+ import re
94
+ import uuid
95
+ from typing import Any, Dict, Mapping, Optional, Sequence, Tuple, Type, Union, cast
96
+
97
+ import bson
98
+ from bson import EPOCH_AWARE
99
+ from bson.binary import ALL_UUID_SUBTYPES, UUID_SUBTYPE, Binary, UuidRepresentation
100
+ from bson.code import Code
101
+ from bson.codec_options import CodecOptions
102
+ from bson.dbref import DBRef
103
+ from bson.decimal128 import Decimal128
104
+ from bson.int64 import Int64
105
+ from bson.max_key import MaxKey
106
+ from bson.min_key import MinKey
107
+ from bson.objectid import ObjectId
108
+ from bson.regex import Regex
109
+ from bson.son import RE_TYPE, SON
110
+ from bson.timestamp import Timestamp
111
+ from bson.tz_util import utc
112
+
113
+ _RE_OPT_TABLE = {
114
+ "i": re.I,
115
+ "l": re.L,
116
+ "m": re.M,
117
+ "s": re.S,
118
+ "u": re.U,
119
+ "x": re.X,
120
+ }
121
+
122
+
123
+ class DatetimeRepresentation:
124
+ LEGACY = 0
125
+ """Legacy MongoDB Extended JSON datetime representation.
126
+
127
+ :class:`datetime.datetime` instances will be encoded to JSON in the
128
+ format `{"$date": <dateAsMilliseconds>}`, where `dateAsMilliseconds` is
129
+ a 64-bit signed integer giving the number of milliseconds since the Unix
130
+ epoch UTC. This was the default encoding before PyMongo version 3.4.
131
+
132
+ .. versionadded:: 3.4
133
+ """
134
+
135
+ NUMBERLONG = 1
136
+ """NumberLong datetime representation.
137
+
138
+ :class:`datetime.datetime` instances will be encoded to JSON in the
139
+ format `{"$date": {"$numberLong": "<dateAsMilliseconds>"}}`,
140
+ where `dateAsMilliseconds` is the string representation of a 64-bit signed
141
+ integer giving the number of milliseconds since the Unix epoch UTC.
142
+
143
+ .. versionadded:: 3.4
144
+ """
145
+
146
+ ISO8601 = 2
147
+ """ISO-8601 datetime representation.
148
+
149
+ :class:`datetime.datetime` instances greater than or equal to the Unix
150
+ epoch UTC will be encoded to JSON in the format `{"$date": "<ISO-8601>"}`.
151
+ :class:`datetime.datetime` instances before the Unix epoch UTC will be
152
+ encoded as if the datetime representation is
153
+ :const:`~DatetimeRepresentation.NUMBERLONG`.
154
+
155
+ .. versionadded:: 3.4
156
+ """
157
+
158
+
159
+ class JSONMode:
160
+ LEGACY = 0
161
+ """Legacy Extended JSON representation.
162
+
163
+ In this mode, :func:`~bson.json_util.dumps` produces PyMongo's legacy
164
+ non-standard JSON output. Consider using
165
+ :const:`~bson.json_util.JSONMode.RELAXED` or
166
+ :const:`~bson.json_util.JSONMode.CANONICAL` instead.
167
+
168
+ .. versionadded:: 3.5
169
+ """
170
+
171
+ RELAXED = 1
172
+ """Relaxed Extended JSON representation.
173
+
174
+ In this mode, :func:`~bson.json_util.dumps` produces Relaxed Extended JSON,
175
+ a mostly JSON-like format. Consider using this for things like a web API,
176
+ where one is sending a document (or a projection of a document) that only
177
+ uses ordinary JSON type primitives. In particular, the ``int``,
178
+ :class:`~bson.int64.Int64`, and ``float`` numeric types are represented in
179
+ the native JSON number format. This output is also the most human readable
180
+ and is useful for debugging and documentation.
181
+
182
+ .. seealso:: The specification for Relaxed `Extended JSON`_.
183
+
184
+ .. versionadded:: 3.5
185
+ """
186
+
187
+ CANONICAL = 2
188
+ """Canonical Extended JSON representation.
189
+
190
+ In this mode, :func:`~bson.json_util.dumps` produces Canonical Extended
191
+ JSON, a type preserving format. Consider using this for things like
192
+ testing, where one has to precisely specify expected types in JSON. In
193
+ particular, the ``int``, :class:`~bson.int64.Int64`, and ``float`` numeric
194
+ types are encoded with type wrappers.
195
+
196
+ .. seealso:: The specification for Canonical `Extended JSON`_.
197
+
198
+ .. versionadded:: 3.5
199
+ """
200
+
201
+
202
+ class JSONOptions(CodecOptions):
203
+ """Encapsulates JSON options for :func:`dumps` and :func:`loads`.
204
+
205
+ :Parameters:
206
+ - `strict_number_long`: If ``True``, :class:`~bson.int64.Int64` objects
207
+ are encoded to MongoDB Extended JSON's *Strict mode* type
208
+ `NumberLong`, ie ``'{"$numberLong": "<number>" }'``. Otherwise they
209
+ will be encoded as an `int`. Defaults to ``False``.
210
+ - `datetime_representation`: The representation to use when encoding
211
+ instances of :class:`datetime.datetime`. Defaults to
212
+ :const:`~DatetimeRepresentation.LEGACY`.
213
+ - `strict_uuid`: If ``True``, :class:`uuid.UUID` object are encoded to
214
+ MongoDB Extended JSON's *Strict mode* type `Binary`. Otherwise it
215
+ will be encoded as ``'{"$uuid": "<hex>" }'``. Defaults to ``False``.
216
+ - `json_mode`: The :class:`JSONMode` to use when encoding BSON types to
217
+ Extended JSON. Defaults to :const:`~JSONMode.LEGACY`.
218
+ - `document_class`: BSON documents returned by :func:`loads` will be
219
+ decoded to an instance of this class. Must be a subclass of
220
+ :class:`collections.MutableMapping`. Defaults to :class:`dict`.
221
+ - `uuid_representation`: The :class:`~bson.binary.UuidRepresentation`
222
+ to use when encoding and decoding instances of :class:`uuid.UUID`.
223
+ Defaults to :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`.
224
+ - `tz_aware`: If ``True``, MongoDB Extended JSON's *Strict mode* type
225
+ `Date` will be decoded to timezone aware instances of
226
+ :class:`datetime.datetime`. Otherwise they will be naive. Defaults
227
+ to ``False``.
228
+ - `tzinfo`: A :class:`datetime.tzinfo` subclass that specifies the
229
+ timezone from which :class:`~datetime.datetime` objects should be
230
+ decoded. Defaults to :const:`~bson.tz_util.utc`.
231
+ - `args`: arguments to :class:`~bson.codec_options.CodecOptions`
232
+ - `kwargs`: arguments to :class:`~bson.codec_options.CodecOptions`
233
+
234
+ .. seealso:: The specification for Relaxed and Canonical `Extended JSON`_.
235
+
236
+ .. versionchanged:: 4.0
237
+ The default for `json_mode` was changed from :const:`JSONMode.LEGACY`
238
+ to :const:`JSONMode.RELAXED`.
239
+ The default for `uuid_representation` was changed from
240
+ :const:`~bson.binary.UuidRepresentation.PYTHON_LEGACY` to
241
+ :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`.
242
+
243
+ .. versionchanged:: 3.5
244
+ Accepts the optional parameter `json_mode`.
245
+
246
+ .. versionchanged:: 4.0
247
+ Changed default value of `tz_aware` to False.
248
+ """
249
+
250
+ json_mode: int
251
+ strict_number_long: bool
252
+ datetime_representation: int
253
+ strict_uuid: bool
254
+
255
+ def __new__(
256
+ cls: Type["JSONOptions"],
257
+ strict_number_long: Optional[bool] = None,
258
+ datetime_representation: Optional[int] = None,
259
+ strict_uuid: Optional[bool] = None,
260
+ json_mode: int = JSONMode.RELAXED,
261
+ *args: Any,
262
+ **kwargs: Any
263
+ ) -> "JSONOptions":
264
+ kwargs["tz_aware"] = kwargs.get("tz_aware", False)
265
+ if kwargs["tz_aware"]:
266
+ kwargs["tzinfo"] = kwargs.get("tzinfo", utc)
267
+ if datetime_representation not in (
268
+ DatetimeRepresentation.LEGACY,
269
+ DatetimeRepresentation.NUMBERLONG,
270
+ DatetimeRepresentation.ISO8601,
271
+ None,
272
+ ):
273
+ raise ValueError(
274
+ "JSONOptions.datetime_representation must be one of LEGACY, "
275
+ "NUMBERLONG, or ISO8601 from DatetimeRepresentation."
276
+ )
277
+ self = cast(JSONOptions, super(JSONOptions, cls).__new__(cls, *args, **kwargs))
278
+ if json_mode not in (JSONMode.LEGACY, JSONMode.RELAXED, JSONMode.CANONICAL):
279
+ raise ValueError(
280
+ "JSONOptions.json_mode must be one of LEGACY, RELAXED, "
281
+ "or CANONICAL from JSONMode."
282
+ )
283
+ self.json_mode = json_mode
284
+ if self.json_mode == JSONMode.RELAXED:
285
+ if strict_number_long:
286
+ raise ValueError("Cannot specify strict_number_long=True with JSONMode.RELAXED")
287
+ if datetime_representation not in (None, DatetimeRepresentation.ISO8601):
288
+ raise ValueError(
289
+ "datetime_representation must be DatetimeRepresentation."
290
+ "ISO8601 or omitted with JSONMode.RELAXED"
291
+ )
292
+ if strict_uuid not in (None, True):
293
+ raise ValueError("Cannot specify strict_uuid=False with JSONMode.RELAXED")
294
+ self.strict_number_long = False
295
+ self.datetime_representation = DatetimeRepresentation.ISO8601
296
+ self.strict_uuid = True
297
+ elif self.json_mode == JSONMode.CANONICAL:
298
+ if strict_number_long not in (None, True):
299
+ raise ValueError("Cannot specify strict_number_long=False with JSONMode.RELAXED")
300
+ if datetime_representation not in (None, DatetimeRepresentation.NUMBERLONG):
301
+ raise ValueError(
302
+ "datetime_representation must be DatetimeRepresentation."
303
+ "NUMBERLONG or omitted with JSONMode.RELAXED"
304
+ )
305
+ if strict_uuid not in (None, True):
306
+ raise ValueError("Cannot specify strict_uuid=False with JSONMode.RELAXED")
307
+ self.strict_number_long = True
308
+ self.datetime_representation = DatetimeRepresentation.NUMBERLONG
309
+ self.strict_uuid = True
310
+ else: # JSONMode.LEGACY
311
+ self.strict_number_long = False
312
+ self.datetime_representation = DatetimeRepresentation.LEGACY
313
+ self.strict_uuid = False
314
+ if strict_number_long is not None:
315
+ self.strict_number_long = strict_number_long
316
+ if datetime_representation is not None:
317
+ self.datetime_representation = datetime_representation
318
+ if strict_uuid is not None:
319
+ self.strict_uuid = strict_uuid
320
+ return self
321
+
322
+ def _arguments_repr(self) -> str:
323
+ return (
324
+ "strict_number_long=%r, "
325
+ "datetime_representation=%r, "
326
+ "strict_uuid=%r, json_mode=%r, %s"
327
+ % (
328
+ self.strict_number_long,
329
+ self.datetime_representation,
330
+ self.strict_uuid,
331
+ self.json_mode,
332
+ super(JSONOptions, self)._arguments_repr(),
333
+ )
334
+ )
335
+
336
+ def _options_dict(self) -> Dict[Any, Any]:
337
+ # TODO: PYTHON-2442 use _asdict() instead
338
+ options_dict = super(JSONOptions, self)._options_dict()
339
+ options_dict.update(
340
+ {
341
+ "strict_number_long": self.strict_number_long,
342
+ "datetime_representation": self.datetime_representation,
343
+ "strict_uuid": self.strict_uuid,
344
+ "json_mode": self.json_mode,
345
+ }
346
+ )
347
+ return options_dict
348
+
349
+ def with_options(self, **kwargs: Any) -> "JSONOptions":
350
+ """
351
+ Make a copy of this JSONOptions, overriding some options::
352
+
353
+ >>> from bson.json_util import CANONICAL_JSON_OPTIONS
354
+ >>> CANONICAL_JSON_OPTIONS.tz_aware
355
+ True
356
+ >>> json_options = CANONICAL_JSON_OPTIONS.with_options(tz_aware=False, tzinfo=None)
357
+ >>> json_options.tz_aware
358
+ False
359
+
360
+ .. versionadded:: 3.12
361
+ """
362
+ opts = self._options_dict()
363
+ for opt in ("strict_number_long", "datetime_representation", "strict_uuid", "json_mode"):
364
+ opts[opt] = kwargs.get(opt, getattr(self, opt))
365
+ opts.update(kwargs)
366
+ return JSONOptions(**opts)
367
+
368
+
369
+ LEGACY_JSON_OPTIONS: JSONOptions = JSONOptions(json_mode=JSONMode.LEGACY)
370
+ """:class:`JSONOptions` for encoding to PyMongo's legacy JSON format.
371
+
372
+ .. seealso:: The documentation for :const:`bson.json_util.JSONMode.LEGACY`.
373
+
374
+ .. versionadded:: 3.5
375
+ """
376
+
377
+ CANONICAL_JSON_OPTIONS: JSONOptions = JSONOptions(json_mode=JSONMode.CANONICAL)
378
+ """:class:`JSONOptions` for Canonical Extended JSON.
379
+
380
+ .. seealso:: The documentation for :const:`bson.json_util.JSONMode.CANONICAL`.
381
+
382
+ .. versionadded:: 3.5
383
+ """
384
+
385
+ RELAXED_JSON_OPTIONS: JSONOptions = JSONOptions(json_mode=JSONMode.RELAXED)
386
+ """:class:`JSONOptions` for Relaxed Extended JSON.
387
+
388
+ .. seealso:: The documentation for :const:`bson.json_util.JSONMode.RELAXED`.
389
+
390
+ .. versionadded:: 3.5
391
+ """
392
+
393
+ DEFAULT_JSON_OPTIONS: JSONOptions = RELAXED_JSON_OPTIONS
394
+ """The default :class:`JSONOptions` for JSON encoding/decoding.
395
+
396
+ The same as :const:`RELAXED_JSON_OPTIONS`.
397
+
398
+ .. versionchanged:: 4.0
399
+ Changed from :const:`LEGACY_JSON_OPTIONS` to
400
+ :const:`RELAXED_JSON_OPTIONS`.
401
+
402
+ .. versionadded:: 3.4
403
+ """
404
+
405
+
406
+ def dumps(obj: Any, *args: Any, **kwargs: Any) -> str:
407
+ """Helper function that wraps :func:`json.dumps`.
408
+
409
+ Recursive function that handles all BSON types including
410
+ :class:`~bson.binary.Binary` and :class:`~bson.code.Code`.
411
+
412
+ :Parameters:
413
+ - `json_options`: A :class:`JSONOptions` instance used to modify the
414
+ encoding of MongoDB Extended JSON types. Defaults to
415
+ :const:`DEFAULT_JSON_OPTIONS`.
416
+
417
+ .. versionchanged:: 4.0
418
+ Now outputs MongoDB Relaxed Extended JSON by default (using
419
+ :const:`DEFAULT_JSON_OPTIONS`).
420
+
421
+ .. versionchanged:: 3.4
422
+ Accepts optional parameter `json_options`. See :class:`JSONOptions`.
423
+ """
424
+ json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS)
425
+ return json.dumps(_json_convert(obj, json_options), *args, **kwargs)
426
+
427
+
428
+ def loads(s: str, *args: Any, **kwargs: Any) -> Any:
429
+ """Helper function that wraps :func:`json.loads`.
430
+
431
+ Automatically passes the object_hook for BSON type conversion.
432
+
433
+ Raises ``TypeError``, ``ValueError``, ``KeyError``, or
434
+ :exc:`~bson.errors.InvalidId` on invalid MongoDB Extended JSON.
435
+
436
+ :Parameters:
437
+ - `json_options`: A :class:`JSONOptions` instance used to modify the
438
+ decoding of MongoDB Extended JSON types. Defaults to
439
+ :const:`DEFAULT_JSON_OPTIONS`.
440
+
441
+ .. versionchanged:: 4.0
442
+ Now loads :class:`datetime.datetime` instances as naive by default. To
443
+ load timezone aware instances utilize the `json_options` parameter.
444
+ See :ref:`tz_aware_default_change` for an example.
445
+
446
+ .. versionchanged:: 3.5
447
+ Parses Relaxed and Canonical Extended JSON as well as PyMongo's legacy
448
+ format. Now raises ``TypeError`` or ``ValueError`` when parsing JSON
449
+ type wrappers with values of the wrong type or any extra keys.
450
+
451
+ .. versionchanged:: 3.4
452
+ Accepts optional parameter `json_options`. See :class:`JSONOptions`.
453
+ """
454
+ json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS)
455
+ kwargs["object_pairs_hook"] = lambda pairs: object_pairs_hook(pairs, json_options)
456
+ return json.loads(s, *args, **kwargs)
457
+
458
+
459
+ def _json_convert(obj: Any, json_options: JSONOptions = DEFAULT_JSON_OPTIONS) -> Any:
460
+ """Recursive helper method that converts BSON types so they can be
461
+ converted into json.
462
+ """
463
+ if hasattr(obj, "items"):
464
+ return SON(((k, _json_convert(v, json_options)) for k, v in obj.items()))
465
+ elif hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes)):
466
+ return list((_json_convert(v, json_options) for v in obj))
467
+ try:
468
+ return default(obj, json_options)
469
+ except TypeError:
470
+ return obj
471
+
472
+
473
+ def object_pairs_hook(
474
+ pairs: Sequence[Tuple[str, Any]], json_options: JSONOptions = DEFAULT_JSON_OPTIONS
475
+ ) -> Any:
476
+ return object_hook(json_options.document_class(pairs), json_options)
477
+
478
+
479
+ def object_hook(dct: Mapping[str, Any], json_options: JSONOptions = DEFAULT_JSON_OPTIONS) -> Any:
480
+ if "$oid" in dct:
481
+ return _parse_canonical_oid(dct)
482
+ if (
483
+ isinstance(dct.get("$ref"), str)
484
+ and "$id" in dct
485
+ and isinstance(dct.get("$db"), (str, type(None)))
486
+ ):
487
+ return _parse_canonical_dbref(dct)
488
+ if "$date" in dct:
489
+ return _parse_canonical_datetime(dct, json_options)
490
+ if "$regex" in dct:
491
+ return _parse_legacy_regex(dct)
492
+ if "$minKey" in dct:
493
+ return _parse_canonical_minkey(dct)
494
+ if "$maxKey" in dct:
495
+ return _parse_canonical_maxkey(dct)
496
+ if "$binary" in dct:
497
+ if "$type" in dct:
498
+ return _parse_legacy_binary(dct, json_options)
499
+ else:
500
+ return _parse_canonical_binary(dct, json_options)
501
+ if "$code" in dct:
502
+ return _parse_canonical_code(dct)
503
+ if "$uuid" in dct:
504
+ return _parse_legacy_uuid(dct, json_options)
505
+ if "$undefined" in dct:
506
+ return None
507
+ if "$numberLong" in dct:
508
+ return _parse_canonical_int64(dct)
509
+ if "$timestamp" in dct:
510
+ tsp = dct["$timestamp"]
511
+ return Timestamp(tsp["t"], tsp["i"])
512
+ if "$numberDecimal" in dct:
513
+ return _parse_canonical_decimal128(dct)
514
+ if "$dbPointer" in dct:
515
+ return _parse_canonical_dbpointer(dct)
516
+ if "$regularExpression" in dct:
517
+ return _parse_canonical_regex(dct)
518
+ if "$symbol" in dct:
519
+ return _parse_canonical_symbol(dct)
520
+ if "$numberInt" in dct:
521
+ return _parse_canonical_int32(dct)
522
+ if "$numberDouble" in dct:
523
+ return _parse_canonical_double(dct)
524
+ return dct
525
+
526
+
527
+ def _parse_legacy_regex(doc: Any) -> Any:
528
+ pattern = doc["$regex"]
529
+ # Check if this is the $regex query operator.
530
+ if not isinstance(pattern, (str, bytes)):
531
+ return doc
532
+ flags = 0
533
+ # PyMongo always adds $options but some other tools may not.
534
+ for opt in doc.get("$options", ""):
535
+ flags |= _RE_OPT_TABLE.get(opt, 0)
536
+ return Regex(pattern, flags)
537
+
538
+
539
+ def _parse_legacy_uuid(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]:
540
+ """Decode a JSON legacy $uuid to Python UUID."""
541
+ if len(doc) != 1:
542
+ raise TypeError("Bad $uuid, extra field(s): %s" % (doc,))
543
+ if not isinstance(doc["$uuid"], str):
544
+ raise TypeError("$uuid must be a string: %s" % (doc,))
545
+ if json_options.uuid_representation == UuidRepresentation.UNSPECIFIED:
546
+ return Binary.from_uuid(uuid.UUID(doc["$uuid"]))
547
+ else:
548
+ return uuid.UUID(doc["$uuid"])
549
+
550
+
551
+ def _binary_or_uuid(data: Any, subtype: int, json_options: JSONOptions) -> Union[Binary, uuid.UUID]:
552
+ # special handling for UUID
553
+ if subtype in ALL_UUID_SUBTYPES:
554
+ uuid_representation = json_options.uuid_representation
555
+ binary_value = Binary(data, subtype)
556
+ if uuid_representation == UuidRepresentation.UNSPECIFIED:
557
+ return binary_value
558
+ if subtype == UUID_SUBTYPE:
559
+ # Legacy behavior: use STANDARD with binary subtype 4.
560
+ uuid_representation = UuidRepresentation.STANDARD
561
+ elif uuid_representation == UuidRepresentation.STANDARD:
562
+ # subtype == OLD_UUID_SUBTYPE
563
+ # Legacy behavior: STANDARD is the same as PYTHON_LEGACY.
564
+ uuid_representation = UuidRepresentation.PYTHON_LEGACY
565
+ return binary_value.as_uuid(uuid_representation)
566
+
567
+ if subtype == 0:
568
+ return cast(uuid.UUID, data)
569
+ return Binary(data, subtype)
570
+
571
+
572
+ def _parse_legacy_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]:
573
+ if isinstance(doc["$type"], int):
574
+ doc["$type"] = "%02x" % doc["$type"]
575
+ subtype = int(doc["$type"], 16)
576
+ if subtype >= 0xFFFFFF80: # Handle mongoexport values
577
+ subtype = int(doc["$type"][6:], 16)
578
+ data = base64.b64decode(doc["$binary"].encode())
579
+ return _binary_or_uuid(data, subtype, json_options)
580
+
581
+
582
+ def _parse_canonical_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]:
583
+ binary = doc["$binary"]
584
+ b64 = binary["base64"]
585
+ subtype = binary["subType"]
586
+ if not isinstance(b64, str):
587
+ raise TypeError("$binary base64 must be a string: %s" % (doc,))
588
+ if not isinstance(subtype, str) or len(subtype) > 2:
589
+ raise TypeError("$binary subType must be a string at most 2 characters: %s" % (doc,))
590
+ if len(binary) != 2:
591
+ raise TypeError('$binary must include only "base64" and "subType" components: %s' % (doc,))
592
+
593
+ data = base64.b64decode(b64.encode())
594
+ return _binary_or_uuid(data, int(subtype, 16), json_options)
595
+
596
+
597
+ def _parse_canonical_datetime(doc: Any, json_options: JSONOptions) -> datetime.datetime:
598
+ """Decode a JSON datetime to python datetime.datetime."""
599
+ dtm = doc["$date"]
600
+ if len(doc) != 1:
601
+ raise TypeError("Bad $date, extra field(s): %s" % (doc,))
602
+ # mongoexport 2.6 and newer
603
+ if isinstance(dtm, str):
604
+ # Parse offset
605
+ if dtm[-1] == "Z":
606
+ dt = dtm[:-1]
607
+ offset = "Z"
608
+ elif dtm[-6] in ("+", "-") and dtm[-3] == ":":
609
+ # (+|-)HH:MM
610
+ dt = dtm[:-6]
611
+ offset = dtm[-6:]
612
+ elif dtm[-5] in ("+", "-"):
613
+ # (+|-)HHMM
614
+ dt = dtm[:-5]
615
+ offset = dtm[-5:]
616
+ elif dtm[-3] in ("+", "-"):
617
+ # (+|-)HH
618
+ dt = dtm[:-3]
619
+ offset = dtm[-3:]
620
+ else:
621
+ dt = dtm
622
+ offset = ""
623
+
624
+ # Parse the optional factional seconds portion.
625
+ dot_index = dt.rfind(".")
626
+ microsecond = 0
627
+ if dot_index != -1:
628
+ microsecond = int(float(dt[dot_index:]) * 1000000)
629
+ dt = dt[:dot_index]
630
+
631
+ aware = datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S").replace(
632
+ microsecond=microsecond, tzinfo=utc
633
+ )
634
+
635
+ if offset and offset != "Z":
636
+ if len(offset) == 6:
637
+ hours, minutes = offset[1:].split(":")
638
+ secs = int(hours) * 3600 + int(minutes) * 60
639
+ elif len(offset) == 5:
640
+ secs = int(offset[1:3]) * 3600 + int(offset[3:]) * 60
641
+ elif len(offset) == 3:
642
+ secs = int(offset[1:3]) * 3600
643
+ if offset[0] == "-":
644
+ secs *= -1
645
+ aware = aware - datetime.timedelta(seconds=secs)
646
+
647
+ if json_options.tz_aware:
648
+ if json_options.tzinfo:
649
+ aware = aware.astimezone(json_options.tzinfo)
650
+ return aware
651
+ else:
652
+ return aware.replace(tzinfo=None)
653
+ return bson._millis_to_datetime(int(dtm), json_options)
654
+
655
+
656
+ def _parse_canonical_oid(doc: Any) -> ObjectId:
657
+ """Decode a JSON ObjectId to bson.objectid.ObjectId."""
658
+ if len(doc) != 1:
659
+ raise TypeError("Bad $oid, extra field(s): %s" % (doc,))
660
+ return ObjectId(doc["$oid"])
661
+
662
+
663
+ def _parse_canonical_symbol(doc: Any) -> str:
664
+ """Decode a JSON symbol to Python string."""
665
+ symbol = doc["$symbol"]
666
+ if len(doc) != 1:
667
+ raise TypeError("Bad $symbol, extra field(s): %s" % (doc,))
668
+ return str(symbol)
669
+
670
+
671
+ def _parse_canonical_code(doc: Any) -> Code:
672
+ """Decode a JSON code to bson.code.Code."""
673
+ for key in doc:
674
+ if key not in ("$code", "$scope"):
675
+ raise TypeError("Bad $code, extra field(s): %s" % (doc,))
676
+ return Code(doc["$code"], scope=doc.get("$scope"))
677
+
678
+
679
+ def _parse_canonical_regex(doc: Any) -> Regex:
680
+ """Decode a JSON regex to bson.regex.Regex."""
681
+ regex = doc["$regularExpression"]
682
+ if len(doc) != 1:
683
+ raise TypeError("Bad $regularExpression, extra field(s): %s" % (doc,))
684
+ if len(regex) != 2:
685
+ raise TypeError(
686
+ 'Bad $regularExpression must include only "pattern"'
687
+ 'and "options" components: %s' % (doc,)
688
+ )
689
+ opts = regex["options"]
690
+ if not isinstance(opts, str):
691
+ raise TypeError(
692
+ "Bad $regularExpression options, options must be string, was type %s" % (type(opts))
693
+ )
694
+ return Regex(regex["pattern"], opts)
695
+
696
+
697
+ def _parse_canonical_dbref(doc: Any) -> DBRef:
698
+ """Decode a JSON DBRef to bson.dbref.DBRef."""
699
+ return DBRef(doc.pop("$ref"), doc.pop("$id"), database=doc.pop("$db", None), **doc)
700
+
701
+
702
+ def _parse_canonical_dbpointer(doc: Any) -> Any:
703
+ """Decode a JSON (deprecated) DBPointer to bson.dbref.DBRef."""
704
+ dbref = doc["$dbPointer"]
705
+ if len(doc) != 1:
706
+ raise TypeError("Bad $dbPointer, extra field(s): %s" % (doc,))
707
+ if isinstance(dbref, DBRef):
708
+ dbref_doc = dbref.as_doc()
709
+ # DBPointer must not contain $db in its value.
710
+ if dbref.database is not None:
711
+ raise TypeError("Bad $dbPointer, extra field $db: %s" % (dbref_doc,))
712
+ if not isinstance(dbref.id, ObjectId):
713
+ raise TypeError("Bad $dbPointer, $id must be an ObjectId: %s" % (dbref_doc,))
714
+ if len(dbref_doc) != 2:
715
+ raise TypeError("Bad $dbPointer, extra field(s) in DBRef: %s" % (dbref_doc,))
716
+ return dbref
717
+ else:
718
+ raise TypeError("Bad $dbPointer, expected a DBRef: %s" % (doc,))
719
+
720
+
721
+ def _parse_canonical_int32(doc: Any) -> int:
722
+ """Decode a JSON int32 to python int."""
723
+ i_str = doc["$numberInt"]
724
+ if len(doc) != 1:
725
+ raise TypeError("Bad $numberInt, extra field(s): %s" % (doc,))
726
+ if not isinstance(i_str, str):
727
+ raise TypeError("$numberInt must be string: %s" % (doc,))
728
+ return int(i_str)
729
+
730
+
731
+ def _parse_canonical_int64(doc: Any) -> Int64:
732
+ """Decode a JSON int64 to bson.int64.Int64."""
733
+ l_str = doc["$numberLong"]
734
+ if len(doc) != 1:
735
+ raise TypeError("Bad $numberLong, extra field(s): %s" % (doc,))
736
+ return Int64(l_str)
737
+
738
+
739
+ def _parse_canonical_double(doc: Any) -> float:
740
+ """Decode a JSON double to python float."""
741
+ d_str = doc["$numberDouble"]
742
+ if len(doc) != 1:
743
+ raise TypeError("Bad $numberDouble, extra field(s): %s" % (doc,))
744
+ if not isinstance(d_str, str):
745
+ raise TypeError("$numberDouble must be string: %s" % (doc,))
746
+ return float(d_str)
747
+
748
+
749
+ def _parse_canonical_decimal128(doc: Any) -> Decimal128:
750
+ """Decode a JSON decimal128 to bson.decimal128.Decimal128."""
751
+ d_str = doc["$numberDecimal"]
752
+ if len(doc) != 1:
753
+ raise TypeError("Bad $numberDecimal, extra field(s): %s" % (doc,))
754
+ if not isinstance(d_str, str):
755
+ raise TypeError("$numberDecimal must be string: %s" % (doc,))
756
+ return Decimal128(d_str)
757
+
758
+
759
+ def _parse_canonical_minkey(doc: Any) -> MinKey:
760
+ """Decode a JSON MinKey to bson.min_key.MinKey."""
761
+ if type(doc["$minKey"]) is not int or doc["$minKey"] != 1:
762
+ raise TypeError("$minKey value must be 1: %s" % (doc,))
763
+ if len(doc) != 1:
764
+ raise TypeError("Bad $minKey, extra field(s): %s" % (doc,))
765
+ return MinKey()
766
+
767
+
768
+ def _parse_canonical_maxkey(doc: Any) -> MaxKey:
769
+ """Decode a JSON MaxKey to bson.max_key.MaxKey."""
770
+ if type(doc["$maxKey"]) is not int or doc["$maxKey"] != 1:
771
+ raise TypeError("$maxKey value must be 1: %s", (doc,))
772
+ if len(doc) != 1:
773
+ raise TypeError("Bad $minKey, extra field(s): %s" % (doc,))
774
+ return MaxKey()
775
+
776
+
777
+ def _encode_binary(data: bytes, subtype: int, json_options: JSONOptions) -> Any:
778
+ if json_options.json_mode == JSONMode.LEGACY:
779
+ return SON([("$binary", base64.b64encode(data).decode()), ("$type", "%02x" % subtype)])
780
+ return {
781
+ "$binary": SON([("base64", base64.b64encode(data).decode()), ("subType", "%02x" % subtype)])
782
+ }
783
+
784
+
785
+ def default(obj: Any, json_options: JSONOptions = DEFAULT_JSON_OPTIONS) -> Any:
786
+ # We preserve key order when rendering SON, DBRef, etc. as JSON by
787
+ # returning a SON for those types instead of a dict.
788
+ if isinstance(obj, ObjectId):
789
+ return {"$oid": str(obj)}
790
+ if isinstance(obj, DBRef):
791
+ return _json_convert(obj.as_doc(), json_options=json_options)
792
+ if isinstance(obj, datetime.datetime):
793
+ if json_options.datetime_representation == DatetimeRepresentation.ISO8601:
794
+ if not obj.tzinfo:
795
+ obj = obj.replace(tzinfo=utc)
796
+ assert obj.tzinfo is not None
797
+ if obj >= EPOCH_AWARE:
798
+ off = obj.tzinfo.utcoffset(obj)
799
+ if (off.days, off.seconds, off.microseconds) == (0, 0, 0): # type: ignore
800
+ tz_string = "Z"
801
+ else:
802
+ tz_string = obj.strftime("%z")
803
+ millis = int(obj.microsecond / 1000)
804
+ fracsecs = ".%03d" % (millis,) if millis else ""
805
+ return {
806
+ "$date": "%s%s%s" % (obj.strftime("%Y-%m-%dT%H:%M:%S"), fracsecs, tz_string)
807
+ }
808
+
809
+ millis = bson._datetime_to_millis(obj)
810
+ if json_options.datetime_representation == DatetimeRepresentation.LEGACY:
811
+ return {"$date": millis}
812
+ return {"$date": {"$numberLong": str(millis)}}
813
+ if json_options.strict_number_long and isinstance(obj, Int64):
814
+ return {"$numberLong": str(obj)}
815
+ if isinstance(obj, (RE_TYPE, Regex)):
816
+ flags = ""
817
+ if obj.flags & re.IGNORECASE:
818
+ flags += "i"
819
+ if obj.flags & re.LOCALE:
820
+ flags += "l"
821
+ if obj.flags & re.MULTILINE:
822
+ flags += "m"
823
+ if obj.flags & re.DOTALL:
824
+ flags += "s"
825
+ if obj.flags & re.UNICODE:
826
+ flags += "u"
827
+ if obj.flags & re.VERBOSE:
828
+ flags += "x"
829
+ if isinstance(obj.pattern, str):
830
+ pattern = obj.pattern
831
+ else:
832
+ pattern = obj.pattern.decode("utf-8")
833
+ if json_options.json_mode == JSONMode.LEGACY:
834
+ return SON([("$regex", pattern), ("$options", flags)])
835
+ return {"$regularExpression": SON([("pattern", pattern), ("options", flags)])}
836
+ if isinstance(obj, MinKey):
837
+ return {"$minKey": 1}
838
+ if isinstance(obj, MaxKey):
839
+ return {"$maxKey": 1}
840
+ if isinstance(obj, Timestamp):
841
+ return {"$timestamp": SON([("t", obj.time), ("i", obj.inc)])}
842
+ if isinstance(obj, Code):
843
+ if obj.scope is None:
844
+ return {"$code": str(obj)}
845
+ return SON([("$code", str(obj)), ("$scope", _json_convert(obj.scope, json_options))])
846
+ if isinstance(obj, Binary):
847
+ return _encode_binary(obj, obj.subtype, json_options)
848
+ if isinstance(obj, bytes):
849
+ return _encode_binary(obj, 0, json_options)
850
+ if isinstance(obj, uuid.UUID):
851
+ if json_options.strict_uuid:
852
+ binval = Binary.from_uuid(obj, uuid_representation=json_options.uuid_representation)
853
+ return _encode_binary(binval, binval.subtype, json_options)
854
+ else:
855
+ return {"$uuid": obj.hex}
856
+ if isinstance(obj, Decimal128):
857
+ return {"$numberDecimal": str(obj)}
858
+ if isinstance(obj, bool):
859
+ return obj
860
+ if json_options.json_mode == JSONMode.CANONICAL and isinstance(obj, int):
861
+ if -(2**31) <= obj < 2**31:
862
+ return {"$numberInt": str(obj)}
863
+ return {"$numberLong": str(obj)}
864
+ if json_options.json_mode != JSONMode.LEGACY and isinstance(obj, float):
865
+ if math.isnan(obj):
866
+ return {"$numberDouble": "NaN"}
867
+ elif math.isinf(obj):
868
+ representation = "Infinity" if obj > 0 else "-Infinity"
869
+ return {"$numberDouble": representation}
870
+ elif json_options.json_mode == JSONMode.CANONICAL:
871
+ # repr() will return the shortest string guaranteed to produce the
872
+ # original value, when float() is called on it.
873
+ return {"$numberDouble": str(repr(obj))}
874
+ raise TypeError("%r is not JSON serializable" % obj)