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,2144 @@
1
+ # Copyright 2009-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
+ """Tools for connecting to MongoDB.
16
+
17
+ .. seealso:: :doc:`/examples/high_availability` for examples of connecting
18
+ to replica sets or sets of mongos servers.
19
+
20
+ To get a :class:`~pymongo.database.Database` instance from a
21
+ :class:`MongoClient` use either dictionary-style or attribute-style
22
+ access:
23
+
24
+ .. doctest::
25
+
26
+ >>> from pymongo import MongoClient
27
+ >>> c = MongoClient()
28
+ >>> c.test_database
29
+ Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'test_database')
30
+ >>> c['test-database']
31
+ Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'test-database')
32
+ """
33
+
34
+ import contextlib
35
+ import threading
36
+ import weakref
37
+ from collections import defaultdict
38
+ from typing import (
39
+ TYPE_CHECKING,
40
+ Any,
41
+ Dict,
42
+ FrozenSet,
43
+ Generic,
44
+ List,
45
+ Mapping,
46
+ NoReturn,
47
+ Optional,
48
+ Sequence,
49
+ Set,
50
+ Tuple,
51
+ Type,
52
+ Union,
53
+ cast,
54
+ )
55
+
56
+ from bson.codec_options import DEFAULT_CODEC_OPTIONS, CodecOptions, TypeRegistry
57
+ from bson.son import SON
58
+ from bson.timestamp import Timestamp
59
+ from pymongo import (
60
+ _csot,
61
+ client_session,
62
+ common,
63
+ database,
64
+ helpers,
65
+ message,
66
+ periodic_executor,
67
+ uri_parser,
68
+ )
69
+ from pymongo.change_stream import ChangeStream, ClusterChangeStream
70
+ from pymongo.client_options import ClientOptions
71
+ from pymongo.client_session import _EmptyServerSession
72
+ from pymongo.command_cursor import CommandCursor
73
+ from pymongo.errors import (
74
+ AutoReconnect,
75
+ BulkWriteError,
76
+ ConfigurationError,
77
+ ConnectionFailure,
78
+ InvalidOperation,
79
+ NotPrimaryError,
80
+ OperationFailure,
81
+ PyMongoError,
82
+ ServerSelectionTimeoutError,
83
+ WaitQueueTimeoutError,
84
+ )
85
+ from pymongo.pool import ConnectionClosedReason
86
+ from pymongo.read_preferences import ReadPreference, _ServerMode
87
+ from pymongo.server_selectors import writable_server_selector
88
+ from pymongo.server_type import SERVER_TYPE
89
+ from pymongo.settings import TopologySettings
90
+ from pymongo.topology import Topology, _ErrorContext
91
+ from pymongo.topology_description import TOPOLOGY_TYPE, TopologyDescription
92
+ from pymongo.typings import _Address, _CollationIn, _DocumentType, _Pipeline
93
+ from pymongo.uri_parser import (
94
+ _check_options,
95
+ _handle_option_deprecations,
96
+ _handle_security_options,
97
+ _normalize_options,
98
+ )
99
+ from pymongo.write_concern import DEFAULT_WRITE_CONCERN, WriteConcern
100
+
101
+ if TYPE_CHECKING:
102
+ import sys
103
+
104
+ from pymongo.read_concern import ReadConcern
105
+
106
+ if sys.version_info[:2] >= (3, 9):
107
+ from collections.abc import Generator
108
+ else:
109
+ # Deprecated since version 3.9: collections.abc.Generator now supports [].
110
+ from typing import Generator
111
+
112
+
113
+ class MongoClient(common.BaseObject, Generic[_DocumentType]):
114
+ """
115
+ A client-side representation of a MongoDB cluster.
116
+
117
+ Instances can represent either a standalone MongoDB server, a replica
118
+ set, or a sharded cluster. Instances of this class are responsible for
119
+ maintaining up-to-date state of the cluster, and possibly cache
120
+ resources related to this, including background threads for monitoring,
121
+ and connection pools.
122
+ """
123
+
124
+ HOST = "localhost"
125
+ PORT = 27017
126
+ # Define order to retrieve options from ClientOptions for __repr__.
127
+ # No host/port; these are retrieved from TopologySettings.
128
+ _constructor_args = ("document_class", "tz_aware", "connect")
129
+
130
+ def __init__(
131
+ self,
132
+ host: Optional[Union[str, Sequence[str]]] = None,
133
+ port: Optional[int] = None,
134
+ document_class: Optional[Type[_DocumentType]] = None,
135
+ tz_aware: Optional[bool] = None,
136
+ connect: Optional[bool] = None,
137
+ type_registry: Optional[TypeRegistry] = None,
138
+ **kwargs: Any,
139
+ ) -> None:
140
+ """Client for a MongoDB instance, a replica set, or a set of mongoses.
141
+
142
+ .. warning:: Starting in PyMongo 4.0, ``directConnection`` now has a default value of
143
+ False instead of None.
144
+ For more details, see the relevant section of the PyMongo 4.x migration guide:
145
+ :ref:`pymongo4-migration-direct-connection`.
146
+
147
+ The client object is thread-safe and has connection-pooling built in.
148
+ If an operation fails because of a network error,
149
+ :class:`~pymongo.errors.ConnectionFailure` is raised and the client
150
+ reconnects in the background. Application code should handle this
151
+ exception (recognizing that the operation failed) and then continue to
152
+ execute.
153
+
154
+ The `host` parameter can be a full `mongodb URI
155
+ <http://dochub.mongodb.org/core/connections>`_, in addition to
156
+ a simple hostname. It can also be a list of hostnames but no more
157
+ than one URI. Any port specified in the host string(s) will override
158
+ the `port` parameter. For username and
159
+ passwords reserved characters like ':', '/', '+' and '@' must be
160
+ percent encoded following RFC 2396::
161
+
162
+ from urllib.parse import quote_plus
163
+
164
+ uri = "mongodb://%s:%s@%s" % (
165
+ quote_plus(user), quote_plus(password), host)
166
+ client = MongoClient(uri)
167
+
168
+ Unix domain sockets are also supported. The socket path must be percent
169
+ encoded in the URI::
170
+
171
+ uri = "mongodb://%s:%s@%s" % (
172
+ quote_plus(user), quote_plus(password), quote_plus(socket_path))
173
+ client = MongoClient(uri)
174
+
175
+ But not when passed as a simple hostname::
176
+
177
+ client = MongoClient('/tmp/mongodb-27017.sock')
178
+
179
+ Starting with version 3.6, PyMongo supports mongodb+srv:// URIs. The
180
+ URI must include one, and only one, hostname. The hostname will be
181
+ resolved to one or more DNS `SRV records
182
+ <https://en.wikipedia.org/wiki/SRV_record>`_ which will be used
183
+ as the seed list for connecting to the MongoDB deployment. When using
184
+ SRV URIs, the `authSource` and `replicaSet` configuration options can
185
+ be specified using `TXT records
186
+ <https://en.wikipedia.org/wiki/TXT_record>`_. See the
187
+ `Initial DNS Seedlist Discovery spec
188
+ <https://github.com/mongodb/specifications/blob/master/source/
189
+ initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.rst>`_
190
+ for more details. Note that the use of SRV URIs implicitly enables
191
+ TLS support. Pass tls=false in the URI to override.
192
+
193
+ .. note:: MongoClient creation will block waiting for answers from
194
+ DNS when mongodb+srv:// URIs are used.
195
+
196
+ .. note:: Starting with version 3.0 the :class:`MongoClient`
197
+ constructor no longer blocks while connecting to the server or
198
+ servers, and it no longer raises
199
+ :class:`~pymongo.errors.ConnectionFailure` if they are
200
+ unavailable, nor :class:`~pymongo.errors.ConfigurationError`
201
+ if the user's credentials are wrong. Instead, the constructor
202
+ returns immediately and launches the connection process on
203
+ background threads. You can check if the server is available
204
+ like this::
205
+
206
+ from pymongo.errors import ConnectionFailure
207
+ client = MongoClient()
208
+ try:
209
+ # The ping command is cheap and does not require auth.
210
+ client.admin.command('ping')
211
+ except ConnectionFailure:
212
+ print("Server not available")
213
+
214
+ .. warning:: When using PyMongo in a multiprocessing context, please
215
+ read :ref:`multiprocessing` first.
216
+
217
+ .. note:: Many of the following options can be passed using a MongoDB
218
+ URI or keyword parameters. If the same option is passed in a URI and
219
+ as a keyword parameter the keyword parameter takes precedence.
220
+
221
+ :Parameters:
222
+ - `host` (optional): hostname or IP address or Unix domain socket
223
+ path of a single mongod or mongos instance to connect to, or a
224
+ mongodb URI, or a list of hostnames (but no more than one mongodb
225
+ URI). If `host` is an IPv6 literal it must be enclosed in '['
226
+ and ']' characters
227
+ following the RFC2732 URL syntax (e.g. '[::1]' for localhost).
228
+ Multihomed and round robin DNS addresses are **not** supported.
229
+ - `port` (optional): port number on which to connect
230
+ - `document_class` (optional): default class to use for
231
+ documents returned from queries on this client
232
+ - `tz_aware` (optional): if ``True``,
233
+ :class:`~datetime.datetime` instances returned as values
234
+ in a document by this :class:`MongoClient` will be timezone
235
+ aware (otherwise they will be naive)
236
+ - `connect` (optional): if ``True`` (the default), immediately
237
+ begin connecting to MongoDB in the background. Otherwise connect
238
+ on the first operation.
239
+ - `type_registry` (optional): instance of
240
+ :class:`~bson.codec_options.TypeRegistry` to enable encoding
241
+ and decoding of custom types.
242
+
243
+ | **Other optional parameters can be passed as keyword arguments:**
244
+
245
+ - `directConnection` (optional): if ``True``, forces this client to
246
+ connect directly to the specified MongoDB host as a standalone.
247
+ If ``false``, the client connects to the entire replica set of
248
+ which the given MongoDB host(s) is a part. If this is ``True``
249
+ and a mongodb+srv:// URI or a URI containing multiple seeds is
250
+ provided, an exception will be raised.
251
+ - `maxPoolSize` (optional): The maximum allowable number of
252
+ concurrent connections to each connected server. Requests to a
253
+ server will block if there are `maxPoolSize` outstanding
254
+ connections to the requested server. Defaults to 100. Can be
255
+ either 0 or None, in which case there is no limit on the number
256
+ of concurrent connections.
257
+ - `minPoolSize` (optional): The minimum required number of concurrent
258
+ connections that the pool will maintain to each connected server.
259
+ Default is 0.
260
+ - `maxIdleTimeMS` (optional): The maximum number of milliseconds that
261
+ a connection can remain idle in the pool before being removed and
262
+ replaced. Defaults to `None` (no limit).
263
+ - `maxConnecting` (optional): The maximum number of connections that
264
+ each pool can establish concurrently. Defaults to `2`.
265
+ - `timeoutMS`: (integer or None) Controls how long (in
266
+ milliseconds) the driver will wait when executing an operation
267
+ (including retry attempts) before raising a timeout error.
268
+ ``0`` or ``None`` means no timeout.
269
+ - `socketTimeoutMS`: (integer or None) Controls how long (in
270
+ milliseconds) the driver will wait for a response after sending an
271
+ ordinary (non-monitoring) database operation before concluding that
272
+ a network error has occurred. ``0`` or ``None`` means no timeout.
273
+ Defaults to ``None`` (no timeout).
274
+ - `connectTimeoutMS`: (integer or None) Controls how long (in
275
+ milliseconds) the driver will wait during server monitoring when
276
+ connecting a new socket to a server before concluding the server
277
+ is unavailable. ``0`` or ``None`` means no timeout.
278
+ Defaults to ``20000`` (20 seconds).
279
+ - `server_selector`: (callable or None) Optional, user-provided
280
+ function that augments server selection rules. The function should
281
+ accept as an argument a list of
282
+ :class:`~pymongo.server_description.ServerDescription` objects and
283
+ return a list of server descriptions that should be considered
284
+ suitable for the desired operation.
285
+ - `serverSelectionTimeoutMS`: (integer) Controls how long (in
286
+ milliseconds) the driver will wait to find an available,
287
+ appropriate server to carry out a database operation; while it is
288
+ waiting, multiple server monitoring operations may be carried out,
289
+ each controlled by `connectTimeoutMS`. Defaults to ``30000`` (30
290
+ seconds).
291
+ - `waitQueueTimeoutMS`: (integer or None) How long (in milliseconds)
292
+ a thread will wait for a socket from the pool if the pool has no
293
+ free sockets. Defaults to ``None`` (no timeout).
294
+ - `heartbeatFrequencyMS`: (optional) The number of milliseconds
295
+ between periodic server checks, or None to accept the default
296
+ frequency of 10 seconds.
297
+ - `appname`: (string or None) The name of the application that
298
+ created this MongoClient instance. The server will log this value
299
+ upon establishing each connection. It is also recorded in the slow
300
+ query log and profile collections.
301
+ - `driver`: (pair or None) A driver implemented on top of PyMongo can
302
+ pass a :class:`~pymongo.driver_info.DriverInfo` to add its name,
303
+ version, and platform to the message printed in the server log when
304
+ establishing a connection.
305
+ - `event_listeners`: a list or tuple of event listeners. See
306
+ :mod:`~pymongo.monitoring` for details.
307
+ - `retryWrites`: (boolean) Whether supported write operations
308
+ executed within this MongoClient will be retried once after a
309
+ network error. Defaults to ``True``.
310
+ The supported write operations are:
311
+
312
+ - :meth:`~pymongo.collection.Collection.bulk_write`, as long as
313
+ :class:`~pymongo.operations.UpdateMany` or
314
+ :class:`~pymongo.operations.DeleteMany` are not included.
315
+ - :meth:`~pymongo.collection.Collection.delete_one`
316
+ - :meth:`~pymongo.collection.Collection.insert_one`
317
+ - :meth:`~pymongo.collection.Collection.insert_many`
318
+ - :meth:`~pymongo.collection.Collection.replace_one`
319
+ - :meth:`~pymongo.collection.Collection.update_one`
320
+ - :meth:`~pymongo.collection.Collection.find_one_and_delete`
321
+ - :meth:`~pymongo.collection.Collection.find_one_and_replace`
322
+ - :meth:`~pymongo.collection.Collection.find_one_and_update`
323
+
324
+ Unsupported write operations include, but are not limited to,
325
+ :meth:`~pymongo.collection.Collection.aggregate` using the ``$out``
326
+ pipeline operator and any operation with an unacknowledged write
327
+ concern (e.g. {w: 0})). See
328
+ https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.rst
329
+ - `retryReads`: (boolean) Whether supported read operations
330
+ executed within this MongoClient will be retried once after a
331
+ network error. Defaults to ``True``.
332
+ The supported read operations are:
333
+ :meth:`~pymongo.collection.Collection.find`,
334
+ :meth:`~pymongo.collection.Collection.find_one`,
335
+ :meth:`~pymongo.collection.Collection.aggregate` without ``$out``,
336
+ :meth:`~pymongo.collection.Collection.distinct`,
337
+ :meth:`~pymongo.collection.Collection.count`,
338
+ :meth:`~pymongo.collection.Collection.estimated_document_count`,
339
+ :meth:`~pymongo.collection.Collection.count_documents`,
340
+ :meth:`pymongo.collection.Collection.watch`,
341
+ :meth:`~pymongo.collection.Collection.list_indexes`,
342
+ :meth:`pymongo.database.Database.watch`,
343
+ :meth:`~pymongo.database.Database.list_collections`,
344
+ :meth:`pymongo.mongo_client.MongoClient.watch`,
345
+ and :meth:`~pymongo.mongo_client.MongoClient.list_databases`.
346
+
347
+ Unsupported read operations include, but are not limited to
348
+ :meth:`~pymongo.database.Database.command` and any getMore
349
+ operation on a cursor.
350
+
351
+ Enabling retryable reads makes applications more resilient to
352
+ transient errors such as network failures, database upgrades, and
353
+ replica set failovers. For an exact definition of which errors
354
+ trigger a retry, see the `retryable reads specification
355
+ <https://github.com/mongodb/specifications/blob/master/source/retryable-reads/retryable-reads.rst>`_.
356
+
357
+ - `compressors`: Comma separated list of compressors for wire
358
+ protocol compression. The list is used to negotiate a compressor
359
+ with the server. Currently supported options are "snappy", "zlib"
360
+ and "zstd". Support for snappy requires the
361
+ `python-snappy <https://pypi.org/project/python-snappy/>`_ package.
362
+ zlib support requires the Python standard library zlib module. zstd
363
+ requires the `zstandard <https://pypi.org/project/zstandard/>`_
364
+ package. By default no compression is used. Compression support
365
+ must also be enabled on the server. MongoDB 3.6+ supports snappy
366
+ and zlib compression. MongoDB 4.2+ adds support for zstd.
367
+ - `zlibCompressionLevel`: (int) The zlib compression level to use
368
+ when zlib is used as the wire protocol compressor. Supported values
369
+ are -1 through 9. -1 tells the zlib library to use its default
370
+ compression level (usually 6). 0 means no compression. 1 is best
371
+ speed. 9 is best compression. Defaults to -1.
372
+ - `uuidRepresentation`: The BSON representation to use when encoding
373
+ from and decoding to instances of :class:`~uuid.UUID`. Valid
374
+ values are the strings: "standard", "pythonLegacy", "javaLegacy",
375
+ "csharpLegacy", and "unspecified" (the default). New applications
376
+ should consider setting this to "standard" for cross language
377
+ compatibility. See :ref:`handling-uuid-data-example` for details.
378
+ - `unicode_decode_error_handler`: The error handler to apply when
379
+ a Unicode-related error occurs during BSON decoding that would
380
+ otherwise raise :exc:`UnicodeDecodeError`. Valid options include
381
+ 'strict', 'replace', 'backslashreplace', 'surrogateescape', and
382
+ 'ignore'. Defaults to 'strict'.
383
+ - `srvServiceName`: (string) The SRV service name to use for
384
+ "mongodb+srv://" URIs. Defaults to "mongodb". Use it like so::
385
+
386
+ MongoClient("mongodb+srv://example.com/?srvServiceName=customname")
387
+
388
+
389
+ | **Write Concern options:**
390
+ | (Only set if passed. No default values.)
391
+
392
+ - `w`: (integer or string) If this is a replica set, write operations
393
+ will block until they have been replicated to the specified number
394
+ or tagged set of servers. `w=<int>` always includes the replica set
395
+ primary (e.g. w=3 means write to the primary and wait until
396
+ replicated to **two** secondaries). Passing w=0 **disables write
397
+ acknowledgement** and all other write concern options.
398
+ - `wTimeoutMS`: (integer) Used in conjunction with `w`. Specify a value
399
+ in milliseconds to control how long to wait for write propagation
400
+ to complete. If replication does not complete in the given
401
+ timeframe, a timeout exception is raised. Passing wTimeoutMS=0
402
+ will cause **write operations to wait indefinitely**.
403
+ - `journal`: If ``True`` block until write operations have been
404
+ committed to the journal. Cannot be used in combination with
405
+ `fsync`. Write operations will fail with an exception if this
406
+ option is used when the server is running without journaling.
407
+ - `fsync`: If ``True`` and the server is running without journaling,
408
+ blocks until the server has synced all data files to disk. If the
409
+ server is running with journaling, this acts the same as the `j`
410
+ option, blocking until write operations have been committed to the
411
+ journal. Cannot be used in combination with `j`.
412
+
413
+ | **Replica set keyword arguments for connecting with a replica set
414
+ - either directly or via a mongos:**
415
+
416
+ - `replicaSet`: (string or None) The name of the replica set to
417
+ connect to. The driver will verify that all servers it connects to
418
+ match this name. Implies that the hosts specified are a seed list
419
+ and the driver should attempt to find all members of the set.
420
+ Defaults to ``None``.
421
+
422
+ | **Read Preference:**
423
+
424
+ - `readPreference`: The replica set read preference for this client.
425
+ One of ``primary``, ``primaryPreferred``, ``secondary``,
426
+ ``secondaryPreferred``, or ``nearest``. Defaults to ``primary``.
427
+ - `readPreferenceTags`: Specifies a tag set as a comma-separated list
428
+ of colon-separated key-value pairs. For example ``dc:ny,rack:1``.
429
+ Defaults to ``None``.
430
+ - `maxStalenessSeconds`: (integer) The maximum estimated
431
+ length of time a replica set secondary can fall behind the primary
432
+ in replication before it will no longer be selected for operations.
433
+ Defaults to ``-1``, meaning no maximum. If maxStalenessSeconds
434
+ is set, it must be a positive integer greater than or equal to
435
+ 90 seconds.
436
+
437
+ .. seealso:: :doc:`/examples/server_selection`
438
+
439
+ | **Authentication:**
440
+
441
+ - `username`: A string.
442
+ - `password`: A string.
443
+
444
+ Although username and password must be percent-escaped in a MongoDB
445
+ URI, they must not be percent-escaped when passed as parameters. In
446
+ this example, both the space and slash special characters are passed
447
+ as-is::
448
+
449
+ MongoClient(username="user name", password="pass/word")
450
+
451
+ - `authSource`: The database to authenticate on. Defaults to the
452
+ database specified in the URI, if provided, or to "admin".
453
+ - `authMechanism`: See :data:`~pymongo.auth.MECHANISMS` for options.
454
+ If no mechanism is specified, PyMongo automatically SCRAM-SHA-1
455
+ when connected to MongoDB 3.6 and negotiates the mechanism to use
456
+ (SCRAM-SHA-1 or SCRAM-SHA-256) when connected to MongoDB 4.0+.
457
+ - `authMechanismProperties`: Used to specify authentication mechanism
458
+ specific options. To specify the service name for GSSAPI
459
+ authentication pass authMechanismProperties='SERVICE_NAME:<service
460
+ name>'.
461
+ To specify the session token for MONGODB-AWS authentication pass
462
+ ``authMechanismProperties='AWS_SESSION_TOKEN:<session token>'``.
463
+
464
+ .. seealso:: :doc:`/examples/authentication`
465
+
466
+ | **TLS/SSL configuration:**
467
+
468
+ - `tls`: (boolean) If ``True``, create the connection to the server
469
+ using transport layer security. Defaults to ``False``.
470
+ - `tlsInsecure`: (boolean) Specify whether TLS constraints should be
471
+ relaxed as much as possible. Setting ``tlsInsecure=True`` implies
472
+ ``tlsAllowInvalidCertificates=True`` and
473
+ ``tlsAllowInvalidHostnames=True``. Defaults to ``False``. Think
474
+ very carefully before setting this to ``True`` as it dramatically
475
+ reduces the security of TLS.
476
+ - `tlsAllowInvalidCertificates`: (boolean) If ``True``, continues
477
+ the TLS handshake regardless of the outcome of the certificate
478
+ verification process. If this is ``False``, and a value is not
479
+ provided for ``tlsCAFile``, PyMongo will attempt to load system
480
+ provided CA certificates. If the python version in use does not
481
+ support loading system CA certificates then the ``tlsCAFile``
482
+ parameter must point to a file of CA certificates.
483
+ ``tlsAllowInvalidCertificates=False`` implies ``tls=True``.
484
+ Defaults to ``False``. Think very carefully before setting this
485
+ to ``True`` as that could make your application vulnerable to
486
+ on-path attackers.
487
+ - `tlsAllowInvalidHostnames`: (boolean) If ``True``, disables TLS
488
+ hostname verification. ``tlsAllowInvalidHostnames=False`` implies
489
+ ``tls=True``. Defaults to ``False``. Think very carefully before
490
+ setting this to ``True`` as that could make your application
491
+ vulnerable to on-path attackers.
492
+ - `tlsCAFile`: A file containing a single or a bundle of
493
+ "certification authority" certificates, which are used to validate
494
+ certificates passed from the other end of the connection.
495
+ Implies ``tls=True``. Defaults to ``None``.
496
+ - `tlsCertificateKeyFile`: A file containing the client certificate
497
+ and private key. Implies ``tls=True``. Defaults to ``None``.
498
+ - `tlsCRLFile`: A file containing a PEM or DER formatted
499
+ certificate revocation list. Only supported by python 2.7.9+
500
+ (pypy 2.5.1+). Implies ``tls=True``. Defaults to ``None``.
501
+ - `tlsCertificateKeyFilePassword`: The password or passphrase for
502
+ decrypting the private key in ``tlsCertificateKeyFile``. Only
503
+ necessary if the private key is encrypted. Only supported by
504
+ python 2.7.9+ (pypy 2.5.1+) and 3.3+. Defaults to ``None``.
505
+ - `tlsDisableOCSPEndpointCheck`: (boolean) If ``True``, disables
506
+ certificate revocation status checking via the OCSP responder
507
+ specified on the server certificate.
508
+ ``tlsDisableOCSPEndpointCheck=False`` implies ``tls=True``.
509
+ Defaults to ``False``.
510
+ - `ssl`: (boolean) Alias for ``tls``.
511
+
512
+ | **Read Concern options:**
513
+ | (If not set explicitly, this will use the server default)
514
+
515
+ - `readConcernLevel`: (string) The read concern level specifies the
516
+ level of isolation for read operations. For example, a read
517
+ operation using a read concern level of ``majority`` will only
518
+ return data that has been written to a majority of nodes. If the
519
+ level is left unspecified, the server default will be used.
520
+
521
+ | **Client side encryption options:**
522
+ | (If not set explicitly, client side encryption will not be enabled.)
523
+
524
+ - `auto_encryption_opts`: A
525
+ :class:`~pymongo.encryption_options.AutoEncryptionOpts` which
526
+ configures this client to automatically encrypt collection commands
527
+ and automatically decrypt results. See
528
+ :ref:`automatic-client-side-encryption` for an example.
529
+ If a :class:`MongoClient` is configured with
530
+ ``auto_encryption_opts`` and a non-None ``maxPoolSize``, a
531
+ separate internal ``MongoClient`` is created if any of the
532
+ following are true:
533
+
534
+ - A ``key_vault_client`` is not passed to
535
+ :class:`~pymongo.encryption_options.AutoEncryptionOpts`
536
+ - ``bypass_auto_encrpytion=False`` is passed to
537
+ :class:`~pymongo.encryption_options.AutoEncryptionOpts`
538
+
539
+ | **Stable API options:**
540
+ | (If not set explicitly, Stable API will not be enabled.)
541
+
542
+ - `server_api`: A
543
+ :class:`~pymongo.server_api.ServerApi` which configures this
544
+ client to use Stable API. See :ref:`versioned-api-ref` for
545
+ details.
546
+
547
+ .. seealso:: The MongoDB documentation on `connections <https://dochub.mongodb.org/core/connections>`_.
548
+
549
+ .. versionchanged:: 4.2
550
+ Added the ``timeoutMS`` keyword argument.
551
+
552
+ .. versionchanged:: 4.0
553
+
554
+ - Removed the fsync, unlock, is_locked, database_names, and
555
+ close_cursor methods.
556
+ See the :ref:`pymongo4-migration-guide`.
557
+ - Removed the ``waitQueueMultiple`` and ``socketKeepAlive``
558
+ keyword arguments.
559
+ - The default for `uuidRepresentation` was changed from
560
+ ``pythonLegacy`` to ``unspecified``.
561
+ - Added the ``srvServiceName`` and ``maxConnecting`` URI and
562
+ keyword argument.
563
+
564
+ .. versionchanged:: 3.12
565
+ Added the ``server_api`` keyword argument.
566
+ The following keyword arguments were deprecated:
567
+
568
+ - ``ssl_certfile`` and ``ssl_keyfile`` were deprecated in favor
569
+ of ``tlsCertificateKeyFile``.
570
+
571
+ .. versionchanged:: 3.11
572
+ Added the following keyword arguments and URI options:
573
+
574
+ - ``tlsDisableOCSPEndpointCheck``
575
+ - ``directConnection``
576
+
577
+ .. versionchanged:: 3.9
578
+ Added the ``retryReads`` keyword argument and URI option.
579
+ Added the ``tlsInsecure`` keyword argument and URI option.
580
+ The following keyword arguments and URI options were deprecated:
581
+
582
+ - ``wTimeout`` was deprecated in favor of ``wTimeoutMS``.
583
+ - ``j`` was deprecated in favor of ``journal``.
584
+ - ``ssl_cert_reqs`` was deprecated in favor of
585
+ ``tlsAllowInvalidCertificates``.
586
+ - ``ssl_match_hostname`` was deprecated in favor of
587
+ ``tlsAllowInvalidHostnames``.
588
+ - ``ssl_ca_certs`` was deprecated in favor of ``tlsCAFile``.
589
+ - ``ssl_certfile`` was deprecated in favor of
590
+ ``tlsCertificateKeyFile``.
591
+ - ``ssl_crlfile`` was deprecated in favor of ``tlsCRLFile``.
592
+ - ``ssl_pem_passphrase`` was deprecated in favor of
593
+ ``tlsCertificateKeyFilePassword``.
594
+
595
+ .. versionchanged:: 3.9
596
+ ``retryWrites`` now defaults to ``True``.
597
+
598
+ .. versionchanged:: 3.8
599
+ Added the ``server_selector`` keyword argument.
600
+ Added the ``type_registry`` keyword argument.
601
+
602
+ .. versionchanged:: 3.7
603
+ Added the ``driver`` keyword argument.
604
+
605
+ .. versionchanged:: 3.6
606
+ Added support for mongodb+srv:// URIs.
607
+ Added the ``retryWrites`` keyword argument and URI option.
608
+
609
+ .. versionchanged:: 3.5
610
+ Add ``username`` and ``password`` options. Document the
611
+ ``authSource``, ``authMechanism``, and ``authMechanismProperties``
612
+ options.
613
+ Deprecated the ``socketKeepAlive`` keyword argument and URI option.
614
+ ``socketKeepAlive`` now defaults to ``True``.
615
+
616
+ .. versionchanged:: 3.0
617
+ :class:`~pymongo.mongo_client.MongoClient` is now the one and only
618
+ client class for a standalone server, mongos, or replica set.
619
+ It includes the functionality that had been split into
620
+ :class:`~pymongo.mongo_client.MongoReplicaSetClient`: it can connect
621
+ to a replica set, discover all its members, and monitor the set for
622
+ stepdowns, elections, and reconfigs.
623
+
624
+ The :class:`~pymongo.mongo_client.MongoClient` constructor no
625
+ longer blocks while connecting to the server or servers, and it no
626
+ longer raises :class:`~pymongo.errors.ConnectionFailure` if they
627
+ are unavailable, nor :class:`~pymongo.errors.ConfigurationError`
628
+ if the user's credentials are wrong. Instead, the constructor
629
+ returns immediately and launches the connection process on
630
+ background threads.
631
+
632
+ Therefore the ``alive`` method is removed since it no longer
633
+ provides meaningful information; even if the client is disconnected,
634
+ it may discover a server in time to fulfill the next operation.
635
+
636
+ In PyMongo 2.x, :class:`~pymongo.MongoClient` accepted a list of
637
+ standalone MongoDB servers and used the first it could connect to::
638
+
639
+ MongoClient(['host1.com:27017', 'host2.com:27017'])
640
+
641
+ A list of multiple standalones is no longer supported; if multiple
642
+ servers are listed they must be members of the same replica set, or
643
+ mongoses in the same sharded cluster.
644
+
645
+ The behavior for a list of mongoses is changed from "high
646
+ availability" to "load balancing". Before, the client connected to
647
+ the lowest-latency mongos in the list, and used it until a network
648
+ error prompted it to re-evaluate all mongoses' latencies and
649
+ reconnect to one of them. In PyMongo 3, the client monitors its
650
+ network latency to all the mongoses continuously, and distributes
651
+ operations evenly among those with the lowest latency. See
652
+ :ref:`mongos-load-balancing` for more information.
653
+
654
+ The ``connect`` option is added.
655
+
656
+ The ``start_request``, ``in_request``, and ``end_request`` methods
657
+ are removed, as well as the ``auto_start_request`` option.
658
+
659
+ The ``copy_database`` method is removed, see the
660
+ :doc:`copy_database examples </examples/copydb>` for alternatives.
661
+
662
+ The :meth:`MongoClient.disconnect` method is removed; it was a
663
+ synonym for :meth:`~pymongo.MongoClient.close`.
664
+
665
+ :class:`~pymongo.mongo_client.MongoClient` no longer returns an
666
+ instance of :class:`~pymongo.database.Database` for attribute names
667
+ with leading underscores. You must use dict-style lookups instead::
668
+
669
+ client['__my_database__']
670
+
671
+ Not::
672
+
673
+ client.__my_database__
674
+ """
675
+ doc_class = document_class or dict
676
+ self.__init_kwargs: Dict[str, Any] = {
677
+ "host": host,
678
+ "port": port,
679
+ "document_class": doc_class,
680
+ "tz_aware": tz_aware,
681
+ "connect": connect,
682
+ "type_registry": type_registry,
683
+ **kwargs,
684
+ }
685
+
686
+ if host is None:
687
+ host = self.HOST
688
+ if isinstance(host, str):
689
+ host = [host]
690
+ if port is None:
691
+ port = self.PORT
692
+ if not isinstance(port, int):
693
+ raise TypeError("port must be an instance of int")
694
+
695
+ # _pool_class, _monitor_class, and _condition_class are for deep
696
+ # customization of PyMongo, e.g. Motor.
697
+ pool_class = kwargs.pop("_pool_class", None)
698
+ monitor_class = kwargs.pop("_monitor_class", None)
699
+ condition_class = kwargs.pop("_condition_class", None)
700
+
701
+ # Parse options passed as kwargs.
702
+ keyword_opts = common._CaseInsensitiveDictionary(kwargs)
703
+ keyword_opts["document_class"] = doc_class
704
+
705
+ seeds = set()
706
+ username = None
707
+ password = None
708
+ dbase = None
709
+ opts = common._CaseInsensitiveDictionary()
710
+ fqdn = None
711
+ srv_service_name = keyword_opts.get("srvservicename")
712
+ srv_max_hosts = keyword_opts.get("srvmaxhosts")
713
+ if len([h for h in host if "/" in h]) > 1:
714
+ raise ConfigurationError("host must not contain multiple MongoDB URIs")
715
+ for entity in host:
716
+ # A hostname can only include a-z, 0-9, '-' and '.'. If we find a '/'
717
+ # it must be a URI,
718
+ # https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names
719
+ if "/" in entity:
720
+ # Determine connection timeout from kwargs.
721
+ timeout = keyword_opts.get("connecttimeoutms")
722
+ if timeout is not None:
723
+ timeout = common.validate_timeout_or_none_or_zero(
724
+ keyword_opts.cased_key("connecttimeoutms"), timeout
725
+ )
726
+ res = uri_parser.parse_uri(
727
+ entity,
728
+ port,
729
+ validate=True,
730
+ warn=True,
731
+ normalize=False,
732
+ connect_timeout=timeout,
733
+ srv_service_name=srv_service_name,
734
+ srv_max_hosts=srv_max_hosts,
735
+ )
736
+ seeds.update(res["nodelist"])
737
+ username = res["username"] or username
738
+ password = res["password"] or password
739
+ dbase = res["database"] or dbase
740
+ opts = res["options"]
741
+ fqdn = res["fqdn"]
742
+ else:
743
+ seeds.update(uri_parser.split_hosts(entity, port))
744
+ if not seeds:
745
+ raise ConfigurationError("need to specify at least one host")
746
+
747
+ # Add options with named keyword arguments to the parsed kwarg options.
748
+ if type_registry is not None:
749
+ keyword_opts["type_registry"] = type_registry
750
+ if tz_aware is None:
751
+ tz_aware = opts.get("tz_aware", False)
752
+ if connect is None:
753
+ connect = opts.get("connect", True)
754
+ keyword_opts["tz_aware"] = tz_aware
755
+ keyword_opts["connect"] = connect
756
+
757
+ # Handle deprecated options in kwarg options.
758
+ keyword_opts = _handle_option_deprecations(keyword_opts)
759
+ # Validate kwarg options.
760
+ keyword_opts = common._CaseInsensitiveDictionary(
761
+ dict(common.validate(keyword_opts.cased_key(k), v) for k, v in keyword_opts.items())
762
+ )
763
+
764
+ # Override connection string options with kwarg options.
765
+ opts.update(keyword_opts)
766
+
767
+ if srv_service_name is None:
768
+ srv_service_name = opts.get("srvServiceName", common.SRV_SERVICE_NAME)
769
+
770
+ srv_max_hosts = srv_max_hosts or opts.get("srvmaxhosts")
771
+ # Handle security-option conflicts in combined options.
772
+ opts = _handle_security_options(opts)
773
+ # Normalize combined options.
774
+ opts = _normalize_options(opts)
775
+ _check_options(seeds, opts)
776
+
777
+ # Username and password passed as kwargs override user info in URI.
778
+ username = opts.get("username", username)
779
+ password = opts.get("password", password)
780
+ self.__options = options = ClientOptions(username, password, dbase, opts)
781
+
782
+ self.__default_database_name = dbase
783
+ self.__lock = threading.Lock()
784
+ self.__kill_cursors_queue: List = []
785
+
786
+ self._event_listeners = options.pool_options._event_listeners
787
+ super(MongoClient, self).__init__(
788
+ options.codec_options,
789
+ options.read_preference,
790
+ options.write_concern,
791
+ options.read_concern,
792
+ )
793
+
794
+ self._topology_settings = TopologySettings(
795
+ seeds=seeds,
796
+ replica_set_name=options.replica_set_name,
797
+ pool_class=pool_class,
798
+ pool_options=options.pool_options,
799
+ monitor_class=monitor_class,
800
+ condition_class=condition_class,
801
+ local_threshold_ms=options.local_threshold_ms,
802
+ server_selection_timeout=options.server_selection_timeout,
803
+ server_selector=options.server_selector,
804
+ heartbeat_frequency=options.heartbeat_frequency,
805
+ fqdn=fqdn,
806
+ direct_connection=options.direct_connection,
807
+ load_balanced=options.load_balanced,
808
+ srv_service_name=srv_service_name,
809
+ srv_max_hosts=srv_max_hosts,
810
+ )
811
+
812
+ self._topology = Topology(self._topology_settings)
813
+
814
+ def target():
815
+ client = self_ref()
816
+ if client is None:
817
+ return False # Stop the executor.
818
+ MongoClient._process_periodic_tasks(client)
819
+ return True
820
+
821
+ executor = periodic_executor.PeriodicExecutor(
822
+ interval=common.KILL_CURSOR_FREQUENCY,
823
+ min_interval=common.MIN_HEARTBEAT_INTERVAL,
824
+ target=target,
825
+ name="pymongo_kill_cursors_thread",
826
+ )
827
+
828
+ # We strongly reference the executor and it weakly references us via
829
+ # this closure. When the client is freed, stop the executor soon.
830
+ self_ref: Any = weakref.ref(self, executor.close)
831
+ self._kill_cursors_executor = executor
832
+
833
+ if connect:
834
+ self._get_topology()
835
+
836
+ self._encrypter = None
837
+ if self.__options.auto_encryption_opts:
838
+ from pymongo.encryption import _Encrypter
839
+
840
+ self._encrypter = _Encrypter(self, self.__options.auto_encryption_opts)
841
+ self._timeout = options.timeout
842
+
843
+ def _duplicate(self, **kwargs):
844
+ args = self.__init_kwargs.copy()
845
+ args.update(kwargs)
846
+ return MongoClient(**args)
847
+
848
+ def _server_property(self, attr_name):
849
+ """An attribute of the current server's description.
850
+
851
+ If the client is not connected, this will block until a connection is
852
+ established or raise ServerSelectionTimeoutError if no server is
853
+ available.
854
+
855
+ Not threadsafe if used multiple times in a single method, since
856
+ the server may change. In such cases, store a local reference to a
857
+ ServerDescription first, then use its properties.
858
+ """
859
+ server = self._topology.select_server(writable_server_selector)
860
+
861
+ return getattr(server.description, attr_name)
862
+
863
+ def watch(
864
+ self,
865
+ pipeline: Optional[_Pipeline] = None,
866
+ full_document: Optional[str] = None,
867
+ resume_after: Optional[Mapping[str, Any]] = None,
868
+ max_await_time_ms: Optional[int] = None,
869
+ batch_size: Optional[int] = None,
870
+ collation: Optional[_CollationIn] = None,
871
+ start_at_operation_time: Optional[Timestamp] = None,
872
+ session: Optional[client_session.ClientSession] = None,
873
+ start_after: Optional[Mapping[str, Any]] = None,
874
+ comment: Optional[Any] = None,
875
+ full_document_before_change: Optional[str] = None,
876
+ ) -> ChangeStream[_DocumentType]:
877
+ """Watch changes on this cluster.
878
+
879
+ Performs an aggregation with an implicit initial ``$changeStream``
880
+ stage and returns a
881
+ :class:`~pymongo.change_stream.ClusterChangeStream` cursor which
882
+ iterates over changes on all databases on this cluster.
883
+
884
+ Introduced in MongoDB 4.0.
885
+
886
+ .. code-block:: python
887
+
888
+ with client.watch() as stream:
889
+ for change in stream:
890
+ print(change)
891
+
892
+ The :class:`~pymongo.change_stream.ClusterChangeStream` iterable
893
+ blocks until the next change document is returned or an error is
894
+ raised. If the
895
+ :meth:`~pymongo.change_stream.ClusterChangeStream.next` method
896
+ encounters a network error when retrieving a batch from the server,
897
+ it will automatically attempt to recreate the cursor such that no
898
+ change events are missed. Any error encountered during the resume
899
+ attempt indicates there may be an outage and will be raised.
900
+
901
+ .. code-block:: python
902
+
903
+ try:
904
+ with client.watch(
905
+ [{'$match': {'operationType': 'insert'}}]) as stream:
906
+ for insert_change in stream:
907
+ print(insert_change)
908
+ except pymongo.errors.PyMongoError:
909
+ # The ChangeStream encountered an unrecoverable error or the
910
+ # resume attempt failed to recreate the cursor.
911
+ logging.error('...')
912
+
913
+ For a precise description of the resume process see the
914
+ `change streams specification`_.
915
+
916
+ :Parameters:
917
+ - `pipeline` (optional): A list of aggregation pipeline stages to
918
+ append to an initial ``$changeStream`` stage. Not all
919
+ pipeline stages are valid after a ``$changeStream`` stage, see the
920
+ MongoDB documentation on change streams for the supported stages.
921
+ - `full_document` (optional): The fullDocument to pass as an option
922
+ to the ``$changeStream`` stage. Allowed values: 'updateLookup',
923
+ 'whenAvailable', 'required'. When set to 'updateLookup', the
924
+ change notification for partial updates will include both a delta
925
+ describing the changes to the document, as well as a copy of the
926
+ entire document that was changed from some time after the change
927
+ occurred.
928
+ - `full_document_before_change`: Allowed values: 'whenAvailable'
929
+ and 'required'. Change events may now result in a
930
+ 'fullDocumentBeforeChange' response field.
931
+ - `resume_after` (optional): A resume token. If provided, the
932
+ change stream will start returning changes that occur directly
933
+ after the operation specified in the resume token. A resume token
934
+ is the _id value of a change document.
935
+ - `max_await_time_ms` (optional): The maximum time in milliseconds
936
+ for the server to wait for changes before responding to a getMore
937
+ operation.
938
+ - `batch_size` (optional): The maximum number of documents to return
939
+ per batch.
940
+ - `collation` (optional): The :class:`~pymongo.collation.Collation`
941
+ to use for the aggregation.
942
+ - `start_at_operation_time` (optional): If provided, the resulting
943
+ change stream will only return changes that occurred at or after
944
+ the specified :class:`~bson.timestamp.Timestamp`. Requires
945
+ MongoDB >= 4.0.
946
+ - `session` (optional): a
947
+ :class:`~pymongo.client_session.ClientSession`.
948
+ - `start_after` (optional): The same as `resume_after` except that
949
+ `start_after` can resume notifications after an invalidate event.
950
+ This option and `resume_after` are mutually exclusive.
951
+ - `comment` (optional): A user-provided comment to attach to this
952
+ command.
953
+
954
+ :Returns:
955
+ A :class:`~pymongo.change_stream.ClusterChangeStream` cursor.
956
+
957
+ .. versionchanged:: 4.2
958
+ Added ``full_document_before_change`` parameter.
959
+
960
+ .. versionchanged:: 4.1
961
+ Added ``comment`` parameter.
962
+
963
+ .. versionchanged:: 3.9
964
+ Added the ``start_after`` parameter.
965
+
966
+ .. versionadded:: 3.7
967
+
968
+ .. seealso:: The MongoDB documentation on `changeStreams <https://mongodb.com/docs/manual/changeStreams/>`_.
969
+
970
+ .. _change streams specification:
971
+ https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst
972
+ """
973
+ return ClusterChangeStream(
974
+ self.admin,
975
+ pipeline,
976
+ full_document,
977
+ resume_after,
978
+ max_await_time_ms,
979
+ batch_size,
980
+ collation,
981
+ start_at_operation_time,
982
+ session,
983
+ start_after,
984
+ comment,
985
+ full_document_before_change,
986
+ )
987
+
988
+ @property
989
+ def topology_description(self) -> TopologyDescription:
990
+ """The description of the connected MongoDB deployment.
991
+
992
+ >>> client.topology_description
993
+ <TopologyDescription id: 605a7b04e76489833a7c6113, topology_type: ReplicaSetWithPrimary, servers: [<ServerDescription ('localhost', 27017) server_type: RSPrimary, rtt: 0.0007973677999995488>, <ServerDescription ('localhost', 27018) server_type: RSSecondary, rtt: 0.0005540556000003249>, <ServerDescription ('localhost', 27019) server_type: RSSecondary, rtt: 0.0010367483999999649>]>
994
+ >>> client.topology_description.topology_type_name
995
+ 'ReplicaSetWithPrimary'
996
+
997
+ Note that the description is periodically updated in the background
998
+ but the returned object itself is immutable. Access this property again
999
+ to get a more recent
1000
+ :class:`~pymongo.topology_description.TopologyDescription`.
1001
+
1002
+ :Returns:
1003
+ An instance of
1004
+ :class:`~pymongo.topology_description.TopologyDescription`.
1005
+
1006
+ .. versionadded:: 4.0
1007
+ """
1008
+ return self._topology.description
1009
+
1010
+ @property
1011
+ def address(self) -> Optional[Tuple[str, int]]:
1012
+ """(host, port) of the current standalone, primary, or mongos, or None.
1013
+
1014
+ Accessing :attr:`address` raises :exc:`~.errors.InvalidOperation` if
1015
+ the client is load-balancing among mongoses, since there is no single
1016
+ address. Use :attr:`nodes` instead.
1017
+
1018
+ If the client is not connected, this will block until a connection is
1019
+ established or raise ServerSelectionTimeoutError if no server is
1020
+ available.
1021
+
1022
+ .. versionadded:: 3.0
1023
+ """
1024
+ topology_type = self._topology._description.topology_type
1025
+ if (
1026
+ topology_type == TOPOLOGY_TYPE.Sharded
1027
+ and len(self.topology_description.server_descriptions()) > 1
1028
+ ):
1029
+ raise InvalidOperation(
1030
+ 'Cannot use "address" property when load balancing among'
1031
+ ' mongoses, use "nodes" instead.'
1032
+ )
1033
+ if topology_type not in (
1034
+ TOPOLOGY_TYPE.ReplicaSetWithPrimary,
1035
+ TOPOLOGY_TYPE.Single,
1036
+ TOPOLOGY_TYPE.LoadBalanced,
1037
+ TOPOLOGY_TYPE.Sharded,
1038
+ ):
1039
+ return None
1040
+ return self._server_property("address")
1041
+
1042
+ @property
1043
+ def primary(self) -> Optional[Tuple[str, int]]:
1044
+ """The (host, port) of the current primary of the replica set.
1045
+
1046
+ Returns ``None`` if this client is not connected to a replica set,
1047
+ there is no primary, or this client was created without the
1048
+ `replicaSet` option.
1049
+
1050
+ .. versionadded:: 3.0
1051
+ MongoClient gained this property in version 3.0.
1052
+ """
1053
+ return self._topology.get_primary()
1054
+
1055
+ @property
1056
+ def secondaries(self) -> Set[Tuple[str, int]]:
1057
+ """The secondary members known to this client.
1058
+
1059
+ A sequence of (host, port) pairs. Empty if this client is not
1060
+ connected to a replica set, there are no visible secondaries, or this
1061
+ client was created without the `replicaSet` option.
1062
+
1063
+ .. versionadded:: 3.0
1064
+ MongoClient gained this property in version 3.0.
1065
+ """
1066
+ return self._topology.get_secondaries()
1067
+
1068
+ @property
1069
+ def arbiters(self) -> Set[Tuple[str, int]]:
1070
+ """Arbiters in the replica set.
1071
+
1072
+ A sequence of (host, port) pairs. Empty if this client is not
1073
+ connected to a replica set, there are no arbiters, or this client was
1074
+ created without the `replicaSet` option.
1075
+ """
1076
+ return self._topology.get_arbiters()
1077
+
1078
+ @property
1079
+ def is_primary(self) -> bool:
1080
+ """If this client is connected to a server that can accept writes.
1081
+
1082
+ True if the current server is a standalone, mongos, or the primary of
1083
+ a replica set. If the client is not connected, this will block until a
1084
+ connection is established or raise ServerSelectionTimeoutError if no
1085
+ server is available.
1086
+ """
1087
+ return self._server_property("is_writable")
1088
+
1089
+ @property
1090
+ def is_mongos(self) -> bool:
1091
+ """If this client is connected to mongos. If the client is not
1092
+ connected, this will block until a connection is established or raise
1093
+ ServerSelectionTimeoutError if no server is available.
1094
+ """
1095
+ return self._server_property("server_type") == SERVER_TYPE.Mongos
1096
+
1097
+ @property
1098
+ def nodes(self) -> FrozenSet[_Address]:
1099
+ """Set of all currently connected servers.
1100
+
1101
+ .. warning:: When connected to a replica set the value of :attr:`nodes`
1102
+ can change over time as :class:`MongoClient`'s view of the replica
1103
+ set changes. :attr:`nodes` can also be an empty set when
1104
+ :class:`MongoClient` is first instantiated and hasn't yet connected
1105
+ to any servers, or a network partition causes it to lose connection
1106
+ to all servers.
1107
+ """
1108
+ description = self._topology.description
1109
+ return frozenset(s.address for s in description.known_servers)
1110
+
1111
+ @property
1112
+ def options(self) -> ClientOptions:
1113
+ """The configuration options for this client.
1114
+
1115
+ :Returns:
1116
+ An instance of :class:`~pymongo.client_options.ClientOptions`.
1117
+
1118
+ .. versionadded:: 4.0
1119
+ """
1120
+ return self.__options
1121
+
1122
+ def _end_sessions(self, session_ids):
1123
+ """Send endSessions command(s) with the given session ids."""
1124
+ try:
1125
+ # Use SocketInfo.command directly to avoid implicitly creating
1126
+ # another session.
1127
+ with self._socket_for_reads(ReadPreference.PRIMARY_PREFERRED, None) as (
1128
+ sock_info,
1129
+ read_pref,
1130
+ ):
1131
+ if not sock_info.supports_sessions:
1132
+ return
1133
+
1134
+ for i in range(0, len(session_ids), common._MAX_END_SESSIONS):
1135
+ spec = SON([("endSessions", session_ids[i : i + common._MAX_END_SESSIONS])])
1136
+ sock_info.command("admin", spec, read_preference=read_pref, client=self)
1137
+ except PyMongoError:
1138
+ # Drivers MUST ignore any errors returned by the endSessions
1139
+ # command.
1140
+ pass
1141
+
1142
+ def close(self) -> None:
1143
+ """Cleanup client resources and disconnect from MongoDB.
1144
+
1145
+ End all server sessions created by this client by sending one or more
1146
+ endSessions commands.
1147
+
1148
+ Close all sockets in the connection pools and stop the monitor threads.
1149
+
1150
+ .. versionchanged:: 4.0
1151
+ Once closed, the client cannot be used again and any attempt will
1152
+ raise :exc:`~pymongo.errors.InvalidOperation`.
1153
+
1154
+ .. versionchanged:: 3.6
1155
+ End all server sessions created by this client.
1156
+ """
1157
+ session_ids = self._topology.pop_all_sessions()
1158
+ if session_ids:
1159
+ self._end_sessions(session_ids)
1160
+ # Stop the periodic task thread and then send pending killCursor
1161
+ # requests before closing the topology.
1162
+ self._kill_cursors_executor.close()
1163
+ self._process_kill_cursors()
1164
+ self._topology.close()
1165
+ if self._encrypter:
1166
+ # TODO: PYTHON-1921 Encrypted MongoClients cannot be re-opened.
1167
+ self._encrypter.close()
1168
+
1169
+ def _get_topology(self):
1170
+ """Get the internal :class:`~pymongo.topology.Topology` object.
1171
+
1172
+ If this client was created with "connect=False", calling _get_topology
1173
+ launches the connection process in the background.
1174
+ """
1175
+ self._topology.open()
1176
+ with self.__lock:
1177
+ self._kill_cursors_executor.open()
1178
+ return self._topology
1179
+
1180
+ @contextlib.contextmanager
1181
+ def _get_socket(self, server, session):
1182
+ in_txn = session and session.in_transaction
1183
+ with _MongoClientErrorHandler(self, server, session) as err_handler:
1184
+ # Reuse the pinned connection, if it exists.
1185
+ if in_txn and session._pinned_connection:
1186
+ err_handler.contribute_socket(session._pinned_connection)
1187
+ yield session._pinned_connection
1188
+ return
1189
+ with server.get_socket(handler=err_handler) as sock_info:
1190
+ # Pin this session to the selected server or connection.
1191
+ if in_txn and server.description.server_type in (
1192
+ SERVER_TYPE.Mongos,
1193
+ SERVER_TYPE.LoadBalancer,
1194
+ ):
1195
+ session._pin(server, sock_info)
1196
+ err_handler.contribute_socket(sock_info)
1197
+ if (
1198
+ self._encrypter
1199
+ and not self._encrypter._bypass_auto_encryption
1200
+ and sock_info.max_wire_version < 8
1201
+ ):
1202
+ raise ConfigurationError(
1203
+ "Auto-encryption requires a minimum MongoDB version of 4.2"
1204
+ )
1205
+ yield sock_info
1206
+
1207
+ def _select_server(self, server_selector, session, address=None):
1208
+ """Select a server to run an operation on this client.
1209
+
1210
+ :Parameters:
1211
+ - `server_selector`: The server selector to use if the session is
1212
+ not pinned and no address is given.
1213
+ - `session`: The ClientSession for the next operation, or None. May
1214
+ be pinned to a mongos server address.
1215
+ - `address` (optional): Address when sending a message
1216
+ to a specific server, used for getMore.
1217
+ """
1218
+ try:
1219
+ topology = self._get_topology()
1220
+ if session and not session.in_transaction:
1221
+ session._transaction.reset()
1222
+ address = address or (session and session._pinned_address)
1223
+ if address:
1224
+ # We're running a getMore or this session is pinned to a mongos.
1225
+ server = topology.select_server_by_address(address)
1226
+ if not server:
1227
+ raise AutoReconnect("server %s:%d no longer available" % address)
1228
+ else:
1229
+ server = topology.select_server(server_selector)
1230
+ return server
1231
+ except PyMongoError as exc:
1232
+ # Server selection errors in a transaction are transient.
1233
+ if session and session.in_transaction:
1234
+ exc._add_error_label("TransientTransactionError")
1235
+ session._unpin()
1236
+ raise
1237
+
1238
+ def _socket_for_writes(self, session):
1239
+ server = self._select_server(writable_server_selector, session)
1240
+ return self._get_socket(server, session)
1241
+
1242
+ @contextlib.contextmanager
1243
+ def _socket_from_server(self, read_preference, server, session):
1244
+ assert read_preference is not None, "read_preference must not be None"
1245
+ # Get a socket for a server matching the read preference, and yield
1246
+ # sock_info with the effective read preference. The Server Selection
1247
+ # Spec says not to send any $readPreference to standalones and to
1248
+ # always send primaryPreferred when directly connected to a repl set
1249
+ # member.
1250
+ # Thread safe: if the type is single it cannot change.
1251
+ topology = self._get_topology()
1252
+ single = topology.description.topology_type == TOPOLOGY_TYPE.Single
1253
+
1254
+ with self._get_socket(server, session) as sock_info:
1255
+ if single:
1256
+ if sock_info.is_repl and not (session and session.in_transaction):
1257
+ # Use primary preferred to ensure any repl set member
1258
+ # can handle the request.
1259
+ read_preference = ReadPreference.PRIMARY_PREFERRED
1260
+ elif sock_info.is_standalone:
1261
+ # Don't send read preference to standalones.
1262
+ read_preference = ReadPreference.PRIMARY
1263
+ yield sock_info, read_preference
1264
+
1265
+ def _socket_for_reads(self, read_preference, session):
1266
+ assert read_preference is not None, "read_preference must not be None"
1267
+ _ = self._get_topology()
1268
+ server = self._select_server(read_preference, session)
1269
+ return self._socket_from_server(read_preference, server, session)
1270
+
1271
+ def _should_pin_cursor(self, session):
1272
+ return self.__options.load_balanced and not (session and session.in_transaction)
1273
+
1274
+ @_csot.apply
1275
+ def _run_operation(self, operation, unpack_res, address=None):
1276
+ """Run a _Query/_GetMore operation and return a Response.
1277
+
1278
+ :Parameters:
1279
+ - `operation`: a _Query or _GetMore object.
1280
+ - `unpack_res`: A callable that decodes the wire protocol response.
1281
+ - `address` (optional): Optional address when sending a message
1282
+ to a specific server, used for getMore.
1283
+ """
1284
+ if operation.sock_mgr:
1285
+ server = self._select_server(
1286
+ operation.read_preference, operation.session, address=address
1287
+ )
1288
+
1289
+ with operation.sock_mgr.lock:
1290
+ with _MongoClientErrorHandler(self, server, operation.session) as err_handler:
1291
+ err_handler.contribute_socket(operation.sock_mgr.sock)
1292
+ return server.run_operation(
1293
+ operation.sock_mgr.sock, operation, True, self._event_listeners, unpack_res
1294
+ )
1295
+
1296
+ def _cmd(session, server, sock_info, read_preference):
1297
+ operation.reset() # Reset op in case of retry.
1298
+ return server.run_operation(
1299
+ sock_info, operation, read_preference, self._event_listeners, unpack_res
1300
+ )
1301
+
1302
+ return self._retryable_read(
1303
+ _cmd,
1304
+ operation.read_preference,
1305
+ operation.session,
1306
+ address=address,
1307
+ retryable=isinstance(operation, message._Query),
1308
+ )
1309
+
1310
+ def _retry_with_session(self, retryable, func, session, bulk):
1311
+ """Execute an operation with at most one consecutive retries
1312
+
1313
+ Returns func()'s return value on success. On error retries the same
1314
+ command once.
1315
+
1316
+ Re-raises any exception thrown by func().
1317
+ """
1318
+ retryable = (
1319
+ retryable and self.options.retry_writes and session and not session.in_transaction
1320
+ )
1321
+ return self._retry_internal(retryable, func, session, bulk)
1322
+
1323
+ @_csot.apply
1324
+ def _retry_internal(self, retryable, func, session, bulk):
1325
+ """Internal retryable write helper."""
1326
+ max_wire_version = 0
1327
+ last_error: Optional[Exception] = None
1328
+ retrying = False
1329
+ multiple_retries = _csot.get_timeout() is not None
1330
+
1331
+ def is_retrying():
1332
+ return bulk.retrying if bulk else retrying
1333
+
1334
+ # Increment the transaction id up front to ensure any retry attempt
1335
+ # will use the proper txnNumber, even if server or socket selection
1336
+ # fails before the command can be sent.
1337
+ if retryable and session and not session.in_transaction:
1338
+ session._start_retryable_write()
1339
+ if bulk:
1340
+ bulk.started_retryable_write = True
1341
+
1342
+ while True:
1343
+ if is_retrying():
1344
+ remaining = _csot.remaining()
1345
+ if remaining is not None and remaining <= 0:
1346
+ assert last_error is not None
1347
+ raise last_error
1348
+ try:
1349
+ server = self._select_server(writable_server_selector, session)
1350
+ supports_session = (
1351
+ session is not None and server.description.retryable_writes_supported
1352
+ )
1353
+ with self._get_socket(server, session) as sock_info:
1354
+ max_wire_version = sock_info.max_wire_version
1355
+ if retryable and not supports_session:
1356
+ if is_retrying():
1357
+ # A retry is not possible because this server does
1358
+ # not support sessions raise the last error.
1359
+ assert last_error is not None
1360
+ raise last_error
1361
+ retryable = False
1362
+ return func(session, sock_info, retryable)
1363
+ except ServerSelectionTimeoutError:
1364
+ if is_retrying():
1365
+ # The application may think the write was never attempted
1366
+ # if we raise ServerSelectionTimeoutError on the retry
1367
+ # attempt. Raise the original exception instead.
1368
+ assert last_error is not None
1369
+ raise last_error
1370
+ # A ServerSelectionTimeoutError error indicates that there may
1371
+ # be a persistent outage. Attempting to retry in this case will
1372
+ # most likely be a waste of time.
1373
+ raise
1374
+ except PyMongoError as exc:
1375
+ if not retryable:
1376
+ raise
1377
+ # Add the RetryableWriteError label, if applicable.
1378
+ _add_retryable_write_error(exc, max_wire_version)
1379
+ retryable_error = exc.has_error_label("RetryableWriteError")
1380
+ if retryable_error:
1381
+ session._unpin()
1382
+ if not retryable_error or (is_retrying() and not multiple_retries):
1383
+ raise
1384
+ if bulk:
1385
+ bulk.retrying = True
1386
+ else:
1387
+ retrying = True
1388
+ last_error = exc
1389
+
1390
+ @_csot.apply
1391
+ def _retryable_read(self, func, read_pref, session, address=None, retryable=True):
1392
+ """Execute an operation with at most one consecutive retries
1393
+
1394
+ Returns func()'s return value on success. On error retries the same
1395
+ command once.
1396
+
1397
+ Re-raises any exception thrown by func().
1398
+ """
1399
+ retryable = (
1400
+ retryable and self.options.retry_reads and not (session and session.in_transaction)
1401
+ )
1402
+ last_error: Optional[Exception] = None
1403
+ retrying = False
1404
+ multiple_retries = _csot.get_timeout() is not None
1405
+
1406
+ while True:
1407
+ if retrying:
1408
+ remaining = _csot.remaining()
1409
+ if remaining is not None and remaining <= 0:
1410
+ assert last_error is not None
1411
+ raise last_error
1412
+ try:
1413
+ server = self._select_server(read_pref, session, address=address)
1414
+ with self._socket_from_server(read_pref, server, session) as (sock_info, read_pref):
1415
+ if retrying and not retryable:
1416
+ # A retry is not possible because this server does
1417
+ # not support retryable reads, raise the last error.
1418
+ assert last_error is not None
1419
+ raise last_error
1420
+ return func(session, server, sock_info, read_pref)
1421
+ except ServerSelectionTimeoutError:
1422
+ if retrying:
1423
+ # The application may think the write was never attempted
1424
+ # if we raise ServerSelectionTimeoutError on the retry
1425
+ # attempt. Raise the original exception instead.
1426
+ assert last_error is not None
1427
+ raise last_error
1428
+ # A ServerSelectionTimeoutError error indicates that there may
1429
+ # be a persistent outage. Attempting to retry in this case will
1430
+ # most likely be a waste of time.
1431
+ raise
1432
+ except ConnectionFailure as exc:
1433
+ if not retryable or (retrying and not multiple_retries):
1434
+ raise
1435
+ retrying = True
1436
+ last_error = exc
1437
+ except OperationFailure as exc:
1438
+ if not retryable or (retrying and not multiple_retries):
1439
+ raise
1440
+ if exc.code not in helpers._RETRYABLE_ERROR_CODES:
1441
+ raise
1442
+ retrying = True
1443
+ last_error = exc
1444
+
1445
+ def _retryable_write(self, retryable, func, session):
1446
+ """Internal retryable write helper."""
1447
+ with self._tmp_session(session) as s:
1448
+ return self._retry_with_session(retryable, func, s, None)
1449
+
1450
+ def __eq__(self, other: Any) -> bool:
1451
+ if isinstance(other, self.__class__):
1452
+ return self._topology == other._topology
1453
+ return NotImplemented
1454
+
1455
+ def __ne__(self, other: Any) -> bool:
1456
+ return not self == other
1457
+
1458
+ def __hash__(self) -> int:
1459
+ return hash(self._topology)
1460
+
1461
+ def _repr_helper(self):
1462
+ def option_repr(option, value):
1463
+ """Fix options whose __repr__ isn't usable in a constructor."""
1464
+ if option == "document_class":
1465
+ if value is dict:
1466
+ return "document_class=dict"
1467
+ else:
1468
+ return "document_class=%s.%s" % (value.__module__, value.__name__)
1469
+ if option in common.TIMEOUT_OPTIONS and value is not None:
1470
+ return "%s=%s" % (option, int(value * 1000))
1471
+
1472
+ return "%s=%r" % (option, value)
1473
+
1474
+ # Host first...
1475
+ options = [
1476
+ "host=%r"
1477
+ % [
1478
+ "%s:%d" % (host, port) if port is not None else host
1479
+ for host, port in self._topology_settings.seeds
1480
+ ]
1481
+ ]
1482
+ # ... then everything in self._constructor_args...
1483
+ options.extend(
1484
+ option_repr(key, self.__options._options[key]) for key in self._constructor_args
1485
+ )
1486
+ # ... then everything else.
1487
+ options.extend(
1488
+ option_repr(key, self.__options._options[key])
1489
+ for key in self.__options._options
1490
+ if key not in set(self._constructor_args) and key != "username" and key != "password"
1491
+ )
1492
+ return ", ".join(options)
1493
+
1494
+ def __repr__(self):
1495
+ return "MongoClient(%s)" % (self._repr_helper(),)
1496
+
1497
+ def __getattr__(self, name: str) -> database.Database[_DocumentType]:
1498
+ """Get a database by name.
1499
+
1500
+ Raises :class:`~pymongo.errors.InvalidName` if an invalid
1501
+ database name is used.
1502
+
1503
+ :Parameters:
1504
+ - `name`: the name of the database to get
1505
+ """
1506
+ if name.startswith("_"):
1507
+ raise AttributeError(
1508
+ "MongoClient has no attribute %r. To access the %s"
1509
+ " database, use client[%r]." % (name, name, name)
1510
+ )
1511
+ return self.__getitem__(name)
1512
+
1513
+ def __getitem__(self, name: str) -> database.Database[_DocumentType]:
1514
+ """Get a database by name.
1515
+
1516
+ Raises :class:`~pymongo.errors.InvalidName` if an invalid
1517
+ database name is used.
1518
+
1519
+ :Parameters:
1520
+ - `name`: the name of the database to get
1521
+ """
1522
+ return database.Database(self, name)
1523
+
1524
+ def _cleanup_cursor(
1525
+ self, locks_allowed, cursor_id, address, sock_mgr, session, explicit_session
1526
+ ):
1527
+ """Cleanup a cursor from cursor.close() or __del__.
1528
+
1529
+ This method handles cleanup for Cursors/CommandCursors including any
1530
+ pinned connection or implicit session attached at the time the cursor
1531
+ was closed or garbage collected.
1532
+
1533
+ :Parameters:
1534
+ - `locks_allowed`: True if we are allowed to acquire locks.
1535
+ - `cursor_id`: The cursor id which may be 0.
1536
+ - `address`: The _CursorAddress.
1537
+ - `sock_mgr`: The _SocketManager for the pinned connection or None.
1538
+ - `session`: The cursor's session.
1539
+ - `explicit_session`: True if the session was passed explicitly.
1540
+ """
1541
+ if locks_allowed:
1542
+ if cursor_id:
1543
+ if sock_mgr and sock_mgr.more_to_come:
1544
+ # If this is an exhaust cursor and we haven't completely
1545
+ # exhausted the result set we *must* close the socket
1546
+ # to stop the server from sending more data.
1547
+ sock_mgr.sock.close_socket(ConnectionClosedReason.ERROR)
1548
+ else:
1549
+ self._close_cursor_now(cursor_id, address, session=session, sock_mgr=sock_mgr)
1550
+ if sock_mgr:
1551
+ sock_mgr.close()
1552
+ else:
1553
+ # The cursor will be closed later in a different session.
1554
+ if cursor_id or sock_mgr:
1555
+ self._close_cursor_soon(cursor_id, address, sock_mgr)
1556
+ if session and not explicit_session:
1557
+ session._end_session(lock=locks_allowed)
1558
+
1559
+ def _close_cursor_soon(self, cursor_id, address, sock_mgr=None):
1560
+ """Request that a cursor and/or connection be cleaned up soon."""
1561
+ self.__kill_cursors_queue.append((address, cursor_id, sock_mgr))
1562
+
1563
+ def _close_cursor_now(self, cursor_id, address=None, session=None, sock_mgr=None):
1564
+ """Send a kill cursors message with the given id.
1565
+
1566
+ The cursor is closed synchronously on the current thread.
1567
+ """
1568
+ if not isinstance(cursor_id, int):
1569
+ raise TypeError("cursor_id must be an instance of int")
1570
+
1571
+ try:
1572
+ if sock_mgr:
1573
+ with sock_mgr.lock:
1574
+ # Cursor is pinned to LB outside of a transaction.
1575
+ self._kill_cursor_impl([cursor_id], address, session, sock_mgr.sock)
1576
+ else:
1577
+ self._kill_cursors([cursor_id], address, self._get_topology(), session)
1578
+ except PyMongoError:
1579
+ # Make another attempt to kill the cursor later.
1580
+ self._close_cursor_soon(cursor_id, address)
1581
+
1582
+ def _kill_cursors(self, cursor_ids, address, topology, session):
1583
+ """Send a kill cursors message with the given ids."""
1584
+ if address:
1585
+ # address could be a tuple or _CursorAddress, but
1586
+ # select_server_by_address needs (host, port).
1587
+ server = topology.select_server_by_address(tuple(address))
1588
+ else:
1589
+ # Application called close_cursor() with no address.
1590
+ server = topology.select_server(writable_server_selector)
1591
+
1592
+ with self._get_socket(server, session) as sock_info:
1593
+ self._kill_cursor_impl(cursor_ids, address, session, sock_info)
1594
+
1595
+ def _kill_cursor_impl(self, cursor_ids, address, session, sock_info):
1596
+ namespace = address.namespace
1597
+ db, coll = namespace.split(".", 1)
1598
+ spec = SON([("killCursors", coll), ("cursors", cursor_ids)])
1599
+ sock_info.command(db, spec, session=session, client=self)
1600
+
1601
+ def _process_kill_cursors(self):
1602
+ """Process any pending kill cursors requests."""
1603
+ address_to_cursor_ids = defaultdict(list)
1604
+ pinned_cursors = []
1605
+
1606
+ # Other threads or the GC may append to the queue concurrently.
1607
+ while True:
1608
+ try:
1609
+ address, cursor_id, sock_mgr = self.__kill_cursors_queue.pop()
1610
+ except IndexError:
1611
+ break
1612
+
1613
+ if sock_mgr:
1614
+ pinned_cursors.append((address, cursor_id, sock_mgr))
1615
+ else:
1616
+ address_to_cursor_ids[address].append(cursor_id)
1617
+
1618
+ for address, cursor_id, sock_mgr in pinned_cursors:
1619
+ try:
1620
+ self._cleanup_cursor(True, cursor_id, address, sock_mgr, None, False)
1621
+ except Exception as exc:
1622
+ if isinstance(exc, InvalidOperation) and self._topology._closed:
1623
+ # Raise the exception when client is closed so that it
1624
+ # can be caught in _process_periodic_tasks
1625
+ raise
1626
+ else:
1627
+ helpers._handle_exception()
1628
+
1629
+ # Don't re-open topology if it's closed and there's no pending cursors.
1630
+ if address_to_cursor_ids:
1631
+ topology = self._get_topology()
1632
+ for address, cursor_ids in address_to_cursor_ids.items():
1633
+ try:
1634
+ self._kill_cursors(cursor_ids, address, topology, session=None)
1635
+ except Exception as exc:
1636
+ if isinstance(exc, InvalidOperation) and self._topology._closed:
1637
+ raise
1638
+ else:
1639
+ helpers._handle_exception()
1640
+
1641
+ # This method is run periodically by a background thread.
1642
+ def _process_periodic_tasks(self):
1643
+ """Process any pending kill cursors requests and
1644
+ maintain connection pool parameters."""
1645
+ try:
1646
+ self._process_kill_cursors()
1647
+ self._topology.update_pool()
1648
+ except Exception as exc:
1649
+ if isinstance(exc, InvalidOperation) and self._topology._closed:
1650
+ return
1651
+ else:
1652
+ helpers._handle_exception()
1653
+
1654
+ def __start_session(self, implicit, **kwargs):
1655
+ # Raises ConfigurationError if sessions are not supported.
1656
+ if implicit:
1657
+ self._topology._check_implicit_session_support()
1658
+ server_session = _EmptyServerSession()
1659
+ else:
1660
+ server_session = self._get_server_session()
1661
+ opts = client_session.SessionOptions(**kwargs)
1662
+ return client_session.ClientSession(self, server_session, opts, implicit)
1663
+
1664
+ def start_session(
1665
+ self,
1666
+ causal_consistency: Optional[bool] = None,
1667
+ default_transaction_options: Optional[client_session.TransactionOptions] = None,
1668
+ snapshot: Optional[bool] = False,
1669
+ ) -> client_session.ClientSession:
1670
+ """Start a logical session.
1671
+
1672
+ This method takes the same parameters as
1673
+ :class:`~pymongo.client_session.SessionOptions`. See the
1674
+ :mod:`~pymongo.client_session` module for details and examples.
1675
+
1676
+ A :class:`~pymongo.client_session.ClientSession` may only be used with
1677
+ the MongoClient that started it. :class:`ClientSession` instances are
1678
+ **not thread-safe or fork-safe**. They can only be used by one thread
1679
+ or process at a time. A single :class:`ClientSession` cannot be used
1680
+ to run multiple operations concurrently.
1681
+
1682
+ :Returns:
1683
+ An instance of :class:`~pymongo.client_session.ClientSession`.
1684
+
1685
+ .. versionadded:: 3.6
1686
+ """
1687
+ return self.__start_session(
1688
+ False,
1689
+ causal_consistency=causal_consistency,
1690
+ default_transaction_options=default_transaction_options,
1691
+ snapshot=snapshot,
1692
+ )
1693
+
1694
+ def _get_server_session(self):
1695
+ """Internal: start or resume a _ServerSession."""
1696
+ return self._topology.get_server_session()
1697
+
1698
+ def _return_server_session(self, server_session, lock):
1699
+ """Internal: return a _ServerSession to the pool."""
1700
+ if isinstance(server_session, _EmptyServerSession):
1701
+ return
1702
+ return self._topology.return_server_session(server_session, lock)
1703
+
1704
+ def _ensure_session(self, session=None):
1705
+ """If provided session is None, lend a temporary session."""
1706
+ if session:
1707
+ return session
1708
+
1709
+ try:
1710
+ # Don't make implicit sessions causally consistent. Applications
1711
+ # should always opt-in.
1712
+ return self.__start_session(True, causal_consistency=False)
1713
+ except (ConfigurationError, InvalidOperation):
1714
+ # Sessions not supported.
1715
+ return None
1716
+
1717
+ @contextlib.contextmanager
1718
+ def _tmp_session(
1719
+ self, session: Optional[client_session.ClientSession], close: bool = True
1720
+ ) -> "Generator[Optional[client_session.ClientSession], None, None]":
1721
+ """If provided session is None, lend a temporary session."""
1722
+ if session is not None:
1723
+ if not isinstance(session, client_session.ClientSession):
1724
+ raise ValueError("'session' argument must be a ClientSession or None.")
1725
+ # Don't call end_session.
1726
+ yield session
1727
+ return
1728
+
1729
+ s = self._ensure_session(session)
1730
+ if s:
1731
+ try:
1732
+ yield s
1733
+ except Exception as exc:
1734
+ if isinstance(exc, ConnectionFailure):
1735
+ s._server_session.mark_dirty()
1736
+
1737
+ # Always call end_session on error.
1738
+ s.end_session()
1739
+ raise
1740
+ finally:
1741
+ # Call end_session when we exit this scope.
1742
+ if close:
1743
+ s.end_session()
1744
+ else:
1745
+ yield None
1746
+
1747
+ def _send_cluster_time(self, command, session):
1748
+ topology_time = self._topology.max_cluster_time()
1749
+ session_time = session.cluster_time if session else None
1750
+ if topology_time and session_time:
1751
+ if topology_time["clusterTime"] > session_time["clusterTime"]:
1752
+ cluster_time = topology_time
1753
+ else:
1754
+ cluster_time = session_time
1755
+ else:
1756
+ cluster_time = topology_time or session_time
1757
+ if cluster_time:
1758
+ command["$clusterTime"] = cluster_time
1759
+
1760
+ def _process_response(self, reply, session):
1761
+ self._topology.receive_cluster_time(reply.get("$clusterTime"))
1762
+ if session is not None:
1763
+ session._process_response(reply)
1764
+
1765
+ def server_info(self, session: Optional[client_session.ClientSession] = None) -> Dict[str, Any]:
1766
+ """Get information about the MongoDB server we're connected to.
1767
+
1768
+ :Parameters:
1769
+ - `session` (optional): a
1770
+ :class:`~pymongo.client_session.ClientSession`.
1771
+
1772
+ .. versionchanged:: 3.6
1773
+ Added ``session`` parameter.
1774
+ """
1775
+ return cast(
1776
+ dict,
1777
+ self.admin.command(
1778
+ "buildinfo", read_preference=ReadPreference.PRIMARY, session=session
1779
+ ),
1780
+ )
1781
+
1782
+ def list_databases(
1783
+ self,
1784
+ session: Optional[client_session.ClientSession] = None,
1785
+ comment: Optional[Any] = None,
1786
+ **kwargs: Any,
1787
+ ) -> CommandCursor[Dict[str, Any]]:
1788
+ """Get a cursor over the databases of the connected server.
1789
+
1790
+ :Parameters:
1791
+ - `session` (optional): a
1792
+ :class:`~pymongo.client_session.ClientSession`.
1793
+ - `comment` (optional): A user-provided comment to attach to this
1794
+ command.
1795
+ - `**kwargs` (optional): Optional parameters of the
1796
+ `listDatabases command
1797
+ <https://mongodb.com/docs/manual/reference/command/listDatabases/>`_
1798
+ can be passed as keyword arguments to this method. The supported
1799
+ options differ by server version.
1800
+
1801
+
1802
+ :Returns:
1803
+ An instance of :class:`~pymongo.command_cursor.CommandCursor`.
1804
+
1805
+ .. versionadded:: 3.6
1806
+ """
1807
+ cmd = SON([("listDatabases", 1)])
1808
+ cmd.update(kwargs)
1809
+ if comment is not None:
1810
+ cmd["comment"] = comment
1811
+ admin = self._database_default_options("admin")
1812
+ res = admin._retryable_read_command(cmd, session=session)
1813
+ # listDatabases doesn't return a cursor (yet). Fake one.
1814
+ cursor = {
1815
+ "id": 0,
1816
+ "firstBatch": res["databases"],
1817
+ "ns": "admin.$cmd",
1818
+ }
1819
+ return CommandCursor(admin["$cmd"], cursor, None, comment=comment)
1820
+
1821
+ def list_database_names(
1822
+ self,
1823
+ session: Optional[client_session.ClientSession] = None,
1824
+ comment: Optional[Any] = None,
1825
+ ) -> List[str]:
1826
+ """Get a list of the names of all databases on the connected server.
1827
+
1828
+ :Parameters:
1829
+ - `session` (optional): a
1830
+ :class:`~pymongo.client_session.ClientSession`.
1831
+ - `comment` (optional): A user-provided comment to attach to this
1832
+ command.
1833
+
1834
+ .. versionchanged:: 4.1
1835
+ Added ``comment`` parameter.
1836
+
1837
+ .. versionadded:: 3.6
1838
+ """
1839
+ return [doc["name"] for doc in self.list_databases(session, nameOnly=True, comment=comment)]
1840
+
1841
+ @_csot.apply
1842
+ def drop_database(
1843
+ self,
1844
+ name_or_database: Union[str, database.Database],
1845
+ session: Optional[client_session.ClientSession] = None,
1846
+ comment: Optional[Any] = None,
1847
+ ) -> None:
1848
+ """Drop a database.
1849
+
1850
+ Raises :class:`TypeError` if `name_or_database` is not an instance of
1851
+ :class:`basestring` (:class:`str` in python 3) or
1852
+ :class:`~pymongo.database.Database`.
1853
+
1854
+ :Parameters:
1855
+ - `name_or_database`: the name of a database to drop, or a
1856
+ :class:`~pymongo.database.Database` instance representing the
1857
+ database to drop
1858
+ - `session` (optional): a
1859
+ :class:`~pymongo.client_session.ClientSession`.
1860
+ - `comment` (optional): A user-provided comment to attach to this
1861
+ command.
1862
+
1863
+ .. versionchanged:: 4.1
1864
+ Added ``comment`` parameter.
1865
+
1866
+ .. versionchanged:: 3.6
1867
+ Added ``session`` parameter.
1868
+
1869
+ .. note:: The :attr:`~pymongo.mongo_client.MongoClient.write_concern` of
1870
+ this client is automatically applied to this operation.
1871
+
1872
+ .. versionchanged:: 3.4
1873
+ Apply this client's write concern automatically to this operation
1874
+ when connected to MongoDB >= 3.4.
1875
+
1876
+ """
1877
+ name = name_or_database
1878
+ if isinstance(name, database.Database):
1879
+ name = name.name
1880
+
1881
+ if not isinstance(name, str):
1882
+ raise TypeError("name_or_database must be an instance of str or a Database")
1883
+
1884
+ with self._socket_for_writes(session) as sock_info:
1885
+ self[name]._command(
1886
+ sock_info,
1887
+ {"dropDatabase": 1, "comment": comment},
1888
+ read_preference=ReadPreference.PRIMARY,
1889
+ write_concern=self._write_concern_for(session),
1890
+ parse_write_concern_error=True,
1891
+ session=session,
1892
+ )
1893
+
1894
+ def get_default_database(
1895
+ self,
1896
+ default: Optional[str] = None,
1897
+ codec_options: Optional[CodecOptions] = None,
1898
+ read_preference: Optional[_ServerMode] = None,
1899
+ write_concern: Optional[WriteConcern] = None,
1900
+ read_concern: Optional["ReadConcern"] = None,
1901
+ ) -> database.Database[_DocumentType]:
1902
+ """Get the database named in the MongoDB connection URI.
1903
+
1904
+ >>> uri = 'mongodb://host/my_database'
1905
+ >>> client = MongoClient(uri)
1906
+ >>> db = client.get_default_database()
1907
+ >>> assert db.name == 'my_database'
1908
+ >>> db = client.get_database()
1909
+ >>> assert db.name == 'my_database'
1910
+
1911
+ Useful in scripts where you want to choose which database to use
1912
+ based only on the URI in a configuration file.
1913
+
1914
+ :Parameters:
1915
+ - `default` (optional): the database name to use if no database name
1916
+ was provided in the URI.
1917
+ - `codec_options` (optional): An instance of
1918
+ :class:`~bson.codec_options.CodecOptions`. If ``None`` (the
1919
+ default) the :attr:`codec_options` of this :class:`MongoClient` is
1920
+ used.
1921
+ - `read_preference` (optional): The read preference to use. If
1922
+ ``None`` (the default) the :attr:`read_preference` of this
1923
+ :class:`MongoClient` is used. See :mod:`~pymongo.read_preferences`
1924
+ for options.
1925
+ - `write_concern` (optional): An instance of
1926
+ :class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the
1927
+ default) the :attr:`write_concern` of this :class:`MongoClient` is
1928
+ used.
1929
+ - `read_concern` (optional): An instance of
1930
+ :class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the
1931
+ default) the :attr:`read_concern` of this :class:`MongoClient` is
1932
+ used.
1933
+ - `comment` (optional): A user-provided comment to attach to this
1934
+ command.
1935
+
1936
+ .. versionchanged:: 4.1
1937
+ Added ``comment`` parameter.
1938
+
1939
+ .. versionchanged:: 3.8
1940
+ Undeprecated. Added the ``default``, ``codec_options``,
1941
+ ``read_preference``, ``write_concern`` and ``read_concern``
1942
+ parameters.
1943
+
1944
+ .. versionchanged:: 3.5
1945
+ Deprecated, use :meth:`get_database` instead.
1946
+ """
1947
+ if self.__default_database_name is None and default is None:
1948
+ raise ConfigurationError("No default database name defined or provided.")
1949
+
1950
+ name = cast(str, self.__default_database_name or default)
1951
+ return database.Database(
1952
+ self, name, codec_options, read_preference, write_concern, read_concern
1953
+ )
1954
+
1955
+ def get_database(
1956
+ self,
1957
+ name: Optional[str] = None,
1958
+ codec_options: Optional[CodecOptions] = None,
1959
+ read_preference: Optional[_ServerMode] = None,
1960
+ write_concern: Optional[WriteConcern] = None,
1961
+ read_concern: Optional["ReadConcern"] = None,
1962
+ ) -> database.Database[_DocumentType]:
1963
+ """Get a :class:`~pymongo.database.Database` with the given name and
1964
+ options.
1965
+
1966
+ Useful for creating a :class:`~pymongo.database.Database` with
1967
+ different codec options, read preference, and/or write concern from
1968
+ this :class:`MongoClient`.
1969
+
1970
+ >>> client.read_preference
1971
+ Primary()
1972
+ >>> db1 = client.test
1973
+ >>> db1.read_preference
1974
+ Primary()
1975
+ >>> from pymongo import ReadPreference
1976
+ >>> db2 = client.get_database(
1977
+ ... 'test', read_preference=ReadPreference.SECONDARY)
1978
+ >>> db2.read_preference
1979
+ Secondary(tag_sets=None)
1980
+
1981
+ :Parameters:
1982
+ - `name` (optional): The name of the database - a string. If ``None``
1983
+ (the default) the database named in the MongoDB connection URI is
1984
+ returned.
1985
+ - `codec_options` (optional): An instance of
1986
+ :class:`~bson.codec_options.CodecOptions`. If ``None`` (the
1987
+ default) the :attr:`codec_options` of this :class:`MongoClient` is
1988
+ used.
1989
+ - `read_preference` (optional): The read preference to use. If
1990
+ ``None`` (the default) the :attr:`read_preference` of this
1991
+ :class:`MongoClient` is used. See :mod:`~pymongo.read_preferences`
1992
+ for options.
1993
+ - `write_concern` (optional): An instance of
1994
+ :class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the
1995
+ default) the :attr:`write_concern` of this :class:`MongoClient` is
1996
+ used.
1997
+ - `read_concern` (optional): An instance of
1998
+ :class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the
1999
+ default) the :attr:`read_concern` of this :class:`MongoClient` is
2000
+ used.
2001
+
2002
+ .. versionchanged:: 3.5
2003
+ The `name` parameter is now optional, defaulting to the database
2004
+ named in the MongoDB connection URI.
2005
+ """
2006
+ if name is None:
2007
+ if self.__default_database_name is None:
2008
+ raise ConfigurationError("No default database defined")
2009
+ name = self.__default_database_name
2010
+
2011
+ return database.Database(
2012
+ self, name, codec_options, read_preference, write_concern, read_concern
2013
+ )
2014
+
2015
+ def _database_default_options(self, name):
2016
+ """Get a Database instance with the default settings."""
2017
+ return self.get_database(
2018
+ name,
2019
+ codec_options=DEFAULT_CODEC_OPTIONS,
2020
+ read_preference=ReadPreference.PRIMARY,
2021
+ write_concern=DEFAULT_WRITE_CONCERN,
2022
+ )
2023
+
2024
+ def __enter__(self) -> "MongoClient[_DocumentType]":
2025
+ return self
2026
+
2027
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
2028
+ self.close()
2029
+
2030
+ # See PYTHON-3084.
2031
+ __iter__ = None
2032
+
2033
+ def __next__(self) -> NoReturn:
2034
+ raise TypeError("'MongoClient' object is not iterable")
2035
+
2036
+ next = __next__
2037
+
2038
+
2039
+ def _retryable_error_doc(exc):
2040
+ """Return the server response from PyMongo exception or None."""
2041
+ if isinstance(exc, BulkWriteError):
2042
+ # Check the last writeConcernError to determine if this
2043
+ # BulkWriteError is retryable.
2044
+ wces = exc.details["writeConcernErrors"]
2045
+ wce = wces[-1] if wces else None
2046
+ return wce
2047
+ if isinstance(exc, (NotPrimaryError, OperationFailure)):
2048
+ return exc.details
2049
+ return None
2050
+
2051
+
2052
+ def _add_retryable_write_error(exc, max_wire_version):
2053
+ doc = _retryable_error_doc(exc)
2054
+ if doc:
2055
+ code = doc.get("code", 0)
2056
+ # retryWrites on MMAPv1 should raise an actionable error.
2057
+ if code == 20 and str(exc).startswith("Transaction numbers"):
2058
+ errmsg = (
2059
+ "This MongoDB deployment does not support "
2060
+ "retryable writes. Please add retryWrites=false "
2061
+ "to your connection string."
2062
+ )
2063
+ raise OperationFailure(errmsg, code, exc.details)
2064
+ if max_wire_version >= 9:
2065
+ # In MongoDB 4.4+, the server reports the error labels.
2066
+ for label in doc.get("errorLabels", []):
2067
+ exc._add_error_label(label)
2068
+ else:
2069
+ if code in helpers._RETRYABLE_ERROR_CODES:
2070
+ exc._add_error_label("RetryableWriteError")
2071
+
2072
+ # Connection errors are always retryable except NotPrimaryError and WaitQueueTimeoutError which is
2073
+ # handled above.
2074
+ if isinstance(exc, ConnectionFailure) and not isinstance(
2075
+ exc, (NotPrimaryError, WaitQueueTimeoutError)
2076
+ ):
2077
+ exc._add_error_label("RetryableWriteError")
2078
+
2079
+
2080
+ class _MongoClientErrorHandler(object):
2081
+ """Handle errors raised when executing an operation."""
2082
+
2083
+ __slots__ = (
2084
+ "client",
2085
+ "server_address",
2086
+ "session",
2087
+ "max_wire_version",
2088
+ "sock_generation",
2089
+ "completed_handshake",
2090
+ "service_id",
2091
+ "handled",
2092
+ )
2093
+
2094
+ def __init__(self, client, server, session):
2095
+ self.client = client
2096
+ self.server_address = server.description.address
2097
+ self.session = session
2098
+ self.max_wire_version = common.MIN_WIRE_VERSION
2099
+ # XXX: When get_socket fails, this generation could be out of date:
2100
+ # "Note that when a network error occurs before the handshake
2101
+ # completes then the error's generation number is the generation
2102
+ # of the pool at the time the connection attempt was started."
2103
+ self.sock_generation = server.pool.gen.get_overall()
2104
+ self.completed_handshake = False
2105
+ self.service_id = None
2106
+ self.handled = False
2107
+
2108
+ def contribute_socket(self, sock_info, completed_handshake=True):
2109
+ """Provide socket information to the error handler."""
2110
+ self.max_wire_version = sock_info.max_wire_version
2111
+ self.sock_generation = sock_info.generation
2112
+ self.service_id = sock_info.service_id
2113
+ self.completed_handshake = completed_handshake
2114
+
2115
+ def handle(self, exc_type, exc_val):
2116
+ if self.handled or exc_type is None:
2117
+ return
2118
+ self.handled = True
2119
+ if self.session:
2120
+ if issubclass(exc_type, ConnectionFailure):
2121
+ if self.session.in_transaction:
2122
+ exc_val._add_error_label("TransientTransactionError")
2123
+ self.session._server_session.mark_dirty()
2124
+
2125
+ if issubclass(exc_type, PyMongoError):
2126
+ if exc_val.has_error_label("TransientTransactionError") or exc_val.has_error_label(
2127
+ "RetryableWriteError"
2128
+ ):
2129
+ self.session._unpin()
2130
+
2131
+ err_ctx = _ErrorContext(
2132
+ exc_val,
2133
+ self.max_wire_version,
2134
+ self.sock_generation,
2135
+ self.completed_handshake,
2136
+ self.service_id,
2137
+ )
2138
+ self.client._topology.handle_error(self.server_address, err_ctx)
2139
+
2140
+ def __enter__(self):
2141
+ return self
2142
+
2143
+ def __exit__(self, exc_type, exc_val, exc_tb):
2144
+ return self.handle(exc_type, exc_val)