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,984 @@
1
+ # Copyright 2011-present MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you
4
+ # may not use this file except in compliance with the License. You
5
+ # 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
12
+ # implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+
15
+
16
+ """Functions and classes common to multiple pymongo modules."""
17
+
18
+ import datetime
19
+ import warnings
20
+ from collections import OrderedDict, abc
21
+ from typing import (
22
+ Any,
23
+ Callable,
24
+ Dict,
25
+ List,
26
+ Mapping,
27
+ MutableMapping,
28
+ NoReturn,
29
+ Optional,
30
+ Sequence,
31
+ Tuple,
32
+ Type,
33
+ Union,
34
+ )
35
+ from urllib.parse import unquote_plus
36
+
37
+ from bson import SON
38
+ from bson.binary import UuidRepresentation
39
+ from bson.codec_options import CodecOptions, TypeRegistry
40
+ from bson.raw_bson import RawBSONDocument
41
+ from pymongo.auth import MECHANISMS
42
+ from pymongo.compression_support import (
43
+ validate_compressors,
44
+ validate_zlib_compression_level,
45
+ )
46
+ from pymongo.driver_info import DriverInfo
47
+ from pymongo.errors import ConfigurationError
48
+ from pymongo.monitoring import _validate_event_listeners
49
+ from pymongo.read_concern import ReadConcern
50
+ from pymongo.read_preferences import _MONGOS_MODES, _ServerMode
51
+ from pymongo.server_api import ServerApi
52
+ from pymongo.write_concern import DEFAULT_WRITE_CONCERN, WriteConcern
53
+
54
+ ORDERED_TYPES: Sequence[Type] = (SON, OrderedDict)
55
+
56
+ # Defaults until we connect to a server and get updated limits.
57
+ MAX_BSON_SIZE = 16 * (1024**2)
58
+ MAX_MESSAGE_SIZE: int = 2 * MAX_BSON_SIZE
59
+ MIN_WIRE_VERSION = 0
60
+ MAX_WIRE_VERSION = 0
61
+ MAX_WRITE_BATCH_SIZE = 1000
62
+
63
+ # What this version of PyMongo supports.
64
+ MIN_SUPPORTED_SERVER_VERSION = "3.6"
65
+ MIN_SUPPORTED_WIRE_VERSION = 6
66
+ MAX_SUPPORTED_WIRE_VERSION = 17
67
+
68
+ # Frequency to call hello on servers, in seconds.
69
+ HEARTBEAT_FREQUENCY = 10
70
+
71
+ # Frequency to clean up unclosed cursors, in seconds.
72
+ # See MongoClient._process_kill_cursors.
73
+ KILL_CURSOR_FREQUENCY = 1
74
+
75
+ # Frequency to process events queue, in seconds.
76
+ EVENTS_QUEUE_FREQUENCY = 1
77
+
78
+ # How long to wait, in seconds, for a suitable server to be found before
79
+ # aborting an operation. For example, if the client attempts an insert
80
+ # during a replica set election, SERVER_SELECTION_TIMEOUT governs the
81
+ # longest it is willing to wait for a new primary to be found.
82
+ SERVER_SELECTION_TIMEOUT = 30
83
+
84
+ # Spec requires at least 500ms between hello calls.
85
+ MIN_HEARTBEAT_INTERVAL = 0.5
86
+
87
+ # Spec requires at least 60s between SRV rescans.
88
+ MIN_SRV_RESCAN_INTERVAL = 60
89
+
90
+ # Default connectTimeout in seconds.
91
+ CONNECT_TIMEOUT = 20.0
92
+
93
+ # Default value for maxPoolSize.
94
+ MAX_POOL_SIZE = 100
95
+
96
+ # Default value for minPoolSize.
97
+ MIN_POOL_SIZE = 0
98
+
99
+ # The maximum number of concurrent connection creation attempts per pool.
100
+ MAX_CONNECTING = 2
101
+
102
+ # Default value for maxIdleTimeMS.
103
+ MAX_IDLE_TIME_MS: Optional[int] = None
104
+
105
+ # Default value for maxIdleTimeMS in seconds.
106
+ MAX_IDLE_TIME_SEC: Optional[int] = None
107
+
108
+ # Default value for waitQueueTimeoutMS in seconds.
109
+ WAIT_QUEUE_TIMEOUT: Optional[int] = None
110
+
111
+ # Default value for localThresholdMS.
112
+ LOCAL_THRESHOLD_MS = 15
113
+
114
+ # Default value for retryWrites.
115
+ RETRY_WRITES = True
116
+
117
+ # Default value for retryReads.
118
+ RETRY_READS = True
119
+
120
+ # The error code returned when a command doesn't exist.
121
+ COMMAND_NOT_FOUND_CODES: Sequence[int] = (59,)
122
+
123
+ # Error codes to ignore if GridFS calls createIndex on a secondary
124
+ UNAUTHORIZED_CODES: Sequence[int] = (13, 16547, 16548)
125
+
126
+ # Maximum number of sessions to send in a single endSessions command.
127
+ # From the driver sessions spec.
128
+ _MAX_END_SESSIONS = 10000
129
+
130
+ # Default value for srvServiceName
131
+ SRV_SERVICE_NAME = "mongodb"
132
+
133
+
134
+ def partition_node(node: str) -> Tuple[str, int]:
135
+ """Split a host:port string into (host, int(port)) pair."""
136
+ host = node
137
+ port = 27017
138
+ idx = node.rfind(":")
139
+ if idx != -1:
140
+ host, port = node[:idx], int(node[idx + 1 :])
141
+ if host.startswith("["):
142
+ host = host[1:-1]
143
+ return host, port
144
+
145
+
146
+ def clean_node(node: str) -> Tuple[str, int]:
147
+ """Split and normalize a node name from a hello response."""
148
+ host, port = partition_node(node)
149
+
150
+ # Normalize hostname to lowercase, since DNS is case-insensitive:
151
+ # http://tools.ietf.org/html/rfc4343
152
+ # This prevents useless rediscovery if "foo.com" is in the seed list but
153
+ # "FOO.com" is in the hello response.
154
+ return host.lower(), port
155
+
156
+
157
+ def raise_config_error(key: str, dummy: Any) -> NoReturn:
158
+ """Raise ConfigurationError with the given key name."""
159
+ raise ConfigurationError("Unknown option %s" % (key,))
160
+
161
+
162
+ # Mapping of URI uuid representation options to valid subtypes.
163
+ _UUID_REPRESENTATIONS = {
164
+ "unspecified": UuidRepresentation.UNSPECIFIED,
165
+ "standard": UuidRepresentation.STANDARD,
166
+ "pythonLegacy": UuidRepresentation.PYTHON_LEGACY,
167
+ "javaLegacy": UuidRepresentation.JAVA_LEGACY,
168
+ "csharpLegacy": UuidRepresentation.CSHARP_LEGACY,
169
+ }
170
+
171
+
172
+ def validate_boolean(option: str, value: Any) -> bool:
173
+ """Validates that 'value' is True or False."""
174
+ if isinstance(value, bool):
175
+ return value
176
+ raise TypeError("%s must be True or False" % (option,))
177
+
178
+
179
+ def validate_boolean_or_string(option: str, value: Any) -> bool:
180
+ """Validates that value is True, False, 'true', or 'false'."""
181
+ if isinstance(value, str):
182
+ if value not in ("true", "false"):
183
+ raise ValueError("The value of %s must be 'true' or 'false'" % (option,))
184
+ return value == "true"
185
+ return validate_boolean(option, value)
186
+
187
+
188
+ def validate_integer(option: str, value: Any) -> int:
189
+ """Validates that 'value' is an integer (or basestring representation)."""
190
+ if isinstance(value, int):
191
+ return value
192
+ elif isinstance(value, str):
193
+ try:
194
+ return int(value)
195
+ except ValueError:
196
+ raise ValueError("The value of %s must be an integer" % (option,))
197
+ raise TypeError("Wrong type for %s, value must be an integer" % (option,))
198
+
199
+
200
+ def validate_positive_integer(option: str, value: Any) -> int:
201
+ """Validate that 'value' is a positive integer, which does not include 0."""
202
+ val = validate_integer(option, value)
203
+ if val <= 0:
204
+ raise ValueError("The value of %s must be a positive integer" % (option,))
205
+ return val
206
+
207
+
208
+ def validate_non_negative_integer(option: str, value: Any) -> int:
209
+ """Validate that 'value' is a positive integer or 0."""
210
+ val = validate_integer(option, value)
211
+ if val < 0:
212
+ raise ValueError("The value of %s must be a non negative integer" % (option,))
213
+ return val
214
+
215
+
216
+ def validate_readable(option: str, value: Any) -> Optional[str]:
217
+ """Validates that 'value' is file-like and readable."""
218
+ if value is None:
219
+ return value
220
+ # First make sure its a string py3.3 open(True, 'r') succeeds
221
+ # Used in ssl cert checking due to poor ssl module error reporting
222
+ value = validate_string(option, value)
223
+ open(value, "r").close()
224
+ return value
225
+
226
+
227
+ def validate_positive_integer_or_none(option: str, value: Any) -> Optional[int]:
228
+ """Validate that 'value' is a positive integer or None."""
229
+ if value is None:
230
+ return value
231
+ return validate_positive_integer(option, value)
232
+
233
+
234
+ def validate_non_negative_integer_or_none(option: str, value: Any) -> Optional[int]:
235
+ """Validate that 'value' is a positive integer or 0 or None."""
236
+ if value is None:
237
+ return value
238
+ return validate_non_negative_integer(option, value)
239
+
240
+
241
+ def validate_string(option: str, value: Any) -> str:
242
+ """Validates that 'value' is an instance of `str`."""
243
+ if isinstance(value, str):
244
+ return value
245
+ raise TypeError("Wrong type for %s, value must be an instance of str" % (option,))
246
+
247
+
248
+ def validate_string_or_none(option: str, value: Any) -> Optional[str]:
249
+ """Validates that 'value' is an instance of `basestring` or `None`."""
250
+ if value is None:
251
+ return value
252
+ return validate_string(option, value)
253
+
254
+
255
+ def validate_int_or_basestring(option: str, value: Any) -> Union[int, str]:
256
+ """Validates that 'value' is an integer or string."""
257
+ if isinstance(value, int):
258
+ return value
259
+ elif isinstance(value, str):
260
+ try:
261
+ return int(value)
262
+ except ValueError:
263
+ return value
264
+ raise TypeError("Wrong type for %s, value must be an integer or a string" % (option,))
265
+
266
+
267
+ def validate_non_negative_int_or_basestring(option: Any, value: Any) -> Union[int, str]:
268
+ """Validates that 'value' is an integer or string."""
269
+ if isinstance(value, int):
270
+ return value
271
+ elif isinstance(value, str):
272
+ try:
273
+ val = int(value)
274
+ except ValueError:
275
+ return value
276
+ return validate_non_negative_integer(option, val)
277
+ raise TypeError(
278
+ "Wrong type for %s, value must be an non negative integer or a string" % (option,)
279
+ )
280
+
281
+
282
+ def validate_positive_float(option: str, value: Any) -> float:
283
+ """Validates that 'value' is a float, or can be converted to one, and is
284
+ positive.
285
+ """
286
+ errmsg = "%s must be an integer or float" % (option,)
287
+ try:
288
+ value = float(value)
289
+ except ValueError:
290
+ raise ValueError(errmsg)
291
+ except TypeError:
292
+ raise TypeError(errmsg)
293
+
294
+ # float('inf') doesn't work in 2.4 or 2.5 on Windows, so just cap floats at
295
+ # one billion - this is a reasonable approximation for infinity
296
+ if not 0 < value < 1e9:
297
+ raise ValueError("%s must be greater than 0 and less than one billion" % (option,))
298
+ return value
299
+
300
+
301
+ def validate_positive_float_or_zero(option: str, value: Any) -> float:
302
+ """Validates that 'value' is 0 or a positive float, or can be converted to
303
+ 0 or a positive float.
304
+ """
305
+ if value == 0 or value == "0":
306
+ return 0
307
+ return validate_positive_float(option, value)
308
+
309
+
310
+ def validate_timeout_or_none(option: str, value: Any) -> Optional[float]:
311
+ """Validates a timeout specified in milliseconds returning
312
+ a value in floating point seconds.
313
+ """
314
+ if value is None:
315
+ return value
316
+ return validate_positive_float(option, value) / 1000.0
317
+
318
+
319
+ def validate_timeout_or_zero(option: str, value: Any) -> float:
320
+ """Validates a timeout specified in milliseconds returning
321
+ a value in floating point seconds for the case where None is an error
322
+ and 0 is valid. Setting the timeout to nothing in the URI string is a
323
+ config error.
324
+ """
325
+ if value is None:
326
+ raise ConfigurationError("%s cannot be None" % (option,))
327
+ if value == 0 or value == "0":
328
+ return 0
329
+ return validate_positive_float(option, value) / 1000.0
330
+
331
+
332
+ def validate_timeout_or_none_or_zero(option: Any, value: Any) -> Optional[float]:
333
+ """Validates a timeout specified in milliseconds returning
334
+ a value in floating point seconds. value=0 and value="0" are treated the
335
+ same as value=None which means unlimited timeout.
336
+ """
337
+ if value is None or value == 0 or value == "0":
338
+ return None
339
+ return validate_positive_float(option, value) / 1000.0
340
+
341
+
342
+ def validate_timeoutms(option: Any, value: Any) -> Optional[float]:
343
+ """Validates a timeout specified in milliseconds returning
344
+ a value in floating point seconds.
345
+ """
346
+ if value is None:
347
+ return None
348
+ return validate_positive_float_or_zero(option, value) / 1000.0
349
+
350
+
351
+ def validate_max_staleness(option: str, value: Any) -> int:
352
+ """Validates maxStalenessSeconds according to the Max Staleness Spec."""
353
+ if value == -1 or value == "-1":
354
+ # Default: No maximum staleness.
355
+ return -1
356
+ return validate_positive_integer(option, value)
357
+
358
+
359
+ def validate_read_preference(dummy: Any, value: Any) -> _ServerMode:
360
+ """Validate a read preference."""
361
+ if not isinstance(value, _ServerMode):
362
+ raise TypeError("%r is not a read preference." % (value,))
363
+ return value
364
+
365
+
366
+ def validate_read_preference_mode(dummy: Any, value: Any) -> _ServerMode:
367
+ """Validate read preference mode for a MongoClient.
368
+
369
+ .. versionchanged:: 3.5
370
+ Returns the original ``value`` instead of the validated read preference
371
+ mode.
372
+ """
373
+ if value not in _MONGOS_MODES:
374
+ raise ValueError("%s is not a valid read preference" % (value,))
375
+ return value
376
+
377
+
378
+ def validate_auth_mechanism(option: str, value: Any) -> str:
379
+ """Validate the authMechanism URI option."""
380
+ if value not in MECHANISMS:
381
+ raise ValueError("%s must be in %s" % (option, tuple(MECHANISMS)))
382
+ return value
383
+
384
+
385
+ def validate_uuid_representation(dummy: Any, value: Any) -> int:
386
+ """Validate the uuid representation option selected in the URI."""
387
+ try:
388
+ return _UUID_REPRESENTATIONS[value]
389
+ except KeyError:
390
+ raise ValueError(
391
+ "%s is an invalid UUID representation. "
392
+ "Must be one of "
393
+ "%s" % (value, tuple(_UUID_REPRESENTATIONS))
394
+ )
395
+
396
+
397
+ def validate_read_preference_tags(name: str, value: Any) -> List[Dict[str, str]]:
398
+ """Parse readPreferenceTags if passed as a client kwarg."""
399
+ if not isinstance(value, list):
400
+ value = [value]
401
+
402
+ tag_sets: List = []
403
+ for tag_set in value:
404
+ if tag_set == "":
405
+ tag_sets.append({})
406
+ continue
407
+ try:
408
+ tags = {}
409
+ for tag in tag_set.split(","):
410
+ key, val = tag.split(":")
411
+ tags[unquote_plus(key)] = unquote_plus(val)
412
+ tag_sets.append(tags)
413
+ except Exception:
414
+ raise ValueError("%r not a valid value for %s" % (tag_set, name))
415
+ return tag_sets
416
+
417
+
418
+ _MECHANISM_PROPS = frozenset(
419
+ ["SERVICE_NAME", "CANONICALIZE_HOST_NAME", "SERVICE_REALM", "AWS_SESSION_TOKEN"]
420
+ )
421
+
422
+
423
+ def validate_auth_mechanism_properties(option: str, value: Any) -> Dict[str, Union[bool, str]]:
424
+ """Validate authMechanismProperties."""
425
+ value = validate_string(option, value)
426
+ props: Dict[str, Any] = {}
427
+ for opt in value.split(","):
428
+ try:
429
+ key, val = opt.split(":")
430
+ except ValueError:
431
+ # Try not to leak the token.
432
+ if "AWS_SESSION_TOKEN" in opt:
433
+ opt = (
434
+ "AWS_SESSION_TOKEN:<redacted token>, did you forget "
435
+ "to percent-escape the token with quote_plus?"
436
+ )
437
+ raise ValueError(
438
+ "auth mechanism properties must be "
439
+ "key:value pairs like SERVICE_NAME:"
440
+ "mongodb, not %s." % (opt,)
441
+ )
442
+ if key not in _MECHANISM_PROPS:
443
+ raise ValueError(
444
+ "%s is not a supported auth "
445
+ "mechanism property. Must be one of "
446
+ "%s." % (key, tuple(_MECHANISM_PROPS))
447
+ )
448
+ if key == "CANONICALIZE_HOST_NAME":
449
+ props[key] = validate_boolean_or_string(key, val)
450
+ else:
451
+ props[key] = unquote_plus(val)
452
+
453
+ return props
454
+
455
+
456
+ def validate_document_class(
457
+ option: str, value: Any
458
+ ) -> Union[Type[MutableMapping], Type[RawBSONDocument]]:
459
+ """Validate the document_class option."""
460
+ # issubclass can raise TypeError for generic aliases like SON[str, Any].
461
+ # In that case we can use the base class for the comparison.
462
+ is_mapping = False
463
+ try:
464
+ is_mapping = issubclass(value, abc.MutableMapping)
465
+ except TypeError:
466
+ if hasattr(value, "__origin__"):
467
+ is_mapping = issubclass(value.__origin__, abc.MutableMapping)
468
+ if not is_mapping and not issubclass(value, RawBSONDocument):
469
+ raise TypeError(
470
+ "%s must be dict, bson.son.SON, "
471
+ "bson.raw_bson.RawBSONDocument, or a "
472
+ "sublass of collections.MutableMapping" % (option,)
473
+ )
474
+ return value
475
+
476
+
477
+ def validate_type_registry(option: Any, value: Any) -> Optional[TypeRegistry]:
478
+ """Validate the type_registry option."""
479
+ if value is not None and not isinstance(value, TypeRegistry):
480
+ raise TypeError("%s must be an instance of %s" % (option, TypeRegistry))
481
+ return value
482
+
483
+
484
+ def validate_list(option: str, value: Any) -> List:
485
+ """Validates that 'value' is a list."""
486
+ if not isinstance(value, list):
487
+ raise TypeError("%s must be a list" % (option,))
488
+ return value
489
+
490
+
491
+ def validate_list_or_none(option: Any, value: Any) -> Optional[List]:
492
+ """Validates that 'value' is a list or None."""
493
+ if value is None:
494
+ return value
495
+ return validate_list(option, value)
496
+
497
+
498
+ def validate_list_or_mapping(option: Any, value: Any) -> None:
499
+ """Validates that 'value' is a list or a document."""
500
+ if not isinstance(value, (abc.Mapping, list)):
501
+ raise TypeError(
502
+ "%s must either be a list or an instance of dict, "
503
+ "bson.son.SON, or any other type that inherits from "
504
+ "collections.Mapping" % (option,)
505
+ )
506
+
507
+
508
+ def validate_is_mapping(option: str, value: Any) -> None:
509
+ """Validate the type of method arguments that expect a document."""
510
+ if not isinstance(value, abc.Mapping):
511
+ raise TypeError(
512
+ "%s must be an instance of dict, bson.son.SON, or "
513
+ "any other type that inherits from "
514
+ "collections.Mapping" % (option,)
515
+ )
516
+
517
+
518
+ def validate_is_document_type(option: str, value: Any) -> None:
519
+ """Validate the type of method arguments that expect a MongoDB document."""
520
+ if not isinstance(value, (abc.MutableMapping, RawBSONDocument)):
521
+ raise TypeError(
522
+ "%s must be an instance of dict, bson.son.SON, "
523
+ "bson.raw_bson.RawBSONDocument, or "
524
+ "a type that inherits from "
525
+ "collections.MutableMapping" % (option,)
526
+ )
527
+
528
+
529
+ def validate_appname_or_none(option: str, value: Any) -> Optional[str]:
530
+ """Validate the appname option."""
531
+ if value is None:
532
+ return value
533
+ validate_string(option, value)
534
+ # We need length in bytes, so encode utf8 first.
535
+ if len(value.encode("utf-8")) > 128:
536
+ raise ValueError("%s must be <= 128 bytes" % (option,))
537
+ return value
538
+
539
+
540
+ def validate_driver_or_none(option: Any, value: Any) -> Optional[DriverInfo]:
541
+ """Validate the driver keyword arg."""
542
+ if value is None:
543
+ return value
544
+ if not isinstance(value, DriverInfo):
545
+ raise TypeError("%s must be an instance of DriverInfo" % (option,))
546
+ return value
547
+
548
+
549
+ def validate_server_api_or_none(option: Any, value: Any) -> Optional[ServerApi]:
550
+ """Validate the server_api keyword arg."""
551
+ if value is None:
552
+ return value
553
+ if not isinstance(value, ServerApi):
554
+ raise TypeError("%s must be an instance of ServerApi" % (option,))
555
+ return value
556
+
557
+
558
+ def validate_is_callable_or_none(option: Any, value: Any) -> Optional[Callable]:
559
+ """Validates that 'value' is a callable."""
560
+ if value is None:
561
+ return value
562
+ if not callable(value):
563
+ raise ValueError("%s must be a callable" % (option,))
564
+ return value
565
+
566
+
567
+ def validate_ok_for_replace(replacement: Mapping[str, Any]) -> None:
568
+ """Validate a replacement document."""
569
+ validate_is_mapping("replacement", replacement)
570
+ # Replacement can be {}
571
+ if replacement and not isinstance(replacement, RawBSONDocument):
572
+ first = next(iter(replacement))
573
+ if first.startswith("$"):
574
+ raise ValueError("replacement can not include $ operators")
575
+
576
+
577
+ def validate_ok_for_update(update: Any) -> None:
578
+ """Validate an update document."""
579
+ validate_list_or_mapping("update", update)
580
+ # Update cannot be {}.
581
+ if not update:
582
+ raise ValueError("update cannot be empty")
583
+
584
+ is_document = not isinstance(update, list)
585
+ first = next(iter(update))
586
+ if is_document and not first.startswith("$"):
587
+ raise ValueError("update only works with $ operators")
588
+
589
+
590
+ _UNICODE_DECODE_ERROR_HANDLERS = frozenset(["strict", "replace", "ignore"])
591
+
592
+
593
+ def validate_unicode_decode_error_handler(dummy: Any, value: str) -> str:
594
+ """Validate the Unicode decode error handler option of CodecOptions."""
595
+ if value not in _UNICODE_DECODE_ERROR_HANDLERS:
596
+ raise ValueError(
597
+ "%s is an invalid Unicode decode error handler. "
598
+ "Must be one of "
599
+ "%s" % (value, tuple(_UNICODE_DECODE_ERROR_HANDLERS))
600
+ )
601
+ return value
602
+
603
+
604
+ def validate_tzinfo(dummy: Any, value: Any) -> Optional[datetime.tzinfo]:
605
+ """Validate the tzinfo option"""
606
+ if value is not None and not isinstance(value, datetime.tzinfo):
607
+ raise TypeError("%s must be an instance of datetime.tzinfo" % value)
608
+ return value
609
+
610
+
611
+ def validate_auto_encryption_opts_or_none(option: Any, value: Any) -> Optional[Any]:
612
+ """Validate the driver keyword arg."""
613
+ if value is None:
614
+ return value
615
+ from pymongo.encryption_options import AutoEncryptionOpts
616
+
617
+ if not isinstance(value, AutoEncryptionOpts):
618
+ raise TypeError("%s must be an instance of AutoEncryptionOpts" % (option,))
619
+
620
+ return value
621
+
622
+
623
+ # Dictionary where keys are the names of public URI options, and values
624
+ # are lists of aliases for that option.
625
+ URI_OPTIONS_ALIAS_MAP: Dict[str, List[str]] = {
626
+ "tls": ["ssl"],
627
+ }
628
+
629
+ # Dictionary where keys are the names of URI options, and values
630
+ # are functions that validate user-input values for that option. If an option
631
+ # alias uses a different validator than its public counterpart, it should be
632
+ # included here as a key, value pair.
633
+ URI_OPTIONS_VALIDATOR_MAP: Dict[str, Callable[[Any, Any], Any]] = {
634
+ "appname": validate_appname_or_none,
635
+ "authmechanism": validate_auth_mechanism,
636
+ "authmechanismproperties": validate_auth_mechanism_properties,
637
+ "authsource": validate_string,
638
+ "compressors": validate_compressors,
639
+ "connecttimeoutms": validate_timeout_or_none_or_zero,
640
+ "directconnection": validate_boolean_or_string,
641
+ "heartbeatfrequencyms": validate_timeout_or_none,
642
+ "journal": validate_boolean_or_string,
643
+ "localthresholdms": validate_positive_float_or_zero,
644
+ "maxidletimems": validate_timeout_or_none,
645
+ "maxconnecting": validate_positive_integer,
646
+ "maxpoolsize": validate_non_negative_integer_or_none,
647
+ "maxstalenessseconds": validate_max_staleness,
648
+ "readconcernlevel": validate_string_or_none,
649
+ "readpreference": validate_read_preference_mode,
650
+ "readpreferencetags": validate_read_preference_tags,
651
+ "replicaset": validate_string_or_none,
652
+ "retryreads": validate_boolean_or_string,
653
+ "retrywrites": validate_boolean_or_string,
654
+ "loadbalanced": validate_boolean_or_string,
655
+ "serverselectiontimeoutms": validate_timeout_or_zero,
656
+ "sockettimeoutms": validate_timeout_or_none_or_zero,
657
+ "tls": validate_boolean_or_string,
658
+ "tlsallowinvalidcertificates": validate_boolean_or_string,
659
+ "tlsallowinvalidhostnames": validate_boolean_or_string,
660
+ "tlscafile": validate_readable,
661
+ "tlscertificatekeyfile": validate_readable,
662
+ "tlscertificatekeyfilepassword": validate_string_or_none,
663
+ "tlsdisableocspendpointcheck": validate_boolean_or_string,
664
+ "tlsinsecure": validate_boolean_or_string,
665
+ "w": validate_non_negative_int_or_basestring,
666
+ "wtimeoutms": validate_non_negative_integer,
667
+ "zlibcompressionlevel": validate_zlib_compression_level,
668
+ "srvservicename": validate_string,
669
+ "srvmaxhosts": validate_non_negative_integer,
670
+ "timeoutms": validate_timeoutms,
671
+ }
672
+
673
+ # Dictionary where keys are the names of URI options specific to pymongo,
674
+ # and values are functions that validate user-input values for those options.
675
+ NONSPEC_OPTIONS_VALIDATOR_MAP: Dict[str, Callable[[Any, Any], Any]] = {
676
+ "connect": validate_boolean_or_string,
677
+ "driver": validate_driver_or_none,
678
+ "server_api": validate_server_api_or_none,
679
+ "fsync": validate_boolean_or_string,
680
+ "minpoolsize": validate_non_negative_integer,
681
+ "tlscrlfile": validate_readable,
682
+ "tz_aware": validate_boolean_or_string,
683
+ "unicode_decode_error_handler": validate_unicode_decode_error_handler,
684
+ "uuidrepresentation": validate_uuid_representation,
685
+ "waitqueuemultiple": validate_non_negative_integer_or_none,
686
+ "waitqueuetimeoutms": validate_timeout_or_none,
687
+ }
688
+
689
+ # Dictionary where keys are the names of keyword-only options for the
690
+ # MongoClient constructor, and values are functions that validate user-input
691
+ # values for those options.
692
+ KW_VALIDATORS: Dict[str, Callable[[Any, Any], Any]] = {
693
+ "document_class": validate_document_class,
694
+ "type_registry": validate_type_registry,
695
+ "read_preference": validate_read_preference,
696
+ "event_listeners": _validate_event_listeners,
697
+ "tzinfo": validate_tzinfo,
698
+ "username": validate_string_or_none,
699
+ "password": validate_string_or_none,
700
+ "server_selector": validate_is_callable_or_none,
701
+ "auto_encryption_opts": validate_auto_encryption_opts_or_none,
702
+ }
703
+
704
+ # Dictionary where keys are any URI option name, and values are the
705
+ # internally-used names of that URI option. Options with only one name
706
+ # variant need not be included here. Options whose public and internal
707
+ # names are the same need not be included here.
708
+ INTERNAL_URI_OPTION_NAME_MAP: Dict[str, str] = {
709
+ "ssl": "tls",
710
+ }
711
+
712
+ # Map from deprecated URI option names to a tuple indicating the method of
713
+ # their deprecation and any additional information that may be needed to
714
+ # construct the warning message.
715
+ URI_OPTIONS_DEPRECATION_MAP: Dict[str, Tuple[str, str]] = {
716
+ # format: <deprecated option name>: (<mode>, <message>),
717
+ # Supported <mode> values:
718
+ # - 'renamed': <message> should be the new option name. Note that case is
719
+ # preserved for renamed options as they are part of user warnings.
720
+ # - 'removed': <message> may suggest the rationale for deprecating the
721
+ # option and/or recommend remedial action.
722
+ # For example:
723
+ # 'wtimeout': ('renamed', 'wTimeoutMS'),
724
+ }
725
+
726
+ # Augment the option validator map with pymongo-specific option information.
727
+ URI_OPTIONS_VALIDATOR_MAP.update(NONSPEC_OPTIONS_VALIDATOR_MAP)
728
+ for optname, aliases in URI_OPTIONS_ALIAS_MAP.items():
729
+ for alias in aliases:
730
+ if alias not in URI_OPTIONS_VALIDATOR_MAP:
731
+ URI_OPTIONS_VALIDATOR_MAP[alias] = URI_OPTIONS_VALIDATOR_MAP[optname]
732
+
733
+ # Map containing all URI option and keyword argument validators.
734
+ VALIDATORS: Dict[str, Callable[[Any, Any], Any]] = URI_OPTIONS_VALIDATOR_MAP.copy()
735
+ VALIDATORS.update(KW_VALIDATORS)
736
+
737
+ # List of timeout-related options.
738
+ TIMEOUT_OPTIONS: List[str] = [
739
+ "connecttimeoutms",
740
+ "heartbeatfrequencyms",
741
+ "maxidletimems",
742
+ "maxstalenessseconds",
743
+ "serverselectiontimeoutms",
744
+ "sockettimeoutms",
745
+ "waitqueuetimeoutms",
746
+ ]
747
+
748
+
749
+ _AUTH_OPTIONS = frozenset(["authmechanismproperties"])
750
+
751
+
752
+ def validate_auth_option(option: str, value: Any) -> Tuple[str, Any]:
753
+ """Validate optional authentication parameters."""
754
+ lower, value = validate(option, value)
755
+ if lower not in _AUTH_OPTIONS:
756
+ raise ConfigurationError("Unknown authentication option: %s" % (option,))
757
+ return option, value
758
+
759
+
760
+ def validate(option: str, value: Any) -> Tuple[str, Any]:
761
+ """Generic validation function."""
762
+ lower = option.lower()
763
+ validator = VALIDATORS.get(lower, raise_config_error)
764
+ value = validator(option, value)
765
+ return option, value
766
+
767
+
768
+ def get_validated_options(
769
+ options: Mapping[str, Any], warn: bool = True
770
+ ) -> MutableMapping[str, Any]:
771
+ """Validate each entry in options and raise a warning if it is not valid.
772
+ Returns a copy of options with invalid entries removed.
773
+
774
+ :Parameters:
775
+ - `opts`: A dict containing MongoDB URI options.
776
+ - `warn` (optional): If ``True`` then warnings will be logged and
777
+ invalid options will be ignored. Otherwise, invalid options will
778
+ cause errors.
779
+ """
780
+ validated_options: MutableMapping[str, Any]
781
+ if isinstance(options, _CaseInsensitiveDictionary):
782
+ validated_options = _CaseInsensitiveDictionary()
783
+ get_normed_key = lambda x: x # noqa: E731
784
+ get_setter_key = lambda x: options.cased_key(x) # noqa: E731
785
+ else:
786
+ validated_options = {}
787
+ get_normed_key = lambda x: x.lower() # noqa: E731
788
+ get_setter_key = lambda x: x # noqa: E731
789
+
790
+ for opt, value in options.items():
791
+ normed_key = get_normed_key(opt)
792
+ try:
793
+ validator = URI_OPTIONS_VALIDATOR_MAP.get(normed_key, raise_config_error)
794
+ value = validator(opt, value)
795
+ except (ValueError, TypeError, ConfigurationError) as exc:
796
+ if warn:
797
+ warnings.warn(str(exc))
798
+ else:
799
+ raise
800
+ else:
801
+ validated_options[get_setter_key(normed_key)] = value
802
+ return validated_options
803
+
804
+
805
+ def _esc_coll_name(encrypted_fields, name):
806
+ return encrypted_fields.get("escCollection", f"enxcol_.{name}.esc")
807
+
808
+
809
+ def _ecc_coll_name(encrypted_fields, name):
810
+ return encrypted_fields.get("eccCollection", f"enxcol_.{name}.ecc")
811
+
812
+
813
+ def _ecoc_coll_name(encrypted_fields, name):
814
+ return encrypted_fields.get("ecocCollection", f"enxcol_.{name}.ecoc")
815
+
816
+
817
+ # List of write-concern-related options.
818
+ WRITE_CONCERN_OPTIONS = frozenset(["w", "wtimeout", "wtimeoutms", "fsync", "j", "journal"])
819
+
820
+
821
+ class BaseObject(object):
822
+ """A base class that provides attributes and methods common
823
+ to multiple pymongo classes.
824
+
825
+ SHOULD NOT BE USED BY DEVELOPERS EXTERNAL TO MONGODB.
826
+ """
827
+
828
+ def __init__(
829
+ self,
830
+ codec_options: CodecOptions,
831
+ read_preference: _ServerMode,
832
+ write_concern: WriteConcern,
833
+ read_concern: ReadConcern,
834
+ ) -> None:
835
+ if not isinstance(codec_options, CodecOptions):
836
+ raise TypeError("codec_options must be an instance of bson.codec_options.CodecOptions")
837
+ self.__codec_options = codec_options
838
+
839
+ if not isinstance(read_preference, _ServerMode):
840
+ raise TypeError(
841
+ "%r is not valid for read_preference. See "
842
+ "pymongo.read_preferences for valid "
843
+ "options." % (read_preference,)
844
+ )
845
+ self.__read_preference = read_preference
846
+
847
+ if not isinstance(write_concern, WriteConcern):
848
+ raise TypeError(
849
+ "write_concern must be an instance of pymongo.write_concern.WriteConcern"
850
+ )
851
+ self.__write_concern = write_concern
852
+
853
+ if not isinstance(read_concern, ReadConcern):
854
+ raise TypeError("read_concern must be an instance of pymongo.read_concern.ReadConcern")
855
+ self.__read_concern = read_concern
856
+
857
+ @property
858
+ def codec_options(self) -> CodecOptions:
859
+ """Read only access to the :class:`~bson.codec_options.CodecOptions`
860
+ of this instance.
861
+ """
862
+ return self.__codec_options
863
+
864
+ @property
865
+ def write_concern(self) -> WriteConcern:
866
+ """Read only access to the :class:`~pymongo.write_concern.WriteConcern`
867
+ of this instance.
868
+
869
+ .. versionchanged:: 3.0
870
+ The :attr:`write_concern` attribute is now read only.
871
+ """
872
+ return self.__write_concern
873
+
874
+ def _write_concern_for(self, session):
875
+ """Read only access to the write concern of this instance or session."""
876
+ # Override this operation's write concern with the transaction's.
877
+ if session and session.in_transaction:
878
+ return DEFAULT_WRITE_CONCERN
879
+ return self.write_concern
880
+
881
+ @property
882
+ def read_preference(self) -> _ServerMode:
883
+ """Read only access to the read preference of this instance.
884
+
885
+ .. versionchanged:: 3.0
886
+ The :attr:`read_preference` attribute is now read only.
887
+ """
888
+ return self.__read_preference
889
+
890
+ def _read_preference_for(self, session):
891
+ """Read only access to the read preference of this instance or session."""
892
+ # Override this operation's read preference with the transaction's.
893
+ if session:
894
+ return session._txn_read_preference() or self.__read_preference
895
+ return self.__read_preference
896
+
897
+ @property
898
+ def read_concern(self) -> ReadConcern:
899
+ """Read only access to the :class:`~pymongo.read_concern.ReadConcern`
900
+ of this instance.
901
+
902
+ .. versionadded:: 3.2
903
+ """
904
+ return self.__read_concern
905
+
906
+
907
+ class _CaseInsensitiveDictionary(abc.MutableMapping):
908
+ def __init__(self, *args, **kwargs):
909
+ self.__casedkeys = {}
910
+ self.__data = {}
911
+ self.update(dict(*args, **kwargs))
912
+
913
+ def __contains__(self, key):
914
+ return key.lower() in self.__data
915
+
916
+ def __len__(self):
917
+ return len(self.__data)
918
+
919
+ def __iter__(self):
920
+ return (key for key in self.__casedkeys)
921
+
922
+ def __repr__(self):
923
+ return str({self.__casedkeys[k]: self.__data[k] for k in self})
924
+
925
+ def __setitem__(self, key, value):
926
+ lc_key = key.lower()
927
+ self.__casedkeys[lc_key] = key
928
+ self.__data[lc_key] = value
929
+
930
+ def __getitem__(self, key):
931
+ return self.__data[key.lower()]
932
+
933
+ def __delitem__(self, key):
934
+ lc_key = key.lower()
935
+ del self.__casedkeys[lc_key]
936
+ del self.__data[lc_key]
937
+
938
+ def __eq__(self, other):
939
+ if not isinstance(other, abc.Mapping):
940
+ return NotImplemented
941
+ if len(self) != len(other):
942
+ return False
943
+ for key in other:
944
+ if self[key] != other[key]:
945
+ return False
946
+
947
+ return True
948
+
949
+ def get(self, key, default=None):
950
+ return self.__data.get(key.lower(), default)
951
+
952
+ def pop(self, key, *args, **kwargs):
953
+ lc_key = key.lower()
954
+ self.__casedkeys.pop(lc_key, None)
955
+ return self.__data.pop(lc_key, *args, **kwargs)
956
+
957
+ def popitem(self):
958
+ lc_key, cased_key = self.__casedkeys.popitem()
959
+ value = self.__data.pop(lc_key)
960
+ return cased_key, value
961
+
962
+ def clear(self):
963
+ self.__casedkeys.clear()
964
+ self.__data.clear()
965
+
966
+ def setdefault(self, key, default=None):
967
+ lc_key = key.lower()
968
+ if key in self:
969
+ return self.__data[lc_key]
970
+ else:
971
+ self.__casedkeys[lc_key] = key
972
+ self.__data[lc_key] = default
973
+ return default
974
+
975
+ def update(self, other):
976
+ if isinstance(other, _CaseInsensitiveDictionary):
977
+ for key in other:
978
+ self[other.cased_key(key)] = other[key]
979
+ else:
980
+ for key in other:
981
+ self[key] = other[key]
982
+
983
+ def cased_key(self, key):
984
+ return self.__casedkeys[key.lower()]