Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb9e036b72 | ||
|
|
f0d1563c1f | ||
|
|
fd9a8bf269 | ||
|
|
d715f5844d |
@ -508,7 +508,7 @@ def object_hook(dct, json_options=DEFAULT_JSON_OPTIONS):
|
|||||||
def _parse_legacy_regex(doc):
|
def _parse_legacy_regex(doc):
|
||||||
pattern = doc["$regex"]
|
pattern = doc["$regex"]
|
||||||
# Check if this is the $regex query operator.
|
# Check if this is the $regex query operator.
|
||||||
if isinstance(pattern, Regex):
|
if not isinstance(pattern, (str, bytes)):
|
||||||
return doc
|
return doc
|
||||||
flags = 0
|
flags = 0
|
||||||
# PyMongo always adds $options but some other tools may not.
|
# PyMongo always adds $options but some other tools may not.
|
||||||
|
|||||||
@ -1,6 +1,27 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
Changes in Version 4.0.1
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Issues Resolved
|
||||||
|
...............
|
||||||
|
|
||||||
|
Version 4.0.1 fixes a number of bugs:
|
||||||
|
|
||||||
|
- Fixed a bug that prevented :meth:`bson.json_util.loads` from
|
||||||
|
decoding a document with a non-string "$regex" field (`PYTHON-3028`_).
|
||||||
|
- Fixed a bug where a client may select a hidden/ghost or not yet initialized
|
||||||
|
replica set member leading to unexpected "connection pool paused" errors
|
||||||
|
(`PYTHON-3027`_).
|
||||||
|
|
||||||
|
See the `PyMongo 4.0.1 release notes in JIRA`_ for the list of resolved issues
|
||||||
|
in this release.
|
||||||
|
|
||||||
|
.. _PYTHON-3027: https://jira.mongodb.org/browse/PYTHON-3027
|
||||||
|
.. _PYTHON-3028: https://jira.mongodb.org/browse/PYTHON-3028
|
||||||
|
.. _PyMongo 4.0.1 release notes in JIRA: https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=10004&version=32504
|
||||||
|
|
||||||
Changes in Version 4.0
|
Changes in Version 4.0
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
.. _handling-uuid-data-example:
|
.. _handling-uuid-data-example:
|
||||||
|
|
||||||
Handling UUID Data
|
Handling UUID Data
|
||||||
@ -12,7 +13,7 @@ to MongoDB and retrieve them as native :class:`uuid.UUID` objects::
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
# use the 'standard' representation for cross-language compatibility.
|
# use the 'standard' representation for cross-language compatibility.
|
||||||
client = MongoClient(uuid_representation=UuidRepresentation.STANDARD)
|
client = MongoClient(uuidRepresentation='standard')
|
||||||
collection = client.get_database('uuid_db').get_collection('uuid_coll')
|
collection = client.get_database('uuid_db').get_collection('uuid_coll')
|
||||||
|
|
||||||
# remove all documents from collection
|
# remove all documents from collection
|
||||||
|
|||||||
@ -53,7 +53,7 @@ TEXT = "text"
|
|||||||
.. _text index: http://docs.mongodb.org/manual/core/index-text/
|
.. _text index: http://docs.mongodb.org/manual/core/index-text/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
version_tuple = (4, 0, 1, '.dev0')
|
version_tuple = (4, 0, 1)
|
||||||
|
|
||||||
def get_version_string():
|
def get_version_string():
|
||||||
if isinstance(version_tuple[-1], str):
|
if isinstance(version_tuple[-1], str):
|
||||||
|
|||||||
@ -325,9 +325,9 @@ class MongoClient(common.BaseObject):
|
|||||||
speed. 9 is best compression. Defaults to -1.
|
speed. 9 is best compression. Defaults to -1.
|
||||||
- `uuidRepresentation`: The BSON representation to use when encoding
|
- `uuidRepresentation`: The BSON representation to use when encoding
|
||||||
from and decoding to instances of :class:`~uuid.UUID`. Valid
|
from and decoding to instances of :class:`~uuid.UUID`. Valid
|
||||||
values are `pythonLegacy`, `javaLegacy`, `csharpLegacy`, `standard`
|
values are the strings: "standard", "pythonLegacy", "javaLegacy",
|
||||||
and `unspecified` (the default). New applications
|
"csharpLegacy", and "unspecified" (the default). New applications
|
||||||
should consider setting this to `standard` for cross language
|
should consider setting this to "standard" for cross language
|
||||||
compatibility. See :ref:`handling-uuid-data-example` for details.
|
compatibility. See :ref:`handling-uuid-data-example` for details.
|
||||||
- `unicode_decode_error_handler`: The error handler to apply when
|
- `unicode_decode_error_handler`: The error handler to apply when
|
||||||
a Unicode-related error occurs during BSON decoding that would
|
a Unicode-related error occurs during BSON decoding that would
|
||||||
|
|||||||
@ -263,9 +263,10 @@ class TopologyDescription(object):
|
|||||||
selector.min_wire_version,
|
selector.min_wire_version,
|
||||||
common_wv))
|
common_wv))
|
||||||
|
|
||||||
if self.topology_type in (TOPOLOGY_TYPE.Single,
|
if self.topology_type == TOPOLOGY_TYPE.Unknown:
|
||||||
TOPOLOGY_TYPE.LoadBalanced,
|
return []
|
||||||
TOPOLOGY_TYPE.Unknown):
|
elif self.topology_type in (TOPOLOGY_TYPE.Single,
|
||||||
|
TOPOLOGY_TYPE.LoadBalanced):
|
||||||
# Ignore selectors for standalone and load balancer mode.
|
# Ignore selectors for standalone and load balancer mode.
|
||||||
return self.known_servers
|
return self.known_servers
|
||||||
elif address:
|
elif address:
|
||||||
|
|||||||
2
setup.py
2
setup.py
@ -36,7 +36,7 @@ except ImportError:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
_HAVE_SPHINX = False
|
_HAVE_SPHINX = False
|
||||||
|
|
||||||
version = "4.0.1.dev0"
|
version = "4.0.1"
|
||||||
|
|
||||||
f = open("README.rst")
|
f = open("README.rst")
|
||||||
try:
|
try:
|
||||||
|
|||||||
52
test/mockupdb/test_rsghost.py
Normal file
52
test/mockupdb/test_rsghost.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# Copyright 2021-present MongoDB, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""Test connections to RSGhost nodes."""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from mockupdb import going, MockupDB
|
||||||
|
from pymongo import MongoClient
|
||||||
|
from pymongo.errors import ServerSelectionTimeoutError
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestRSGhost(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_rsghost(self):
|
||||||
|
rsother_response = {
|
||||||
|
'ok': 1.0, 'ismaster': False, 'secondary': False,
|
||||||
|
'info': 'Does not have a valid replica set config',
|
||||||
|
'isreplicaset': True, 'maxBsonObjectSize': 16777216,
|
||||||
|
'maxMessageSizeBytes': 48000000, 'maxWriteBatchSize': 100000,
|
||||||
|
'localTime': datetime.datetime(2021, 11, 30, 0, 53, 4, 99000),
|
||||||
|
'logicalSessionTimeoutMinutes': 30, 'connectionId': 3,
|
||||||
|
'minWireVersion': 0, 'maxWireVersion': 15, 'readOnly': False}
|
||||||
|
server = MockupDB(auto_ismaster=rsother_response)
|
||||||
|
server.run()
|
||||||
|
self.addCleanup(server.stop)
|
||||||
|
# Default auto discovery yields a server selection timeout.
|
||||||
|
with MongoClient(server.uri, serverSelectionTimeoutMS=250) as client:
|
||||||
|
with self.assertRaises(ServerSelectionTimeoutError):
|
||||||
|
client.test.command('ping')
|
||||||
|
# Direct connection succeeds.
|
||||||
|
with MongoClient(server.uri, directConnection=True) as client:
|
||||||
|
with going(client.test.command, 'ping'):
|
||||||
|
request = server.receives(ping=1)
|
||||||
|
request.reply()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"topology_description": {
|
||||||
|
"type": "Unknown",
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"address": "a:27017",
|
||||||
|
"avg_rtt_ms": 5,
|
||||||
|
"type": "RSGhost"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"operation": "read",
|
||||||
|
"read_preference": {
|
||||||
|
"mode": "Nearest"
|
||||||
|
},
|
||||||
|
"suitable_servers": [],
|
||||||
|
"in_latency_window": []
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"topology_description": {
|
||||||
|
"type": "Unknown",
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"address": "a:27017",
|
||||||
|
"avg_rtt_ms": 5,
|
||||||
|
"type": "RSGhost"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"operation": "write",
|
||||||
|
"read_preference": {
|
||||||
|
"mode": "Nearest"
|
||||||
|
},
|
||||||
|
"suitable_servers": [],
|
||||||
|
"in_latency_window": []
|
||||||
|
}
|
||||||
@ -270,6 +270,15 @@ class TestJsonUtil(unittest.TestCase):
|
|||||||
json_util.dumps(Regex('.*', re.M | re.X),
|
json_util.dumps(Regex('.*', re.M | re.X),
|
||||||
json_options=LEGACY_JSON_OPTIONS))
|
json_options=LEGACY_JSON_OPTIONS))
|
||||||
|
|
||||||
|
def test_regex_validation(self):
|
||||||
|
non_str_types = [10, {}, []]
|
||||||
|
docs = [{"$regex": i} for i in non_str_types]
|
||||||
|
for doc in docs:
|
||||||
|
self.assertEqual(doc, json_util.loads(json.dumps(doc)))
|
||||||
|
|
||||||
|
doc = {"$regex": ""}
|
||||||
|
self.assertIsInstance(json_util.loads(json.dumps(doc)), Regex)
|
||||||
|
|
||||||
def test_minkey(self):
|
def test_minkey(self):
|
||||||
self.round_trip({"m": MinKey()})
|
self.round_trip({"m": MinKey()})
|
||||||
|
|
||||||
|
|||||||
@ -63,7 +63,7 @@ def make_server_description(server, hosts):
|
|||||||
return ServerDescription(clean_node(server['address']), Hello({}))
|
return ServerDescription(clean_node(server['address']), Hello({}))
|
||||||
|
|
||||||
hello_response = {'ok': True, 'hosts': hosts}
|
hello_response = {'ok': True, 'hosts': hosts}
|
||||||
if server_type != "Standalone" and server_type != "Mongos":
|
if server_type not in ("Standalone", "Mongos", "RSGhost"):
|
||||||
hello_response['setName'] = "rs"
|
hello_response['setName'] = "rs"
|
||||||
|
|
||||||
if server_type == "RSPrimary":
|
if server_type == "RSPrimary":
|
||||||
@ -72,6 +72,10 @@ def make_server_description(server, hosts):
|
|||||||
hello_response['secondary'] = True
|
hello_response['secondary'] = True
|
||||||
elif server_type == "Mongos":
|
elif server_type == "Mongos":
|
||||||
hello_response['msg'] = 'isdbgrid'
|
hello_response['msg'] = 'isdbgrid'
|
||||||
|
elif server_type == "RSGhost":
|
||||||
|
hello_response['isreplicaset'] = True
|
||||||
|
elif server_type == "RSArbiter":
|
||||||
|
hello_response['arbiterOnly'] = True
|
||||||
|
|
||||||
hello_response['lastWrite'] = {
|
hello_response['lastWrite'] = {
|
||||||
'lastWriteDate': make_last_write_date(server)
|
'lastWriteDate': make_last_write_date(server)
|
||||||
@ -149,7 +153,7 @@ def create_topology(scenario_def, **kwargs):
|
|||||||
|
|
||||||
# Assert that descriptions match
|
# Assert that descriptions match
|
||||||
assert (scenario_def['topology_description']['type'] ==
|
assert (scenario_def['topology_description']['type'] ==
|
||||||
topology.description.topology_type_name)
|
topology.description.topology_type_name), topology.description.topology_type_name
|
||||||
|
|
||||||
return topology
|
return topology
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user