sensu-plugins-mongodb-mrtrotl 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1 -0
- data/LICENSE +22 -0
- data/README.md +27 -0
- data/bin/check-mongodb-metric.rb +144 -0
- data/bin/check-mongodb-query-count.rb +267 -0
- data/bin/check-mongodb.py +1644 -0
- data/bin/check-mongodb.rb +5 -0
- data/bin/metrics-mongodb-replication.rb +254 -0
- data/bin/metrics-mongodb.rb +133 -0
- data/lib/bson/__init__.py +1347 -0
- data/lib/bson/__pycache__/__init__.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/_helpers.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/binary.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/code.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/codec_options.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/dbref.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/decimal128.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/errors.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/int64.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/json_util.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/max_key.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/min_key.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/objectid.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/raw_bson.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/regex.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/son.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/timestamp.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/tz_util.cpython-310.pyc +0 -0
- data/lib/bson/_cbson.cpython-310-x86_64-linux-gnu.so +0 -0
- data/lib/bson/_helpers.py +41 -0
- data/lib/bson/binary.py +364 -0
- data/lib/bson/code.py +101 -0
- data/lib/bson/codec_options.py +414 -0
- data/lib/bson/codec_options.pyi +100 -0
- data/lib/bson/dbref.py +133 -0
- data/lib/bson/decimal128.py +314 -0
- data/lib/bson/errors.py +35 -0
- data/lib/bson/int64.py +39 -0
- data/lib/bson/json_util.py +874 -0
- data/lib/bson/max_key.py +55 -0
- data/lib/bson/min_key.py +55 -0
- data/lib/bson/objectid.py +286 -0
- data/lib/bson/py.typed +2 -0
- data/lib/bson/raw_bson.py +175 -0
- data/lib/bson/regex.py +135 -0
- data/lib/bson/son.py +208 -0
- data/lib/bson/timestamp.py +124 -0
- data/lib/bson/tz_util.py +52 -0
- data/lib/gridfs/__init__.py +1015 -0
- data/lib/gridfs/__pycache__/__init__.cpython-310.pyc +0 -0
- data/lib/gridfs/__pycache__/errors.cpython-310.pyc +0 -0
- data/lib/gridfs/__pycache__/grid_file.cpython-310.pyc +0 -0
- data/lib/gridfs/errors.py +33 -0
- data/lib/gridfs/grid_file.py +907 -0
- data/lib/gridfs/py.typed +2 -0
- data/lib/pymongo/__init__.py +185 -0
- data/lib/pymongo/__pycache__/__init__.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/_csot.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/aggregation.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/auth.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/auth_aws.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/bulk.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/change_stream.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/client_options.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/client_session.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/collation.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/collection.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/command_cursor.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/common.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/compression_support.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/cursor.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/daemon.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/database.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/driver_info.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/encryption.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/encryption_options.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/errors.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/event_loggers.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/hello.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/helpers.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/max_staleness_selectors.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/message.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/mongo_client.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/monitor.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/monitoring.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/network.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/ocsp_cache.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/ocsp_support.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/operations.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/periodic_executor.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/pool.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/pyopenssl_context.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/read_concern.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/read_preferences.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/response.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/results.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/saslprep.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server_api.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server_description.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server_selectors.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server_type.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/settings.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/socket_checker.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/srv_resolver.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/ssl_context.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/ssl_support.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/topology.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/topology_description.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/typings.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/uri_parser.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/write_concern.cpython-310.pyc +0 -0
- data/lib/pymongo/_cmessage.cpython-310-x86_64-linux-gnu.so +0 -0
- data/lib/pymongo/_csot.py +118 -0
- data/lib/pymongo/aggregation.py +229 -0
- data/lib/pymongo/auth.py +549 -0
- data/lib/pymongo/auth_aws.py +94 -0
- data/lib/pymongo/bulk.py +513 -0
- data/lib/pymongo/change_stream.py +457 -0
- data/lib/pymongo/client_options.py +302 -0
- data/lib/pymongo/client_session.py +1112 -0
- data/lib/pymongo/collation.py +224 -0
- data/lib/pymongo/collection.py +3204 -0
- data/lib/pymongo/command_cursor.py +353 -0
- data/lib/pymongo/common.py +984 -0
- data/lib/pymongo/compression_support.py +149 -0
- data/lib/pymongo/cursor.py +1345 -0
- data/lib/pymongo/daemon.py +141 -0
- data/lib/pymongo/database.py +1202 -0
- data/lib/pymongo/driver_info.py +42 -0
- data/lib/pymongo/encryption.py +884 -0
- data/lib/pymongo/encryption_options.py +221 -0
- data/lib/pymongo/errors.py +365 -0
- data/lib/pymongo/event_loggers.py +221 -0
- data/lib/pymongo/hello.py +219 -0
- data/lib/pymongo/helpers.py +259 -0
- data/lib/pymongo/max_staleness_selectors.py +114 -0
- data/lib/pymongo/message.py +1440 -0
- data/lib/pymongo/mongo_client.py +2144 -0
- data/lib/pymongo/monitor.py +440 -0
- data/lib/pymongo/monitoring.py +1801 -0
- data/lib/pymongo/network.py +311 -0
- data/lib/pymongo/ocsp_cache.py +87 -0
- data/lib/pymongo/ocsp_support.py +372 -0
- data/lib/pymongo/operations.py +507 -0
- data/lib/pymongo/periodic_executor.py +183 -0
- data/lib/pymongo/pool.py +1660 -0
- data/lib/pymongo/py.typed +2 -0
- data/lib/pymongo/pyopenssl_context.py +383 -0
- data/lib/pymongo/read_concern.py +75 -0
- data/lib/pymongo/read_preferences.py +609 -0
- data/lib/pymongo/response.py +109 -0
- data/lib/pymongo/results.py +217 -0
- data/lib/pymongo/saslprep.py +113 -0
- data/lib/pymongo/server.py +247 -0
- data/lib/pymongo/server_api.py +170 -0
- data/lib/pymongo/server_description.py +285 -0
- data/lib/pymongo/server_selectors.py +153 -0
- data/lib/pymongo/server_type.py +32 -0
- data/lib/pymongo/settings.py +159 -0
- data/lib/pymongo/socket_checker.py +104 -0
- data/lib/pymongo/srv_resolver.py +126 -0
- data/lib/pymongo/ssl_context.py +39 -0
- data/lib/pymongo/ssl_support.py +99 -0
- data/lib/pymongo/topology.py +890 -0
- data/lib/pymongo/topology_description.py +639 -0
- data/lib/pymongo/typings.py +39 -0
- data/lib/pymongo/uri_parser.py +624 -0
- data/lib/pymongo/write_concern.py +129 -0
- data/lib/pymongo-4.2.0.dist-info/INSTALLER +1 -0
- data/lib/pymongo-4.2.0.dist-info/LICENSE +201 -0
- data/lib/pymongo-4.2.0.dist-info/METADATA +250 -0
- data/lib/pymongo-4.2.0.dist-info/RECORD +167 -0
- data/lib/pymongo-4.2.0.dist-info/REQUESTED +0 -0
- data/lib/pymongo-4.2.0.dist-info/WHEEL +6 -0
- data/lib/pymongo-4.2.0.dist-info/top_level.txt +3 -0
- data/lib/sensu-plugins-mongodb/metrics.rb +391 -0
- data/lib/sensu-plugins-mongodb/version.rb +9 -0
- data/lib/sensu-plugins-mongodb.rb +1 -0
- 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)
|