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,1347 @@
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
+ """BSON (Binary JSON) encoding and decoding.
16
+
17
+ The mapping from Python types to BSON types is as follows:
18
+
19
+ ======================================= ============= ===================
20
+ Python Type BSON Type Supported Direction
21
+ ======================================= ============= ===================
22
+ None null both
23
+ bool boolean both
24
+ int [#int]_ int32 / int64 py -> bson
25
+ `bson.int64.Int64` int64 both
26
+ float number (real) both
27
+ str string both
28
+ list array both
29
+ dict / `SON` object both
30
+ datetime.datetime [#dt]_ [#dt2]_ date both
31
+ `bson.regex.Regex` regex both
32
+ compiled re [#re]_ regex py -> bson
33
+ `bson.binary.Binary` binary both
34
+ `bson.objectid.ObjectId` oid both
35
+ `bson.dbref.DBRef` dbref both
36
+ None undefined bson -> py
37
+ `bson.code.Code` code both
38
+ str symbol bson -> py
39
+ bytes [#bytes]_ binary both
40
+ ======================================= ============= ===================
41
+
42
+ .. [#int] A Python int will be saved as a BSON int32 or BSON int64 depending
43
+ on its size. A BSON int32 will always decode to a Python int. A BSON
44
+ int64 will always decode to a :class:`~bson.int64.Int64`.
45
+ .. [#dt] datetime.datetime instances will be rounded to the nearest
46
+ millisecond when saved
47
+ .. [#dt2] all datetime.datetime instances are treated as *naive*. clients
48
+ should always use UTC.
49
+ .. [#re] :class:`~bson.regex.Regex` instances and regular expression
50
+ objects from ``re.compile()`` are both saved as BSON regular expressions.
51
+ BSON regular expressions are decoded as :class:`~bson.regex.Regex`
52
+ instances.
53
+ .. [#bytes] The bytes type is encoded as BSON binary with
54
+ subtype 0. It will be decoded back to bytes.
55
+ """
56
+
57
+ import calendar
58
+ import datetime
59
+ import itertools
60
+ import re
61
+ import struct
62
+ import sys
63
+ import uuid
64
+ from codecs import utf_8_decode as _utf_8_decode # type: ignore[attr-defined]
65
+ from codecs import utf_8_encode as _utf_8_encode # type: ignore[attr-defined]
66
+ from collections import abc as _abc
67
+ from typing import (
68
+ IO,
69
+ TYPE_CHECKING,
70
+ Any,
71
+ BinaryIO,
72
+ Callable,
73
+ Dict,
74
+ Generator,
75
+ Iterator,
76
+ List,
77
+ Mapping,
78
+ MutableMapping,
79
+ NoReturn,
80
+ Optional,
81
+ Sequence,
82
+ Tuple,
83
+ Type,
84
+ TypeVar,
85
+ Union,
86
+ cast,
87
+ )
88
+
89
+ from bson.binary import (
90
+ ALL_UUID_SUBTYPES,
91
+ CSHARP_LEGACY,
92
+ JAVA_LEGACY,
93
+ OLD_UUID_SUBTYPE,
94
+ STANDARD,
95
+ UUID_SUBTYPE,
96
+ Binary,
97
+ UuidRepresentation,
98
+ )
99
+ from bson.code import Code
100
+ from bson.codec_options import (
101
+ DEFAULT_CODEC_OPTIONS,
102
+ CodecOptions,
103
+ _DocumentType,
104
+ _raw_document_class,
105
+ )
106
+ from bson.dbref import DBRef
107
+ from bson.decimal128 import Decimal128
108
+ from bson.errors import InvalidBSON, InvalidDocument, InvalidStringData
109
+ from bson.int64 import Int64
110
+ from bson.max_key import MaxKey
111
+ from bson.min_key import MinKey
112
+ from bson.objectid import ObjectId
113
+ from bson.regex import Regex
114
+ from bson.son import RE_TYPE, SON
115
+ from bson.timestamp import Timestamp
116
+ from bson.tz_util import utc
117
+
118
+ # Import some modules for type-checking only.
119
+ if TYPE_CHECKING:
120
+ from array import array
121
+ from mmap import mmap
122
+
123
+
124
+ try:
125
+ from bson import _cbson # type: ignore[attr-defined]
126
+
127
+ _USE_C = True
128
+ except ImportError:
129
+ _USE_C = False
130
+
131
+ __all__ = [
132
+ "ALL_UUID_SUBTYPES",
133
+ "CSHARP_LEGACY",
134
+ "JAVA_LEGACY",
135
+ "OLD_UUID_SUBTYPE",
136
+ "STANDARD",
137
+ "UUID_SUBTYPE",
138
+ "Binary",
139
+ "UuidRepresentation",
140
+ "Code",
141
+ "DEFAULT_CODEC_OPTIONS",
142
+ "CodecOptions",
143
+ "DBRef",
144
+ "Decimal128",
145
+ "InvalidBSON",
146
+ "InvalidDocument",
147
+ "InvalidStringData",
148
+ "Int64",
149
+ "MaxKey",
150
+ "MinKey",
151
+ "ObjectId",
152
+ "Regex",
153
+ "RE_TYPE",
154
+ "SON",
155
+ "Timestamp",
156
+ "utc",
157
+ "EPOCH_AWARE",
158
+ "EPOCH_NAIVE",
159
+ "BSONNUM",
160
+ "BSONSTR",
161
+ "BSONOBJ",
162
+ "BSONARR",
163
+ "BSONBIN",
164
+ "BSONUND",
165
+ "BSONOID",
166
+ "BSONBOO",
167
+ "BSONDAT",
168
+ "BSONNUL",
169
+ "BSONRGX",
170
+ "BSONREF",
171
+ "BSONCOD",
172
+ "BSONSYM",
173
+ "BSONCWS",
174
+ "BSONINT",
175
+ "BSONTIM",
176
+ "BSONLON",
177
+ "BSONDEC",
178
+ "BSONMIN",
179
+ "BSONMAX",
180
+ "get_data_and_view",
181
+ "gen_list_name",
182
+ "encode",
183
+ "decode",
184
+ "decode_all",
185
+ "decode_iter",
186
+ "decode_file_iter",
187
+ "is_valid",
188
+ "BSON",
189
+ "has_c",
190
+ ]
191
+
192
+ EPOCH_AWARE = datetime.datetime.fromtimestamp(0, utc)
193
+ EPOCH_NAIVE = datetime.datetime.utcfromtimestamp(0)
194
+
195
+
196
+ BSONNUM = b"\x01" # Floating point
197
+ BSONSTR = b"\x02" # UTF-8 string
198
+ BSONOBJ = b"\x03" # Embedded document
199
+ BSONARR = b"\x04" # Array
200
+ BSONBIN = b"\x05" # Binary
201
+ BSONUND = b"\x06" # Undefined
202
+ BSONOID = b"\x07" # ObjectId
203
+ BSONBOO = b"\x08" # Boolean
204
+ BSONDAT = b"\x09" # UTC Datetime
205
+ BSONNUL = b"\x0A" # Null
206
+ BSONRGX = b"\x0B" # Regex
207
+ BSONREF = b"\x0C" # DBRef
208
+ BSONCOD = b"\x0D" # Javascript code
209
+ BSONSYM = b"\x0E" # Symbol
210
+ BSONCWS = b"\x0F" # Javascript code with scope
211
+ BSONINT = b"\x10" # 32bit int
212
+ BSONTIM = b"\x11" # Timestamp
213
+ BSONLON = b"\x12" # 64bit int
214
+ BSONDEC = b"\x13" # Decimal128
215
+ BSONMIN = b"\xFF" # Min key
216
+ BSONMAX = b"\x7F" # Max key
217
+
218
+
219
+ _UNPACK_FLOAT_FROM = struct.Struct("<d").unpack_from
220
+ _UNPACK_INT = struct.Struct("<i").unpack
221
+ _UNPACK_INT_FROM = struct.Struct("<i").unpack_from
222
+ _UNPACK_LENGTH_SUBTYPE_FROM = struct.Struct("<iB").unpack_from
223
+ _UNPACK_LONG_FROM = struct.Struct("<q").unpack_from
224
+ _UNPACK_TIMESTAMP_FROM = struct.Struct("<II").unpack_from
225
+
226
+
227
+ def get_data_and_view(data: Any) -> Tuple[Any, memoryview]:
228
+ if isinstance(data, (bytes, bytearray)):
229
+ return data, memoryview(data)
230
+ view = memoryview(data)
231
+ return view.tobytes(), view
232
+
233
+
234
+ def _raise_unknown_type(element_type: int, element_name: str) -> NoReturn:
235
+ """Unknown type helper."""
236
+ raise InvalidBSON(
237
+ "Detected unknown BSON type %r for fieldname '%s'. Are "
238
+ "you using the latest driver version?" % (chr(element_type).encode(), element_name)
239
+ )
240
+
241
+
242
+ def _get_int(
243
+ data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any
244
+ ) -> Tuple[int, int]:
245
+ """Decode a BSON int32 to python int."""
246
+ return _UNPACK_INT_FROM(data, position)[0], position + 4
247
+
248
+
249
+ def _get_c_string(data: Any, view: Any, position: int, opts: CodecOptions) -> Tuple[str, int]:
250
+ """Decode a BSON 'C' string to python str."""
251
+ end = data.index(b"\x00", position)
252
+ return _utf_8_decode(view[position:end], opts.unicode_decode_error_handler, True)[0], end + 1
253
+
254
+
255
+ def _get_float(
256
+ data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any
257
+ ) -> Tuple[float, int]:
258
+ """Decode a BSON double to python float."""
259
+ return _UNPACK_FLOAT_FROM(data, position)[0], position + 8
260
+
261
+
262
+ def _get_string(
263
+ data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, dummy: Any
264
+ ) -> Tuple[str, int]:
265
+ """Decode a BSON string to python str."""
266
+ length = _UNPACK_INT_FROM(data, position)[0]
267
+ position += 4
268
+ if length < 1 or obj_end - position < length:
269
+ raise InvalidBSON("invalid string length")
270
+ end = position + length - 1
271
+ if data[end] != 0:
272
+ raise InvalidBSON("invalid end of string")
273
+ return _utf_8_decode(view[position:end], opts.unicode_decode_error_handler, True)[0], end + 1
274
+
275
+
276
+ def _get_object_size(data: Any, position: int, obj_end: int) -> Tuple[int, int]:
277
+ """Validate and return a BSON document's size."""
278
+ try:
279
+ obj_size = _UNPACK_INT_FROM(data, position)[0]
280
+ except struct.error as exc:
281
+ raise InvalidBSON(str(exc))
282
+ end = position + obj_size - 1
283
+ if data[end] != 0:
284
+ raise InvalidBSON("bad eoo")
285
+ if end >= obj_end:
286
+ raise InvalidBSON("invalid object length")
287
+ # If this is the top-level document, validate the total size too.
288
+ if position == 0 and obj_size != obj_end:
289
+ raise InvalidBSON("invalid object length")
290
+ return obj_size, end
291
+
292
+
293
+ def _get_object(
294
+ data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, dummy: Any
295
+ ) -> Tuple[Any, int]:
296
+ """Decode a BSON subdocument to opts.document_class or bson.dbref.DBRef."""
297
+ obj_size, end = _get_object_size(data, position, obj_end)
298
+ if _raw_document_class(opts.document_class):
299
+ return (opts.document_class(data[position : end + 1], opts), position + obj_size)
300
+
301
+ obj = _elements_to_dict(data, view, position + 4, end, opts)
302
+
303
+ position += obj_size
304
+ # If DBRef validation fails, return a normal doc.
305
+ if (
306
+ isinstance(obj.get("$ref"), str)
307
+ and "$id" in obj
308
+ and isinstance(obj.get("$db"), (str, type(None)))
309
+ ):
310
+ return (DBRef(obj.pop("$ref"), obj.pop("$id", None), obj.pop("$db", None), obj), position)
311
+ return obj, position
312
+
313
+
314
+ def _get_array(
315
+ data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, element_name: str
316
+ ) -> Tuple[Any, int]:
317
+ """Decode a BSON array to python list."""
318
+ size = _UNPACK_INT_FROM(data, position)[0]
319
+ end = position + size - 1
320
+ if data[end] != 0:
321
+ raise InvalidBSON("bad eoo")
322
+
323
+ position += 4
324
+ end -= 1
325
+ result: List[Any] = []
326
+
327
+ # Avoid doing global and attribute lookups in the loop.
328
+ append = result.append
329
+ index = data.index
330
+ getter = _ELEMENT_GETTER
331
+ decoder_map = opts.type_registry._decoder_map
332
+
333
+ while position < end:
334
+ element_type = data[position]
335
+ # Just skip the keys.
336
+ position = index(b"\x00", position) + 1
337
+ try:
338
+ value, position = getter[element_type](
339
+ data, view, position, obj_end, opts, element_name
340
+ )
341
+ except KeyError:
342
+ _raise_unknown_type(element_type, element_name)
343
+
344
+ if decoder_map:
345
+ custom_decoder = decoder_map.get(type(value))
346
+ if custom_decoder is not None:
347
+ value = custom_decoder(value)
348
+
349
+ append(value)
350
+
351
+ if position != end + 1:
352
+ raise InvalidBSON("bad array length")
353
+ return result, position + 1
354
+
355
+
356
+ def _get_binary(
357
+ data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, dummy1: Any
358
+ ) -> Tuple[Union[Binary, uuid.UUID], int]:
359
+ """Decode a BSON binary to bson.binary.Binary or python UUID."""
360
+ length, subtype = _UNPACK_LENGTH_SUBTYPE_FROM(data, position)
361
+ position += 5
362
+ if subtype == 2:
363
+ length2 = _UNPACK_INT_FROM(data, position)[0]
364
+ position += 4
365
+ if length2 != length - 4:
366
+ raise InvalidBSON("invalid binary (st 2) - lengths don't match!")
367
+ length = length2
368
+ end = position + length
369
+ if length < 0 or end > obj_end:
370
+ raise InvalidBSON("bad binary object length")
371
+
372
+ # Convert UUID subtypes to native UUIDs.
373
+ if subtype in ALL_UUID_SUBTYPES:
374
+ uuid_rep = opts.uuid_representation
375
+ binary_value = Binary(data[position:end], subtype)
376
+ if (
377
+ (uuid_rep == UuidRepresentation.UNSPECIFIED)
378
+ or (subtype == UUID_SUBTYPE and uuid_rep != STANDARD)
379
+ or (subtype == OLD_UUID_SUBTYPE and uuid_rep == STANDARD)
380
+ ):
381
+ return binary_value, end
382
+ return binary_value.as_uuid(uuid_rep), end
383
+
384
+ # Decode subtype 0 to 'bytes'.
385
+ if subtype == 0:
386
+ value = data[position:end]
387
+ else:
388
+ value = Binary(data[position:end], subtype)
389
+
390
+ return value, end
391
+
392
+
393
+ def _get_oid(
394
+ data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any
395
+ ) -> Tuple[ObjectId, int]:
396
+ """Decode a BSON ObjectId to bson.objectid.ObjectId."""
397
+ end = position + 12
398
+ return ObjectId(data[position:end]), end
399
+
400
+
401
+ def _get_boolean(
402
+ data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any
403
+ ) -> Tuple[bool, int]:
404
+ """Decode a BSON true/false to python True/False."""
405
+ end = position + 1
406
+ boolean_byte = data[position:end]
407
+ if boolean_byte == b"\x00":
408
+ return False, end
409
+ elif boolean_byte == b"\x01":
410
+ return True, end
411
+ raise InvalidBSON("invalid boolean value: %r" % boolean_byte)
412
+
413
+
414
+ def _get_date(
415
+ data: Any, view: Any, position: int, dummy0: int, opts: CodecOptions, dummy1: Any
416
+ ) -> Tuple[datetime.datetime, int]:
417
+ """Decode a BSON datetime to python datetime.datetime."""
418
+ return _millis_to_datetime(_UNPACK_LONG_FROM(data, position)[0], opts), position + 8
419
+
420
+
421
+ def _get_code(
422
+ data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, element_name: str
423
+ ) -> Tuple[Code, int]:
424
+ """Decode a BSON code to bson.code.Code."""
425
+ code, position = _get_string(data, view, position, obj_end, opts, element_name)
426
+ return Code(code), position
427
+
428
+
429
+ def _get_code_w_scope(
430
+ data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, element_name: str
431
+ ) -> Tuple[Code, int]:
432
+ """Decode a BSON code_w_scope to bson.code.Code."""
433
+ code_end = position + _UNPACK_INT_FROM(data, position)[0]
434
+ code, position = _get_string(data, view, position + 4, code_end, opts, element_name)
435
+ scope, position = _get_object(data, view, position, code_end, opts, element_name)
436
+ if position != code_end:
437
+ raise InvalidBSON("scope outside of javascript code boundaries")
438
+ return Code(code, scope), position
439
+
440
+
441
+ def _get_regex(
442
+ data: Any, view: Any, position: int, dummy0: Any, opts: CodecOptions, dummy1: Any
443
+ ) -> Tuple[Regex, int]:
444
+ """Decode a BSON regex to bson.regex.Regex or a python pattern object."""
445
+ pattern, position = _get_c_string(data, view, position, opts)
446
+ bson_flags, position = _get_c_string(data, view, position, opts)
447
+ bson_re = Regex(pattern, bson_flags)
448
+ return bson_re, position
449
+
450
+
451
+ def _get_ref(
452
+ data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, element_name: str
453
+ ) -> Tuple[DBRef, int]:
454
+ """Decode (deprecated) BSON DBPointer to bson.dbref.DBRef."""
455
+ collection, position = _get_string(data, view, position, obj_end, opts, element_name)
456
+ oid, position = _get_oid(data, view, position, obj_end, opts, element_name)
457
+ return DBRef(collection, oid), position
458
+
459
+
460
+ def _get_timestamp(
461
+ data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any
462
+ ) -> Tuple[Timestamp, int]:
463
+ """Decode a BSON timestamp to bson.timestamp.Timestamp."""
464
+ inc, timestamp = _UNPACK_TIMESTAMP_FROM(data, position)
465
+ return Timestamp(timestamp, inc), position + 8
466
+
467
+
468
+ def _get_int64(
469
+ data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any
470
+ ) -> Tuple[Int64, int]:
471
+ """Decode a BSON int64 to bson.int64.Int64."""
472
+ return Int64(_UNPACK_LONG_FROM(data, position)[0]), position + 8
473
+
474
+
475
+ def _get_decimal128(
476
+ data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any
477
+ ) -> Tuple[Decimal128, int]:
478
+ """Decode a BSON decimal128 to bson.decimal128.Decimal128."""
479
+ end = position + 16
480
+ return Decimal128.from_bid(data[position:end]), end
481
+
482
+
483
+ # Each decoder function's signature is:
484
+ # - data: bytes
485
+ # - view: memoryview that references `data`
486
+ # - position: int, beginning of object in 'data' to decode
487
+ # - obj_end: int, end of object to decode in 'data' if variable-length type
488
+ # - opts: a CodecOptions
489
+ _ELEMENT_GETTER: Dict[int, Callable[..., Tuple[Any, int]]] = {
490
+ ord(BSONNUM): _get_float,
491
+ ord(BSONSTR): _get_string,
492
+ ord(BSONOBJ): _get_object,
493
+ ord(BSONARR): _get_array,
494
+ ord(BSONBIN): _get_binary,
495
+ ord(BSONUND): lambda u, v, w, x, y, z: (None, w), # Deprecated undefined
496
+ ord(BSONOID): _get_oid,
497
+ ord(BSONBOO): _get_boolean,
498
+ ord(BSONDAT): _get_date,
499
+ ord(BSONNUL): lambda u, v, w, x, y, z: (None, w),
500
+ ord(BSONRGX): _get_regex,
501
+ ord(BSONREF): _get_ref, # Deprecated DBPointer
502
+ ord(BSONCOD): _get_code,
503
+ ord(BSONSYM): _get_string, # Deprecated symbol
504
+ ord(BSONCWS): _get_code_w_scope,
505
+ ord(BSONINT): _get_int,
506
+ ord(BSONTIM): _get_timestamp,
507
+ ord(BSONLON): _get_int64,
508
+ ord(BSONDEC): _get_decimal128,
509
+ ord(BSONMIN): lambda u, v, w, x, y, z: (MinKey(), w),
510
+ ord(BSONMAX): lambda u, v, w, x, y, z: (MaxKey(), w),
511
+ }
512
+
513
+
514
+ if _USE_C:
515
+
516
+ def _element_to_dict(
517
+ data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions
518
+ ) -> Any:
519
+ return _cbson._element_to_dict(data, position, obj_end, opts)
520
+
521
+ else:
522
+
523
+ def _element_to_dict(
524
+ data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions
525
+ ) -> Any:
526
+ """Decode a single key, value pair."""
527
+ element_type = data[position]
528
+ position += 1
529
+ element_name, position = _get_c_string(data, view, position, opts)
530
+ try:
531
+ value, position = _ELEMENT_GETTER[element_type](
532
+ data, view, position, obj_end, opts, element_name
533
+ )
534
+ except KeyError:
535
+ _raise_unknown_type(element_type, element_name)
536
+
537
+ if opts.type_registry._decoder_map:
538
+ custom_decoder = opts.type_registry._decoder_map.get(type(value))
539
+ if custom_decoder is not None:
540
+ value = custom_decoder(value)
541
+
542
+ return element_name, value, position
543
+
544
+
545
+ _T = TypeVar("_T", bound=MutableMapping[Any, Any])
546
+
547
+
548
+ def _raw_to_dict(data: Any, position: int, obj_end: int, opts: CodecOptions, result: _T) -> _T:
549
+ data, view = get_data_and_view(data)
550
+ return _elements_to_dict(data, view, position, obj_end, opts, result)
551
+
552
+
553
+ def _elements_to_dict(
554
+ data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, result: Any = None
555
+ ) -> Any:
556
+ """Decode a BSON document into result."""
557
+ if result is None:
558
+ result = opts.document_class()
559
+ end = obj_end - 1
560
+ while position < end:
561
+ key, value, position = _element_to_dict(data, view, position, obj_end, opts)
562
+ result[key] = value
563
+ if position != obj_end:
564
+ raise InvalidBSON("bad object or element length")
565
+ return result
566
+
567
+
568
+ def _bson_to_dict(data: Any, opts: CodecOptions) -> Any:
569
+ """Decode a BSON string to document_class."""
570
+ data, view = get_data_and_view(data)
571
+ try:
572
+ if _raw_document_class(opts.document_class):
573
+ return opts.document_class(data, opts)
574
+ _, end = _get_object_size(data, 0, len(data))
575
+ return _elements_to_dict(data, view, 4, end, opts)
576
+ except InvalidBSON:
577
+ raise
578
+ except Exception:
579
+ # Change exception type to InvalidBSON but preserve traceback.
580
+ _, exc_value, exc_tb = sys.exc_info()
581
+ raise InvalidBSON(str(exc_value)).with_traceback(exc_tb)
582
+
583
+
584
+ if _USE_C:
585
+ _bson_to_dict = _cbson._bson_to_dict # noqa: F811
586
+
587
+
588
+ _PACK_FLOAT = struct.Struct("<d").pack
589
+ _PACK_INT = struct.Struct("<i").pack
590
+ _PACK_LENGTH_SUBTYPE = struct.Struct("<iB").pack
591
+ _PACK_LONG = struct.Struct("<q").pack
592
+ _PACK_TIMESTAMP = struct.Struct("<II").pack
593
+ _LIST_NAMES = tuple((str(i) + "\x00").encode("utf8") for i in range(1000))
594
+
595
+
596
+ def gen_list_name() -> Generator[bytes, None, None]:
597
+ """Generate "keys" for encoded lists in the sequence
598
+ b"0\x00", b"1\x00", b"2\x00", ...
599
+
600
+ The first 1000 keys are returned from a pre-built cache. All
601
+ subsequent keys are generated on the fly.
602
+ """
603
+ for name in _LIST_NAMES:
604
+ yield name
605
+
606
+ counter = itertools.count(1000)
607
+ while True:
608
+ yield (str(next(counter)) + "\x00").encode("utf8")
609
+
610
+
611
+ def _make_c_string_check(string: Union[str, bytes]) -> bytes:
612
+ """Make a 'C' string, checking for embedded NUL characters."""
613
+ if isinstance(string, bytes):
614
+ if b"\x00" in string:
615
+ raise InvalidDocument("BSON keys / regex patterns must not contain a NUL character")
616
+ try:
617
+ _utf_8_decode(string, None, True)
618
+ return string + b"\x00"
619
+ except UnicodeError:
620
+ raise InvalidStringData("strings in documents must be valid UTF-8: %r" % string)
621
+ else:
622
+ if "\x00" in string:
623
+ raise InvalidDocument("BSON keys / regex patterns must not contain a NUL character")
624
+ return cast(bytes, _utf_8_encode(string)[0]) + b"\x00"
625
+
626
+
627
+ def _make_c_string(string: Union[str, bytes]) -> bytes:
628
+ """Make a 'C' string."""
629
+ if isinstance(string, bytes):
630
+ try:
631
+ _utf_8_decode(string, None, True)
632
+ return string + b"\x00"
633
+ except UnicodeError:
634
+ raise InvalidStringData("strings in documents must be valid UTF-8: %r" % string)
635
+ else:
636
+ return cast(bytes, _utf_8_encode(string)[0]) + b"\x00"
637
+
638
+
639
+ def _make_name(string: str) -> bytes:
640
+ """Make a 'C' string suitable for a BSON key."""
641
+ # Keys can only be text in python 3.
642
+ if "\x00" in string:
643
+ raise InvalidDocument("BSON keys / regex patterns must not contain a NUL character")
644
+ return cast(bytes, _utf_8_encode(string)[0]) + b"\x00"
645
+
646
+
647
+ def _encode_float(name: bytes, value: float, dummy0: Any, dummy1: Any) -> bytes:
648
+ """Encode a float."""
649
+ return b"\x01" + name + _PACK_FLOAT(value)
650
+
651
+
652
+ def _encode_bytes(name: bytes, value: bytes, dummy0: Any, dummy1: Any) -> bytes:
653
+ """Encode a python bytes."""
654
+ # Python3 special case. Store 'bytes' as BSON binary subtype 0.
655
+ return b"\x05" + name + _PACK_INT(len(value)) + b"\x00" + value
656
+
657
+
658
+ def _encode_mapping(name: bytes, value: Any, check_keys: bool, opts: CodecOptions) -> bytes:
659
+ """Encode a mapping type."""
660
+ if _raw_document_class(value):
661
+ return b"\x03" + name + value.raw
662
+ data = b"".join([_element_to_bson(key, val, check_keys, opts) for key, val in value.items()])
663
+ return b"\x03" + name + _PACK_INT(len(data) + 5) + data + b"\x00"
664
+
665
+
666
+ def _encode_dbref(name: bytes, value: DBRef, check_keys: bool, opts: CodecOptions) -> bytes:
667
+ """Encode bson.dbref.DBRef."""
668
+ buf = bytearray(b"\x03" + name + b"\x00\x00\x00\x00")
669
+ begin = len(buf) - 4
670
+
671
+ buf += _name_value_to_bson(b"$ref\x00", value.collection, check_keys, opts)
672
+ buf += _name_value_to_bson(b"$id\x00", value.id, check_keys, opts)
673
+ if value.database is not None:
674
+ buf += _name_value_to_bson(b"$db\x00", value.database, check_keys, opts)
675
+ for key, val in value._DBRef__kwargs.items():
676
+ buf += _element_to_bson(key, val, check_keys, opts)
677
+
678
+ buf += b"\x00"
679
+ buf[begin : begin + 4] = _PACK_INT(len(buf) - begin)
680
+ return bytes(buf)
681
+
682
+
683
+ def _encode_list(name: bytes, value: Sequence[Any], check_keys: bool, opts: CodecOptions) -> bytes:
684
+ """Encode a list/tuple."""
685
+ lname = gen_list_name()
686
+ data = b"".join([_name_value_to_bson(next(lname), item, check_keys, opts) for item in value])
687
+ return b"\x04" + name + _PACK_INT(len(data) + 5) + data + b"\x00"
688
+
689
+
690
+ def _encode_text(name: bytes, value: str, dummy0: Any, dummy1: Any) -> bytes:
691
+ """Encode a python str."""
692
+ bvalue = _utf_8_encode(value)[0]
693
+ return b"\x02" + name + _PACK_INT(len(bvalue) + 1) + bvalue + b"\x00"
694
+
695
+
696
+ def _encode_binary(name: bytes, value: Binary, dummy0: Any, dummy1: Any) -> bytes:
697
+ """Encode bson.binary.Binary."""
698
+ subtype = value.subtype
699
+ if subtype == 2:
700
+ value = _PACK_INT(len(value)) + value # type: ignore
701
+ return b"\x05" + name + _PACK_LENGTH_SUBTYPE(len(value), subtype) + value
702
+
703
+
704
+ def _encode_uuid(name: bytes, value: uuid.UUID, dummy: Any, opts: CodecOptions) -> bytes:
705
+ """Encode uuid.UUID."""
706
+ uuid_representation = opts.uuid_representation
707
+ binval = Binary.from_uuid(value, uuid_representation=uuid_representation)
708
+ return _encode_binary(name, binval, dummy, opts)
709
+
710
+
711
+ def _encode_objectid(name: bytes, value: ObjectId, dummy: Any, dummy1: Any) -> bytes:
712
+ """Encode bson.objectid.ObjectId."""
713
+ return b"\x07" + name + value.binary
714
+
715
+
716
+ def _encode_bool(name: bytes, value: bool, dummy0: Any, dummy1: Any) -> bytes:
717
+ """Encode a python boolean (True/False)."""
718
+ return b"\x08" + name + (value and b"\x01" or b"\x00")
719
+
720
+
721
+ def _encode_datetime(name: bytes, value: datetime.datetime, dummy0: Any, dummy1: Any) -> bytes:
722
+ """Encode datetime.datetime."""
723
+ millis = _datetime_to_millis(value)
724
+ return b"\x09" + name + _PACK_LONG(millis)
725
+
726
+
727
+ def _encode_none(name: bytes, dummy0: Any, dummy1: Any, dummy2: Any) -> bytes:
728
+ """Encode python None."""
729
+ return b"\x0A" + name
730
+
731
+
732
+ def _encode_regex(name: bytes, value: Regex, dummy0: Any, dummy1: Any) -> bytes:
733
+ """Encode a python regex or bson.regex.Regex."""
734
+ flags = value.flags
735
+ # Python 3 common case
736
+ if flags == re.UNICODE:
737
+ return b"\x0B" + name + _make_c_string_check(value.pattern) + b"u\x00"
738
+ elif flags == 0:
739
+ return b"\x0B" + name + _make_c_string_check(value.pattern) + b"\x00"
740
+ else:
741
+ sflags = b""
742
+ if flags & re.IGNORECASE:
743
+ sflags += b"i"
744
+ if flags & re.LOCALE:
745
+ sflags += b"l"
746
+ if flags & re.MULTILINE:
747
+ sflags += b"m"
748
+ if flags & re.DOTALL:
749
+ sflags += b"s"
750
+ if flags & re.UNICODE:
751
+ sflags += b"u"
752
+ if flags & re.VERBOSE:
753
+ sflags += b"x"
754
+ sflags += b"\x00"
755
+ return b"\x0B" + name + _make_c_string_check(value.pattern) + sflags
756
+
757
+
758
+ def _encode_code(name: bytes, value: Code, dummy: Any, opts: CodecOptions) -> bytes:
759
+ """Encode bson.code.Code."""
760
+ cstring = _make_c_string(value)
761
+ cstrlen = len(cstring)
762
+ if value.scope is None:
763
+ return b"\x0D" + name + _PACK_INT(cstrlen) + cstring
764
+ scope = _dict_to_bson(value.scope, False, opts, False)
765
+ full_length = _PACK_INT(8 + cstrlen + len(scope))
766
+ return b"\x0F" + name + full_length + _PACK_INT(cstrlen) + cstring + scope
767
+
768
+
769
+ def _encode_int(name: bytes, value: int, dummy0: Any, dummy1: Any) -> bytes:
770
+ """Encode a python int."""
771
+ if -2147483648 <= value <= 2147483647:
772
+ return b"\x10" + name + _PACK_INT(value)
773
+ else:
774
+ try:
775
+ return b"\x12" + name + _PACK_LONG(value)
776
+ except struct.error:
777
+ raise OverflowError("BSON can only handle up to 8-byte ints")
778
+
779
+
780
+ def _encode_timestamp(name: bytes, value: Any, dummy0: Any, dummy1: Any) -> bytes:
781
+ """Encode bson.timestamp.Timestamp."""
782
+ return b"\x11" + name + _PACK_TIMESTAMP(value.inc, value.time)
783
+
784
+
785
+ def _encode_long(name: bytes, value: Any, dummy0: Any, dummy1: Any) -> bytes:
786
+ """Encode a python long (python 2.x)"""
787
+ try:
788
+ return b"\x12" + name + _PACK_LONG(value)
789
+ except struct.error:
790
+ raise OverflowError("BSON can only handle up to 8-byte ints")
791
+
792
+
793
+ def _encode_decimal128(name: bytes, value: Decimal128, dummy0: Any, dummy1: Any) -> bytes:
794
+ """Encode bson.decimal128.Decimal128."""
795
+ return b"\x13" + name + value.bid
796
+
797
+
798
+ def _encode_minkey(name: bytes, dummy0: Any, dummy1: Any, dummy2: Any) -> bytes:
799
+ """Encode bson.min_key.MinKey."""
800
+ return b"\xFF" + name
801
+
802
+
803
+ def _encode_maxkey(name: bytes, dummy0: Any, dummy1: Any, dummy2: Any) -> bytes:
804
+ """Encode bson.max_key.MaxKey."""
805
+ return b"\x7F" + name
806
+
807
+
808
+ # Each encoder function's signature is:
809
+ # - name: utf-8 bytes
810
+ # - value: a Python data type, e.g. a Python int for _encode_int
811
+ # - check_keys: bool, whether to check for invalid names
812
+ # - opts: a CodecOptions
813
+ _ENCODERS = {
814
+ bool: _encode_bool,
815
+ bytes: _encode_bytes,
816
+ datetime.datetime: _encode_datetime,
817
+ dict: _encode_mapping,
818
+ float: _encode_float,
819
+ int: _encode_int,
820
+ list: _encode_list,
821
+ str: _encode_text,
822
+ tuple: _encode_list,
823
+ type(None): _encode_none,
824
+ uuid.UUID: _encode_uuid,
825
+ Binary: _encode_binary,
826
+ Int64: _encode_long,
827
+ Code: _encode_code,
828
+ DBRef: _encode_dbref,
829
+ MaxKey: _encode_maxkey,
830
+ MinKey: _encode_minkey,
831
+ ObjectId: _encode_objectid,
832
+ Regex: _encode_regex,
833
+ RE_TYPE: _encode_regex,
834
+ SON: _encode_mapping,
835
+ Timestamp: _encode_timestamp,
836
+ Decimal128: _encode_decimal128,
837
+ # Special case. This will never be looked up directly.
838
+ _abc.Mapping: _encode_mapping,
839
+ }
840
+
841
+
842
+ _MARKERS = {
843
+ 5: _encode_binary,
844
+ 7: _encode_objectid,
845
+ 11: _encode_regex,
846
+ 13: _encode_code,
847
+ 17: _encode_timestamp,
848
+ 18: _encode_long,
849
+ 100: _encode_dbref,
850
+ 127: _encode_maxkey,
851
+ 255: _encode_minkey,
852
+ }
853
+
854
+
855
+ _BUILT_IN_TYPES = tuple(t for t in _ENCODERS)
856
+
857
+
858
+ def _name_value_to_bson(
859
+ name: bytes,
860
+ value: Any,
861
+ check_keys: bool,
862
+ opts: CodecOptions,
863
+ in_custom_call: bool = False,
864
+ in_fallback_call: bool = False,
865
+ ) -> bytes:
866
+ """Encode a single name, value pair."""
867
+ # First see if the type is already cached. KeyError will only ever
868
+ # happen once per subtype.
869
+ try:
870
+ return _ENCODERS[type(value)](name, value, check_keys, opts) # type: ignore
871
+ except KeyError:
872
+ pass
873
+
874
+ # Second, fall back to trying _type_marker. This has to be done
875
+ # before the loop below since users could subclass one of our
876
+ # custom types that subclasses a python built-in (e.g. Binary)
877
+ marker = getattr(value, "_type_marker", None)
878
+ if isinstance(marker, int) and marker in _MARKERS:
879
+ func = _MARKERS[marker]
880
+ # Cache this type for faster subsequent lookup.
881
+ _ENCODERS[type(value)] = func
882
+ return func(name, value, check_keys, opts) # type: ignore
883
+
884
+ # Third, check if a type encoder is registered for this type.
885
+ # Note that subtypes of registered custom types are not auto-encoded.
886
+ if not in_custom_call and opts.type_registry._encoder_map:
887
+ custom_encoder = opts.type_registry._encoder_map.get(type(value))
888
+ if custom_encoder is not None:
889
+ return _name_value_to_bson(
890
+ name, custom_encoder(value), check_keys, opts, in_custom_call=True
891
+ )
892
+
893
+ # Fourth, test each base type. This will only happen once for
894
+ # a subtype of a supported base type. Unlike in the C-extensions, this
895
+ # is done after trying the custom type encoder because checking for each
896
+ # subtype is expensive.
897
+ for base in _BUILT_IN_TYPES:
898
+ if isinstance(value, base):
899
+ func = _ENCODERS[base]
900
+ # Cache this type for faster subsequent lookup.
901
+ _ENCODERS[type(value)] = func
902
+ return func(name, value, check_keys, opts) # type: ignore
903
+
904
+ # As a last resort, try using the fallback encoder, if the user has
905
+ # provided one.
906
+ fallback_encoder = opts.type_registry._fallback_encoder
907
+ if not in_fallback_call and fallback_encoder is not None:
908
+ return _name_value_to_bson(
909
+ name, fallback_encoder(value), check_keys, opts, in_fallback_call=True
910
+ )
911
+
912
+ raise InvalidDocument("cannot encode object: %r, of type: %r" % (value, type(value)))
913
+
914
+
915
+ def _element_to_bson(key: Any, value: Any, check_keys: bool, opts: CodecOptions) -> bytes:
916
+ """Encode a single key, value pair."""
917
+ if not isinstance(key, str):
918
+ raise InvalidDocument("documents must have only string keys, key was %r" % (key,))
919
+ if check_keys:
920
+ if key.startswith("$"):
921
+ raise InvalidDocument("key %r must not start with '$'" % (key,))
922
+ if "." in key:
923
+ raise InvalidDocument("key %r must not contain '.'" % (key,))
924
+
925
+ name = _make_name(key)
926
+ return _name_value_to_bson(name, value, check_keys, opts)
927
+
928
+
929
+ def _dict_to_bson(doc: Any, check_keys: bool, opts: CodecOptions, top_level: bool = True) -> bytes:
930
+ """Encode a document to BSON."""
931
+ if _raw_document_class(doc):
932
+ return cast(bytes, doc.raw)
933
+ try:
934
+ elements = []
935
+ if top_level and "_id" in doc:
936
+ elements.append(_name_value_to_bson(b"_id\x00", doc["_id"], check_keys, opts))
937
+ for key, value in doc.items():
938
+ if not top_level or key != "_id":
939
+ elements.append(_element_to_bson(key, value, check_keys, opts))
940
+ except AttributeError:
941
+ raise TypeError("encoder expected a mapping type but got: %r" % (doc,))
942
+
943
+ encoded = b"".join(elements)
944
+ return _PACK_INT(len(encoded) + 5) + encoded + b"\x00"
945
+
946
+
947
+ if _USE_C:
948
+ _dict_to_bson = _cbson._dict_to_bson # noqa: F811
949
+
950
+
951
+ def _millis_to_datetime(millis: int, opts: CodecOptions) -> datetime.datetime:
952
+ """Convert milliseconds since epoch UTC to datetime."""
953
+ diff = ((millis % 1000) + 1000) % 1000
954
+ seconds = (millis - diff) // 1000
955
+ micros = diff * 1000
956
+ if opts.tz_aware:
957
+ dt = EPOCH_AWARE + datetime.timedelta(seconds=seconds, microseconds=micros)
958
+ if opts.tzinfo:
959
+ dt = dt.astimezone(opts.tzinfo)
960
+ return dt
961
+ else:
962
+ return EPOCH_NAIVE + datetime.timedelta(seconds=seconds, microseconds=micros)
963
+
964
+
965
+ def _datetime_to_millis(dtm: datetime.datetime) -> int:
966
+ """Convert datetime to milliseconds since epoch UTC."""
967
+ if dtm.utcoffset() is not None:
968
+ dtm = dtm - dtm.utcoffset() # type: ignore
969
+ return int(calendar.timegm(dtm.timetuple()) * 1000 + dtm.microsecond // 1000)
970
+
971
+
972
+ _CODEC_OPTIONS_TYPE_ERROR = TypeError("codec_options must be an instance of CodecOptions")
973
+
974
+
975
+ _DocumentIn = Mapping[str, Any]
976
+ _ReadableBuffer = Union[bytes, memoryview, "mmap", "array"]
977
+
978
+
979
+ def encode(
980
+ document: _DocumentIn,
981
+ check_keys: bool = False,
982
+ codec_options: CodecOptions = DEFAULT_CODEC_OPTIONS,
983
+ ) -> bytes:
984
+ """Encode a document to BSON.
985
+
986
+ A document can be any mapping type (like :class:`dict`).
987
+
988
+ Raises :class:`TypeError` if `document` is not a mapping type,
989
+ or contains keys that are not instances of
990
+ :class:`basestring` (:class:`str` in python 3). Raises
991
+ :class:`~bson.errors.InvalidDocument` if `document` cannot be
992
+ converted to :class:`BSON`.
993
+
994
+ :Parameters:
995
+ - `document`: mapping type representing a document
996
+ - `check_keys` (optional): check if keys start with '$' or
997
+ contain '.', raising :class:`~bson.errors.InvalidDocument` in
998
+ either case
999
+ - `codec_options` (optional): An instance of
1000
+ :class:`~bson.codec_options.CodecOptions`.
1001
+
1002
+ .. versionadded:: 3.9
1003
+ """
1004
+ if not isinstance(codec_options, CodecOptions):
1005
+ raise _CODEC_OPTIONS_TYPE_ERROR
1006
+
1007
+ return _dict_to_bson(document, check_keys, codec_options)
1008
+
1009
+
1010
+ def decode(
1011
+ data: _ReadableBuffer, codec_options: "Optional[CodecOptions[_DocumentType]]" = None
1012
+ ) -> _DocumentType:
1013
+ """Decode BSON to a document.
1014
+
1015
+ By default, returns a BSON document represented as a Python
1016
+ :class:`dict`. To use a different :class:`MutableMapping` class,
1017
+ configure a :class:`~bson.codec_options.CodecOptions`::
1018
+
1019
+ >>> import collections # From Python standard library.
1020
+ >>> import bson
1021
+ >>> from bson.codec_options import CodecOptions
1022
+ >>> data = bson.encode({'a': 1})
1023
+ >>> decoded_doc = bson.decode(data)
1024
+ <type 'dict'>
1025
+ >>> options = CodecOptions(document_class=collections.OrderedDict)
1026
+ >>> decoded_doc = bson.decode(data, codec_options=options)
1027
+ >>> type(decoded_doc)
1028
+ <class 'collections.OrderedDict'>
1029
+
1030
+ :Parameters:
1031
+ - `data`: the BSON to decode. Any bytes-like object that implements
1032
+ the buffer protocol.
1033
+ - `codec_options` (optional): An instance of
1034
+ :class:`~bson.codec_options.CodecOptions`.
1035
+
1036
+ .. versionadded:: 3.9
1037
+ """
1038
+ opts: CodecOptions = codec_options or DEFAULT_CODEC_OPTIONS
1039
+ if not isinstance(opts, CodecOptions):
1040
+ raise _CODEC_OPTIONS_TYPE_ERROR
1041
+
1042
+ return _bson_to_dict(data, opts)
1043
+
1044
+
1045
+ def _decode_all(data: _ReadableBuffer, opts: "CodecOptions[_DocumentType]") -> List[_DocumentType]:
1046
+ """Decode a BSON data to multiple documents."""
1047
+ data, view = get_data_and_view(data)
1048
+ data_len = len(data)
1049
+ docs: List[_DocumentType] = []
1050
+ position = 0
1051
+ end = data_len - 1
1052
+ use_raw = _raw_document_class(opts.document_class)
1053
+ try:
1054
+ while position < end:
1055
+ obj_size = _UNPACK_INT_FROM(data, position)[0]
1056
+ if data_len - position < obj_size:
1057
+ raise InvalidBSON("invalid object size")
1058
+ obj_end = position + obj_size - 1
1059
+ if data[obj_end] != 0:
1060
+ raise InvalidBSON("bad eoo")
1061
+ if use_raw:
1062
+ docs.append(opts.document_class(data[position : obj_end + 1], opts)) # type: ignore
1063
+ else:
1064
+ docs.append(_elements_to_dict(data, view, position + 4, obj_end, opts))
1065
+ position += obj_size
1066
+ return docs
1067
+ except InvalidBSON:
1068
+ raise
1069
+ except Exception:
1070
+ # Change exception type to InvalidBSON but preserve traceback.
1071
+ _, exc_value, exc_tb = sys.exc_info()
1072
+ raise InvalidBSON(str(exc_value)).with_traceback(exc_tb)
1073
+
1074
+
1075
+ if _USE_C:
1076
+ _decode_all = _cbson._decode_all # noqa: F811
1077
+
1078
+
1079
+ def decode_all(
1080
+ data: _ReadableBuffer, codec_options: "Optional[CodecOptions[_DocumentType]]" = None
1081
+ ) -> List[_DocumentType]:
1082
+ """Decode BSON data to multiple documents.
1083
+
1084
+ `data` must be a bytes-like object implementing the buffer protocol that
1085
+ provides concatenated, valid, BSON-encoded documents.
1086
+
1087
+ :Parameters:
1088
+ - `data`: BSON data
1089
+ - `codec_options` (optional): An instance of
1090
+ :class:`~bson.codec_options.CodecOptions`.
1091
+
1092
+ .. versionchanged:: 3.9
1093
+ Supports bytes-like objects that implement the buffer protocol.
1094
+
1095
+ .. versionchanged:: 3.0
1096
+ Removed `compile_re` option: PyMongo now always represents BSON regular
1097
+ expressions as :class:`~bson.regex.Regex` objects. Use
1098
+ :meth:`~bson.regex.Regex.try_compile` to attempt to convert from a
1099
+ BSON regular expression to a Python regular expression object.
1100
+
1101
+ Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with
1102
+ `codec_options`.
1103
+ """
1104
+ opts = codec_options or DEFAULT_CODEC_OPTIONS
1105
+ if not isinstance(opts, CodecOptions):
1106
+ raise _CODEC_OPTIONS_TYPE_ERROR
1107
+
1108
+ return _decode_all(data, opts) # type: ignore[arg-type]
1109
+
1110
+
1111
+ def _decode_selective(rawdoc: Any, fields: Any, codec_options: Any) -> Mapping[Any, Any]:
1112
+ if _raw_document_class(codec_options.document_class):
1113
+ # If document_class is RawBSONDocument, use vanilla dictionary for
1114
+ # decoding command response.
1115
+ doc = {}
1116
+ else:
1117
+ # Else, use the specified document_class.
1118
+ doc = codec_options.document_class()
1119
+ for key, value in rawdoc.items():
1120
+ if key in fields:
1121
+ if fields[key] == 1:
1122
+ doc[key] = _bson_to_dict(rawdoc.raw, codec_options)[key]
1123
+ else:
1124
+ doc[key] = _decode_selective(value, fields[key], codec_options)
1125
+ else:
1126
+ doc[key] = value
1127
+ return doc
1128
+
1129
+
1130
+ def _convert_raw_document_lists_to_streams(document: Any) -> None:
1131
+ cursor = document.get("cursor")
1132
+ if cursor:
1133
+ for key in ("firstBatch", "nextBatch"):
1134
+ batch = cursor.get(key)
1135
+ if batch:
1136
+ stream = b"".join(doc.raw for doc in batch)
1137
+ cursor[key] = [stream]
1138
+
1139
+
1140
+ def _decode_all_selective(data: Any, codec_options: CodecOptions, fields: Any) -> List[Any]:
1141
+ """Decode BSON data to a single document while using user-provided
1142
+ custom decoding logic.
1143
+
1144
+ `data` must be a string representing a valid, BSON-encoded document.
1145
+
1146
+ :Parameters:
1147
+ - `data`: BSON data
1148
+ - `codec_options`: An instance of
1149
+ :class:`~bson.codec_options.CodecOptions` with user-specified type
1150
+ decoders. If no decoders are found, this method is the same as
1151
+ ``decode_all``.
1152
+ - `fields`: Map of document namespaces where data that needs
1153
+ to be custom decoded lives or None. For example, to custom decode a
1154
+ list of objects in 'field1.subfield1', the specified value should be
1155
+ ``{'field1': {'subfield1': 1}}``. If ``fields`` is an empty map or
1156
+ None, this method is the same as ``decode_all``.
1157
+
1158
+ :Returns:
1159
+ - `document_list`: Single-member list containing the decoded document.
1160
+
1161
+ .. versionadded:: 3.8
1162
+ """
1163
+ if not codec_options.type_registry._decoder_map:
1164
+ return decode_all(data, codec_options)
1165
+
1166
+ if not fields:
1167
+ return decode_all(data, codec_options.with_options(type_registry=None))
1168
+
1169
+ # Decode documents for internal use.
1170
+ from bson.raw_bson import RawBSONDocument
1171
+
1172
+ internal_codec_options = codec_options.with_options(
1173
+ document_class=RawBSONDocument, type_registry=None
1174
+ )
1175
+ _doc = _bson_to_dict(data, internal_codec_options)
1176
+ return [
1177
+ _decode_selective(
1178
+ _doc,
1179
+ fields,
1180
+ codec_options,
1181
+ )
1182
+ ]
1183
+
1184
+
1185
+ def decode_iter(
1186
+ data: bytes, codec_options: "Optional[CodecOptions[_DocumentType]]" = None
1187
+ ) -> Iterator[_DocumentType]:
1188
+ """Decode BSON data to multiple documents as a generator.
1189
+
1190
+ Works similarly to the decode_all function, but yields one document at a
1191
+ time.
1192
+
1193
+ `data` must be a string of concatenated, valid, BSON-encoded
1194
+ documents.
1195
+
1196
+ :Parameters:
1197
+ - `data`: BSON data
1198
+ - `codec_options` (optional): An instance of
1199
+ :class:`~bson.codec_options.CodecOptions`.
1200
+
1201
+ .. versionchanged:: 3.0
1202
+ Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with
1203
+ `codec_options`.
1204
+
1205
+ .. versionadded:: 2.8
1206
+ """
1207
+ opts = codec_options or DEFAULT_CODEC_OPTIONS
1208
+ if not isinstance(opts, CodecOptions):
1209
+ raise _CODEC_OPTIONS_TYPE_ERROR
1210
+
1211
+ position = 0
1212
+ end = len(data) - 1
1213
+ while position < end:
1214
+ obj_size = _UNPACK_INT_FROM(data, position)[0]
1215
+ elements = data[position : position + obj_size]
1216
+ position += obj_size
1217
+
1218
+ yield _bson_to_dict(elements, opts)
1219
+
1220
+
1221
+ def decode_file_iter(
1222
+ file_obj: Union[BinaryIO, IO], codec_options: "Optional[CodecOptions[_DocumentType]]" = None
1223
+ ) -> Iterator[_DocumentType]:
1224
+ """Decode bson data from a file to multiple documents as a generator.
1225
+
1226
+ Works similarly to the decode_all function, but reads from the file object
1227
+ in chunks and parses bson in chunks, yielding one document at a time.
1228
+
1229
+ :Parameters:
1230
+ - `file_obj`: A file object containing BSON data.
1231
+ - `codec_options` (optional): An instance of
1232
+ :class:`~bson.codec_options.CodecOptions`.
1233
+
1234
+ .. versionchanged:: 3.0
1235
+ Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with
1236
+ `codec_options`.
1237
+
1238
+ .. versionadded:: 2.8
1239
+ """
1240
+ opts = codec_options or DEFAULT_CODEC_OPTIONS
1241
+ while True:
1242
+ # Read size of next object.
1243
+ size_data = file_obj.read(4)
1244
+ if not size_data:
1245
+ break # Finished with file normaly.
1246
+ elif len(size_data) != 4:
1247
+ raise InvalidBSON("cut off in middle of objsize")
1248
+ obj_size = _UNPACK_INT_FROM(size_data, 0)[0] - 4
1249
+ elements = size_data + file_obj.read(max(0, obj_size))
1250
+ yield _bson_to_dict(elements, opts)
1251
+
1252
+
1253
+ def is_valid(bson: bytes) -> bool:
1254
+ """Check that the given string represents valid :class:`BSON` data.
1255
+
1256
+ Raises :class:`TypeError` if `bson` is not an instance of
1257
+ :class:`str` (:class:`bytes` in python 3). Returns ``True``
1258
+ if `bson` is valid :class:`BSON`, ``False`` otherwise.
1259
+
1260
+ :Parameters:
1261
+ - `bson`: the data to be validated
1262
+ """
1263
+ if not isinstance(bson, bytes):
1264
+ raise TypeError("BSON data must be an instance of a subclass of bytes")
1265
+
1266
+ try:
1267
+ _bson_to_dict(bson, DEFAULT_CODEC_OPTIONS)
1268
+ return True
1269
+ except Exception:
1270
+ return False
1271
+
1272
+
1273
+ class BSON(bytes):
1274
+ """BSON (Binary JSON) data.
1275
+
1276
+ .. warning:: Using this class to encode and decode BSON adds a performance
1277
+ cost. For better performance use the module level functions
1278
+ :func:`encode` and :func:`decode` instead.
1279
+ """
1280
+
1281
+ @classmethod
1282
+ def encode(
1283
+ cls: Type["BSON"],
1284
+ document: _DocumentIn,
1285
+ check_keys: bool = False,
1286
+ codec_options: CodecOptions = DEFAULT_CODEC_OPTIONS,
1287
+ ) -> "BSON":
1288
+ """Encode a document to a new :class:`BSON` instance.
1289
+
1290
+ A document can be any mapping type (like :class:`dict`).
1291
+
1292
+ Raises :class:`TypeError` if `document` is not a mapping type,
1293
+ or contains keys that are not instances of
1294
+ :class:`basestring` (:class:`str` in python 3). Raises
1295
+ :class:`~bson.errors.InvalidDocument` if `document` cannot be
1296
+ converted to :class:`BSON`.
1297
+
1298
+ :Parameters:
1299
+ - `document`: mapping type representing a document
1300
+ - `check_keys` (optional): check if keys start with '$' or
1301
+ contain '.', raising :class:`~bson.errors.InvalidDocument` in
1302
+ either case
1303
+ - `codec_options` (optional): An instance of
1304
+ :class:`~bson.codec_options.CodecOptions`.
1305
+
1306
+ .. versionchanged:: 3.0
1307
+ Replaced `uuid_subtype` option with `codec_options`.
1308
+ """
1309
+ return cls(encode(document, check_keys, codec_options))
1310
+
1311
+ def decode(self, codec_options: CodecOptions = DEFAULT_CODEC_OPTIONS) -> _DocumentType: # type: ignore[override]
1312
+ """Decode this BSON data.
1313
+
1314
+ By default, returns a BSON document represented as a Python
1315
+ :class:`dict`. To use a different :class:`MutableMapping` class,
1316
+ configure a :class:`~bson.codec_options.CodecOptions`::
1317
+
1318
+ >>> import collections # From Python standard library.
1319
+ >>> import bson
1320
+ >>> from bson.codec_options import CodecOptions
1321
+ >>> data = bson.BSON.encode({'a': 1})
1322
+ >>> decoded_doc = bson.BSON(data).decode()
1323
+ <type 'dict'>
1324
+ >>> options = CodecOptions(document_class=collections.OrderedDict)
1325
+ >>> decoded_doc = bson.BSON(data).decode(codec_options=options)
1326
+ >>> type(decoded_doc)
1327
+ <class 'collections.OrderedDict'>
1328
+
1329
+ :Parameters:
1330
+ - `codec_options` (optional): An instance of
1331
+ :class:`~bson.codec_options.CodecOptions`.
1332
+
1333
+ .. versionchanged:: 3.0
1334
+ Removed `compile_re` option: PyMongo now always represents BSON
1335
+ regular expressions as :class:`~bson.regex.Regex` objects. Use
1336
+ :meth:`~bson.regex.Regex.try_compile` to attempt to convert from a
1337
+ BSON regular expression to a Python regular expression object.
1338
+
1339
+ Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with
1340
+ `codec_options`.
1341
+ """
1342
+ return decode(self, codec_options)
1343
+
1344
+
1345
+ def has_c() -> bool:
1346
+ """Is the C extension installed?"""
1347
+ return _USE_C