PYTHON-2884: Replaced SON usage in all internal classes and commands (#1426)
This commit is contained in:
parent
ffd61f8d74
commit
60d0761527
@ -52,17 +52,16 @@ overhead of decoding or encoding BSON.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, ItemsView, Iterator, Mapping, MutableMapping, Optional
|
||||
from typing import Any, ItemsView, Iterator, Mapping, Optional
|
||||
|
||||
from bson import _get_object_size, _raw_to_dict
|
||||
from bson.codec_options import _RAW_BSON_DOCUMENT_MARKER, CodecOptions
|
||||
from bson.codec_options import DEFAULT_CODEC_OPTIONS as DEFAULT
|
||||
from bson.son import SON
|
||||
|
||||
|
||||
def _inflate_bson(
|
||||
bson_bytes: bytes, codec_options: CodecOptions[RawBSONDocument], raw_array: bool = False
|
||||
) -> MutableMapping[str, Any]:
|
||||
) -> dict[str, Any]:
|
||||
"""Inflates the top level fields of a BSON document.
|
||||
|
||||
:param bson_bytes: the BSON bytes that compose this document
|
||||
@ -70,10 +69,7 @@ def _inflate_bson(
|
||||
:class:`~bson.codec_options.CodecOptions` whose ``document_class``
|
||||
must be :class:`RawBSONDocument`.
|
||||
"""
|
||||
# Use SON to preserve ordering of elements.
|
||||
return _raw_to_dict(
|
||||
bson_bytes, 4, len(bson_bytes) - 1, codec_options, SON(), raw_array=raw_array
|
||||
)
|
||||
return _raw_to_dict(bson_bytes, 4, len(bson_bytes) - 1, codec_options, {}, raw_array=raw_array)
|
||||
|
||||
|
||||
class RawBSONDocument(Mapping[str, Any]):
|
||||
@ -152,7 +148,6 @@ class RawBSONDocument(Mapping[str, Any]):
|
||||
if self.__inflated_doc is None:
|
||||
# We already validated the object's size when this document was
|
||||
# created, so no need to do that again.
|
||||
# Use SON to preserve ordering of elements.
|
||||
self.__inflated_doc = self._inflate_bson(self.__raw, self.__codec_options)
|
||||
return self.__inflated_doc
|
||||
|
||||
|
||||
@ -11,6 +11,8 @@ PyMongo 4.7 brings a number of improvements including:
|
||||
:attr:`pymongo.monitoring.CommandSucceededEvent.server_connection_id`, and
|
||||
:attr:`pymongo.monitoring.CommandFailedEvent.server_connection_id` properties.
|
||||
- Fixed a bug where inflating a :class:`~bson.raw_bson.RawBSONDocument` containing a :class:`~bson.code.Code` would cause an error.
|
||||
- Replaced usage of :class:`bson.son.SON` on all internal classes and commands to dict,
|
||||
:attr:`options.pool_options.metadata` is now of type ``dict`` as opposed to :class:`bson.son.SON`.
|
||||
|
||||
Changes in Version 4.6.1
|
||||
------------------------
|
||||
|
||||
@ -24,7 +24,6 @@ from typing import Any, Iterable, Mapping, NoReturn, Optional
|
||||
from bson.binary import Binary
|
||||
from bson.int64 import Int64
|
||||
from bson.objectid import ObjectId
|
||||
from bson.son import SON
|
||||
from gridfs.errors import CorruptGridFile, FileExists, NoFile
|
||||
from pymongo import ASCENDING
|
||||
from pymongo.client_session import ClientSession
|
||||
@ -50,8 +49,8 @@ NEWLN = b"\n"
|
||||
# Slightly under a power of 2, to work well with server's record allocations.
|
||||
DEFAULT_CHUNK_SIZE = 255 * 1024
|
||||
|
||||
_C_INDEX: SON[str, Any] = SON([("files_id", ASCENDING), ("n", ASCENDING)])
|
||||
_F_INDEX: SON[str, Any] = SON([("filename", ASCENDING), ("uploadDate", ASCENDING)])
|
||||
_C_INDEX: dict[str, Any] = {"files_id": ASCENDING, "n": ASCENDING}
|
||||
_F_INDEX: dict[str, Any] = {"filename": ASCENDING, "uploadDate": ASCENDING}
|
||||
|
||||
|
||||
def _grid_in_property(
|
||||
|
||||
@ -18,7 +18,6 @@ from __future__ import annotations
|
||||
from collections.abc import Callable, Mapping, MutableMapping
|
||||
from typing import TYPE_CHECKING, Any, Optional, Union
|
||||
|
||||
from bson.son import SON
|
||||
from pymongo import common
|
||||
from pymongo.collation import validate_collation_or_none
|
||||
from pymongo.errors import ConfigurationError
|
||||
@ -137,7 +136,7 @@ class _AggregationCommand:
|
||||
read_preference: _ServerMode,
|
||||
) -> CommandCursor[_DocumentType]:
|
||||
# Serialize command.
|
||||
cmd = SON([("aggregate", self._aggregation_target), ("pipeline", self._pipeline)])
|
||||
cmd = {"aggregate": self._aggregation_target, "pipeline": self._pipeline}
|
||||
cmd.update(self._options)
|
||||
|
||||
# Apply this target's read concern if:
|
||||
|
||||
@ -36,7 +36,6 @@ from typing import (
|
||||
from urllib.parse import quote
|
||||
|
||||
from bson.binary import Binary
|
||||
from bson.son import SON
|
||||
from pymongo.auth_aws import _authenticate_aws
|
||||
from pymongo.auth_oidc import _authenticate_oidc, _get_authenticator, _OIDCProperties
|
||||
from pymongo.errors import ConfigurationError, OperationFailure
|
||||
@ -217,15 +216,13 @@ def _authenticate_scram_start(
|
||||
nonce = standard_b64encode(os.urandom(32))
|
||||
first_bare = b"n=" + user + b",r=" + nonce
|
||||
|
||||
cmd = SON(
|
||||
[
|
||||
("saslStart", 1),
|
||||
("mechanism", mechanism),
|
||||
("payload", Binary(b"n,," + first_bare)),
|
||||
("autoAuthorize", 1),
|
||||
("options", {"skipEmptyExchange": True}),
|
||||
]
|
||||
)
|
||||
cmd = {
|
||||
"saslStart": 1,
|
||||
"mechanism": mechanism,
|
||||
"payload": Binary(b"n,," + first_bare),
|
||||
"autoAuthorize": 1,
|
||||
"options": {"skipEmptyExchange": True},
|
||||
}
|
||||
return nonce, first_bare, cmd
|
||||
|
||||
|
||||
@ -288,13 +285,11 @@ def _authenticate_scram(credentials: MongoCredential, conn: Connection, mechanis
|
||||
|
||||
server_sig = standard_b64encode(_hmac(server_key, auth_msg, digestmod).digest())
|
||||
|
||||
cmd = SON(
|
||||
[
|
||||
("saslContinue", 1),
|
||||
("conversationId", res["conversationId"]),
|
||||
("payload", Binary(client_final)),
|
||||
]
|
||||
)
|
||||
cmd = {
|
||||
"saslContinue": 1,
|
||||
"conversationId": res["conversationId"],
|
||||
"payload": Binary(client_final),
|
||||
}
|
||||
res = conn.command(source, cmd)
|
||||
|
||||
parsed = _parse_scram_response(res["payload"])
|
||||
@ -304,13 +299,11 @@ def _authenticate_scram(credentials: MongoCredential, conn: Connection, mechanis
|
||||
# A third empty challenge may be required if the server does not support
|
||||
# skipEmptyExchange: SERVER-44857.
|
||||
if not res["done"]:
|
||||
cmd = SON(
|
||||
[
|
||||
("saslContinue", 1),
|
||||
("conversationId", res["conversationId"]),
|
||||
("payload", Binary(b"")),
|
||||
]
|
||||
)
|
||||
cmd = {
|
||||
"saslContinue": 1,
|
||||
"conversationId": res["conversationId"],
|
||||
"payload": Binary(b""),
|
||||
}
|
||||
res = conn.command(source, cmd)
|
||||
if not res["done"]:
|
||||
raise OperationFailure("SASL conversation failed to complete.")
|
||||
@ -415,14 +408,12 @@ def _authenticate_gssapi(credentials: MongoCredential, conn: Connection) -> None
|
||||
# Since mongo accepts base64 strings as the payload we don't
|
||||
# have to use bson.binary.Binary.
|
||||
payload = kerberos.authGSSClientResponse(ctx)
|
||||
cmd = SON(
|
||||
[
|
||||
("saslStart", 1),
|
||||
("mechanism", "GSSAPI"),
|
||||
("payload", payload),
|
||||
("autoAuthorize", 1),
|
||||
]
|
||||
)
|
||||
cmd = {
|
||||
"saslStart": 1,
|
||||
"mechanism": "GSSAPI",
|
||||
"payload": payload,
|
||||
"autoAuthorize": 1,
|
||||
}
|
||||
response = conn.command("$external", cmd)
|
||||
|
||||
# Limit how many times we loop to catch protocol / library issues
|
||||
@ -433,13 +424,11 @@ def _authenticate_gssapi(credentials: MongoCredential, conn: Connection) -> None
|
||||
|
||||
payload = kerberos.authGSSClientResponse(ctx) or ""
|
||||
|
||||
cmd = SON(
|
||||
[
|
||||
("saslContinue", 1),
|
||||
("conversationId", response["conversationId"]),
|
||||
("payload", payload),
|
||||
]
|
||||
)
|
||||
cmd = {
|
||||
"saslContinue": 1,
|
||||
"conversationId": response["conversationId"],
|
||||
"payload": payload,
|
||||
}
|
||||
response = conn.command("$external", cmd)
|
||||
|
||||
if result == kerberos.AUTH_GSS_COMPLETE:
|
||||
@ -456,13 +445,11 @@ def _authenticate_gssapi(credentials: MongoCredential, conn: Connection) -> None
|
||||
raise OperationFailure("Unknown kerberos failure during GSS_Wrap step.")
|
||||
|
||||
payload = kerberos.authGSSClientResponse(ctx)
|
||||
cmd = SON(
|
||||
[
|
||||
("saslContinue", 1),
|
||||
("conversationId", response["conversationId"]),
|
||||
("payload", payload),
|
||||
]
|
||||
)
|
||||
cmd = {
|
||||
"saslContinue": 1,
|
||||
"conversationId": response["conversationId"],
|
||||
"payload": payload,
|
||||
}
|
||||
conn.command("$external", cmd)
|
||||
|
||||
finally:
|
||||
@ -478,14 +465,12 @@ def _authenticate_plain(credentials: MongoCredential, conn: Connection) -> None:
|
||||
username = credentials.username
|
||||
password = credentials.password
|
||||
payload = (f"\x00{username}\x00{password}").encode()
|
||||
cmd = SON(
|
||||
[
|
||||
("saslStart", 1),
|
||||
("mechanism", "PLAIN"),
|
||||
("payload", Binary(payload)),
|
||||
("autoAuthorize", 1),
|
||||
]
|
||||
)
|
||||
cmd = {
|
||||
"saslStart": 1,
|
||||
"mechanism": "PLAIN",
|
||||
"payload": Binary(payload),
|
||||
"autoAuthorize": 1,
|
||||
}
|
||||
conn.command(source, cmd)
|
||||
|
||||
|
||||
@ -511,7 +496,7 @@ def _authenticate_mongo_cr(credentials: MongoCredential, conn: Connection) -> No
|
||||
key = _auth_key(nonce, username, password)
|
||||
|
||||
# Actually authenticate
|
||||
query = SON([("authenticate", 1), ("user", username), ("nonce", nonce), ("key", key)])
|
||||
query = {"authenticate": 1, "user": username, "nonce": nonce, "key": key}
|
||||
conn.command(source, query)
|
||||
|
||||
|
||||
@ -588,7 +573,7 @@ class _ScramContext(_AuthContext):
|
||||
|
||||
class _X509Context(_AuthContext):
|
||||
def speculate_command(self) -> MutableMapping[str, Any]:
|
||||
cmd = SON([("authenticate", 1), ("mechanism", "MONGODB-X509")])
|
||||
cmd = {"authenticate": 1, "mechanism": "MONGODB-X509"}
|
||||
if self.credentials.username is not None:
|
||||
cmd["user"] = self.credentials.username
|
||||
return cmd
|
||||
|
||||
@ -50,7 +50,6 @@ from typing import TYPE_CHECKING, Any, Mapping, Optional, Type
|
||||
|
||||
import bson
|
||||
from bson.binary import Binary
|
||||
from bson.son import SON
|
||||
from pymongo.errors import ConfigurationError, OperationFailure
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -94,21 +93,17 @@ def _authenticate_aws(credentials: MongoCredential, conn: Connection) -> None:
|
||||
)
|
||||
)
|
||||
client_payload = ctx.step(None)
|
||||
client_first = SON(
|
||||
[("saslStart", 1), ("mechanism", "MONGODB-AWS"), ("payload", client_payload)]
|
||||
)
|
||||
client_first = {"saslStart": 1, "mechanism": "MONGODB-AWS", "payload": client_payload}
|
||||
server_first = conn.command("$external", client_first)
|
||||
res = server_first
|
||||
# Limit how many times we loop to catch protocol / library issues
|
||||
for _ in range(10):
|
||||
client_payload = ctx.step(res["payload"])
|
||||
cmd = SON(
|
||||
[
|
||||
("saslContinue", 1),
|
||||
("conversationId", server_first["conversationId"]),
|
||||
("payload", client_payload),
|
||||
]
|
||||
)
|
||||
cmd = {
|
||||
"saslContinue": 1,
|
||||
"conversationId": server_first["conversationId"],
|
||||
"payload": client_payload,
|
||||
}
|
||||
res = conn.command("$external", cmd)
|
||||
if res["done"]:
|
||||
# SASL complete.
|
||||
|
||||
@ -251,13 +251,11 @@ class _OIDCAuthenticator:
|
||||
token = self.get_current_token()
|
||||
conn.oidc_token_gen_id = self.token_gen_id
|
||||
bin_payload = Binary(bson.encode({"jwt": token}))
|
||||
cmd = SON(
|
||||
[
|
||||
("saslContinue", 1),
|
||||
("conversationId", conversation_id),
|
||||
("payload", bin_payload),
|
||||
]
|
||||
)
|
||||
cmd = {
|
||||
"saslContinue": 1,
|
||||
"conversationId": conversation_id,
|
||||
"payload": bin_payload,
|
||||
}
|
||||
resp = self.run_command(conn, cmd)
|
||||
assert resp is not None
|
||||
if not resp["done"]:
|
||||
|
||||
@ -34,7 +34,6 @@ from typing import (
|
||||
|
||||
from bson.objectid import ObjectId
|
||||
from bson.raw_bson import RawBSONDocument
|
||||
from bson.son import SON
|
||||
from pymongo import _csot, common
|
||||
from pymongo.client_session import ClientSession, _validate_session_write_concern
|
||||
from pymongo.common import (
|
||||
@ -215,7 +214,7 @@ class _Bulk:
|
||||
upsert: bool = False,
|
||||
collation: Optional[Mapping[str, Any]] = None,
|
||||
array_filters: Optional[list[Mapping[str, Any]]] = None,
|
||||
hint: Union[str, SON[str, Any], None] = None,
|
||||
hint: Union[str, dict[str, Any], None] = None,
|
||||
) -> None:
|
||||
"""Create an update document and add it to the list of ops."""
|
||||
validate_ok_for_update(update)
|
||||
@ -242,11 +241,11 @@ class _Bulk:
|
||||
replacement: Mapping[str, Any],
|
||||
upsert: bool = False,
|
||||
collation: Optional[Mapping[str, Any]] = None,
|
||||
hint: Union[str, SON[str, Any], None] = None,
|
||||
hint: Union[str, dict[str, Any], None] = None,
|
||||
) -> None:
|
||||
"""Create a replace document and add it to the list of ops."""
|
||||
validate_ok_for_replace(replacement)
|
||||
cmd = SON([("q", selector), ("u", replacement), ("multi", False), ("upsert", upsert)])
|
||||
cmd = {"q": selector, "u": replacement, "multi": False, "upsert": upsert}
|
||||
if collation is not None:
|
||||
self.uses_collation = True
|
||||
cmd["collation"] = collation
|
||||
@ -260,10 +259,10 @@ class _Bulk:
|
||||
selector: Mapping[str, Any],
|
||||
limit: int,
|
||||
collation: Optional[Mapping[str, Any]] = None,
|
||||
hint: Union[str, SON[str, Any], None] = None,
|
||||
hint: Union[str, dict[str, Any], None] = None,
|
||||
) -> None:
|
||||
"""Create a delete document and add it to the list of ops."""
|
||||
cmd = SON([("q", selector), ("limit", limit)])
|
||||
cmd = {"q": selector, "limit": limit}
|
||||
if collation is not None:
|
||||
self.uses_collation = True
|
||||
cmd["collation"] = collation
|
||||
@ -350,7 +349,7 @@ class _Bulk:
|
||||
if last_run and (len(run.ops) - run.idx_offset) == 1:
|
||||
write_concern = final_write_concern or write_concern
|
||||
|
||||
cmd = SON([(cmd_name, self.collection.name), ("ordered", self.ordered)])
|
||||
cmd = {cmd_name: self.collection.name, "ordered": self.ordered}
|
||||
if self.comment:
|
||||
cmd["comment"] = self.comment
|
||||
_csot.apply_write_concern(cmd, write_concern)
|
||||
@ -469,13 +468,11 @@ class _Bulk:
|
||||
)
|
||||
|
||||
while run.idx_offset < len(run.ops):
|
||||
cmd = SON(
|
||||
[
|
||||
(cmd_name, self.collection.name),
|
||||
("ordered", False),
|
||||
("writeConcern", {"w": 0}),
|
||||
]
|
||||
)
|
||||
cmd = {
|
||||
cmd_name: self.collection.name,
|
||||
"ordered": False,
|
||||
"writeConcern": {"w": 0},
|
||||
}
|
||||
conn.add_server_api(cmd)
|
||||
ops = islice(run.ops, run.idx_offset, None)
|
||||
# Run as many ops as possible.
|
||||
|
||||
@ -154,7 +154,6 @@ from typing import (
|
||||
|
||||
from bson.binary import Binary
|
||||
from bson.int64 import Int64
|
||||
from bson.son import SON
|
||||
from bson.timestamp import Timestamp
|
||||
from pymongo import _csot
|
||||
from pymongo.cursor import _ConnectionManager
|
||||
@ -844,7 +843,7 @@ class ClientSession:
|
||||
opts = self._transaction.opts
|
||||
assert opts
|
||||
wc = opts.write_concern
|
||||
cmd = SON([(command_name, 1)])
|
||||
cmd = {command_name: 1}
|
||||
if command_name == "commitTransaction":
|
||||
if opts.max_commit_time_ms and _csot.get_timeout() is None:
|
||||
cmd["maxTimeMS"] = opts.max_commit_time_ms
|
||||
|
||||
@ -328,7 +328,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
qev2_required: bool = False,
|
||||
) -> None:
|
||||
"""Sends a create command with the given options."""
|
||||
cmd: SON[str, Any] = SON([("create", name)])
|
||||
cmd: dict[str, Any] = {"create": name}
|
||||
if encrypted_fields:
|
||||
cmd["encryptedFields"] = encrypted_fields
|
||||
|
||||
@ -576,7 +576,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
"""Internal helper for inserting a single document."""
|
||||
write_concern = write_concern or self.write_concern
|
||||
acknowledged = write_concern.acknowledged
|
||||
command = SON([("insert", self.name), ("ordered", ordered), ("documents", [doc])])
|
||||
command = {"insert": self.name, "ordered": ordered, "documents": [doc]}
|
||||
if comment is not None:
|
||||
command["comment"] = comment
|
||||
|
||||
@ -767,9 +767,12 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
collation = validate_collation_or_none(collation)
|
||||
write_concern = write_concern or self.write_concern
|
||||
acknowledged = write_concern.acknowledged
|
||||
update_doc: SON[str, Any] = SON(
|
||||
[("q", criteria), ("u", document), ("multi", multi), ("upsert", upsert)]
|
||||
)
|
||||
update_doc: dict[str, Any] = {
|
||||
"q": criteria,
|
||||
"u": document,
|
||||
"multi": multi,
|
||||
"upsert": upsert,
|
||||
}
|
||||
if collation is not None:
|
||||
if not acknowledged:
|
||||
raise ConfigurationError("Collation is unsupported for unacknowledged writes.")
|
||||
@ -788,7 +791,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
if not isinstance(hint, str):
|
||||
hint = helpers._index_document(hint)
|
||||
update_doc["hint"] = hint
|
||||
command = SON([("update", self.name), ("ordered", ordered), ("updates", [update_doc])])
|
||||
command = {"update": self.name, "ordered": ordered, "updates": [update_doc]}
|
||||
if let is not None:
|
||||
common.validate_is_mapping("let", let)
|
||||
command["let"] = let
|
||||
@ -1245,7 +1248,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
common.validate_is_mapping("filter", criteria)
|
||||
write_concern = write_concern or self.write_concern
|
||||
acknowledged = write_concern.acknowledged
|
||||
delete_doc = SON([("q", criteria), ("limit", int(not multi))])
|
||||
delete_doc = {"q": criteria, "limit": int(not multi)}
|
||||
collation = validate_collation_or_none(collation)
|
||||
if collation is not None:
|
||||
if not acknowledged:
|
||||
@ -1260,7 +1263,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
if not isinstance(hint, str):
|
||||
hint = helpers._index_document(hint)
|
||||
delete_doc["hint"] = hint
|
||||
command = SON([("delete", self.name), ("ordered", ordered), ("deletes", [delete_doc])])
|
||||
command = {"delete": self.name, "ordered": ordered, "deletes": [delete_doc]}
|
||||
|
||||
if let is not None:
|
||||
common.validate_is_document_type("let", let)
|
||||
@ -1708,7 +1711,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
session: Optional[ClientSession],
|
||||
conn: Connection,
|
||||
read_preference: Optional[_ServerMode],
|
||||
cmd: SON[str, Any],
|
||||
cmd: dict[str, Any],
|
||||
collation: Optional[Collation],
|
||||
) -> int:
|
||||
"""Internal count command helper."""
|
||||
@ -1732,7 +1735,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
self,
|
||||
conn: Connection,
|
||||
read_preference: Optional[_ServerMode],
|
||||
cmd: SON[str, Any],
|
||||
cmd: dict[str, Any],
|
||||
collation: Optional[_CollationIn],
|
||||
session: Optional[ClientSession],
|
||||
) -> Optional[Mapping[str, Any]]:
|
||||
@ -1791,7 +1794,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
conn: Connection,
|
||||
read_preference: Optional[_ServerMode],
|
||||
) -> int:
|
||||
cmd: SON[str, Any] = SON([("count", self.__name)])
|
||||
cmd: dict[str, Any] = {"count": self.__name}
|
||||
cmd.update(kwargs)
|
||||
return self._count_cmd(session, conn, read_preference, cmd, collation=None)
|
||||
|
||||
@ -1867,7 +1870,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
if comment is not None:
|
||||
kwargs["comment"] = comment
|
||||
pipeline.append({"$group": {"_id": 1, "n": {"$sum": 1}}})
|
||||
cmd = SON([("aggregate", self.__name), ("pipeline", pipeline), ("cursor", {})])
|
||||
cmd = {"aggregate": self.__name, "pipeline": pipeline, "cursor": {}}
|
||||
if "hint" in kwargs and not isinstance(kwargs["hint"], str):
|
||||
kwargs["hint"] = helpers._index_document(kwargs["hint"])
|
||||
collation = validate_collation_or_none(kwargs.pop("collation", None))
|
||||
@ -1970,7 +1973,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
names.append(document["name"])
|
||||
yield document
|
||||
|
||||
cmd = SON([("createIndexes", self.name), ("indexes", list(gen_indexes()))])
|
||||
cmd = {"createIndexes": self.name, "indexes": list(gen_indexes())}
|
||||
cmd.update(kwargs)
|
||||
if "commitQuorum" in kwargs and not supports_quorum:
|
||||
raise ConfigurationError(
|
||||
@ -2193,7 +2196,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
if not isinstance(name, str):
|
||||
raise TypeError("index_or_name must be an instance of str or list")
|
||||
|
||||
cmd = SON([("dropIndexes", self.__name), ("index", name)])
|
||||
cmd = {"dropIndexes": self.__name, "index": name}
|
||||
cmd.update(kwargs)
|
||||
if comment is not None:
|
||||
cmd["comment"] = comment
|
||||
@ -2248,7 +2251,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
conn: Connection,
|
||||
read_preference: _ServerMode,
|
||||
) -> CommandCursor[MutableMapping[str, Any]]:
|
||||
cmd = SON([("listIndexes", self.__name), ("cursor", {})])
|
||||
cmd = {"listIndexes": self.__name, "cursor": {}}
|
||||
if comment is not None:
|
||||
cmd["comment"] = comment
|
||||
|
||||
@ -2429,7 +2432,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
)
|
||||
yield index.document
|
||||
|
||||
cmd = SON([("createSearchIndexes", self.name), ("indexes", list(gen_indexes()))])
|
||||
cmd = {"createSearchIndexes": self.name, "indexes": list(gen_indexes())}
|
||||
cmd.update(kwargs)
|
||||
|
||||
with self._conn_for_writes(session) as conn:
|
||||
@ -2462,7 +2465,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
|
||||
.. versionadded:: 4.5
|
||||
"""
|
||||
cmd = SON([("dropSearchIndex", self.__name), ("name", name)])
|
||||
cmd = {"dropSearchIndex": self.__name, "name": name}
|
||||
cmd.update(kwargs)
|
||||
if comment is not None:
|
||||
cmd["comment"] = comment
|
||||
@ -2498,7 +2501,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
|
||||
.. versionadded:: 4.5
|
||||
"""
|
||||
cmd = SON([("updateSearchIndex", self.__name), ("name", name), ("definition", definition)])
|
||||
cmd = {"updateSearchIndex": self.__name, "name": name, "definition": definition}
|
||||
cmd.update(kwargs)
|
||||
if comment is not None:
|
||||
cmd["comment"] = comment
|
||||
@ -2916,7 +2919,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
raise InvalidName("collection names must not contain '$'")
|
||||
|
||||
new_name = f"{self.__database.name}.{new_name}"
|
||||
cmd = SON([("renameCollection", self.__full_name), ("to", new_name)])
|
||||
cmd = {"renameCollection": self.__full_name, "to": new_name}
|
||||
cmd.update(kwargs)
|
||||
if comment is not None:
|
||||
cmd["comment"] = comment
|
||||
@ -2977,7 +2980,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
"""
|
||||
if not isinstance(key, str):
|
||||
raise TypeError("key must be an instance of str")
|
||||
cmd = SON([("distinct", self.__name), ("key", key)])
|
||||
cmd = {"distinct": self.__name, "key": key}
|
||||
if filter is not None:
|
||||
if "query" in kwargs:
|
||||
raise ConfigurationError("can't pass both filter and query")
|
||||
@ -3034,7 +3037,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
||||
"return_document must be ReturnDocument.BEFORE or ReturnDocument.AFTER"
|
||||
)
|
||||
collation = validate_collation_or_none(kwargs.pop("collation", None))
|
||||
cmd = SON([("findAndModify", self.__name), ("query", filter), ("new", return_document)])
|
||||
cmd = {"findAndModify": self.__name, "query": filter, "new": return_document}
|
||||
if let is not None:
|
||||
common.validate_is_mapping("let", let)
|
||||
cmd["let"] = let
|
||||
|
||||
@ -272,14 +272,14 @@ class Cursor(Generic[_DocumentType]):
|
||||
self.__comment = comment
|
||||
self.__max_time_ms = max_time_ms
|
||||
self.__max_await_time_ms: Optional[int] = None
|
||||
self.__max: Optional[Union[SON[Any, Any], _Sort]] = max
|
||||
self.__min: Optional[Union[SON[Any, Any], _Sort]] = min
|
||||
self.__max: Optional[Union[dict[Any, Any], _Sort]] = max
|
||||
self.__min: Optional[Union[dict[Any, Any], _Sort]] = min
|
||||
self.__collation = validate_collation_or_none(collation)
|
||||
self.__return_key = return_key
|
||||
self.__show_record_id = show_record_id
|
||||
self.__allow_disk_use = allow_disk_use
|
||||
self.__snapshot = snapshot
|
||||
self.__hint: Union[str, SON[str, Any], None]
|
||||
self.__hint: Union[str, dict[str, Any], None]
|
||||
self.__set_hint(hint)
|
||||
|
||||
# Exhaust cursor support
|
||||
@ -473,17 +473,12 @@ class Cursor(Generic[_DocumentType]):
|
||||
|
||||
if operators:
|
||||
# Make a shallow copy so we can cleanly rewind or clone.
|
||||
spec = copy.copy(self.__spec)
|
||||
spec = dict(self.__spec)
|
||||
|
||||
# Allow-listed commands must be wrapped in $query.
|
||||
if "$query" not in spec:
|
||||
# $query has to come first
|
||||
spec = SON([("$query", spec)])
|
||||
|
||||
if not isinstance(spec, SON):
|
||||
# Ensure the spec is SON. As order is important this will
|
||||
# ensure its set before merging in any extra operators.
|
||||
spec = SON(spec)
|
||||
spec = {"$query": spec}
|
||||
|
||||
spec.update(operators)
|
||||
return spec
|
||||
@ -495,7 +490,7 @@ class Cursor(Generic[_DocumentType]):
|
||||
elif "query" in self.__spec and (
|
||||
len(self.__spec) == 1 or next(iter(self.__spec)) == "query"
|
||||
):
|
||||
return SON({"$query": self.__spec})
|
||||
return {"$query": self.__spec}
|
||||
|
||||
return self.__spec
|
||||
|
||||
@ -800,7 +795,7 @@ class Cursor(Generic[_DocumentType]):
|
||||
raise TypeError("spec must be an instance of list or tuple")
|
||||
|
||||
self.__check_okay_to_chain()
|
||||
self.__max = SON(spec)
|
||||
self.__max = dict(spec)
|
||||
return self
|
||||
|
||||
def min(self, spec: _Sort) -> Cursor[_DocumentType]:
|
||||
@ -822,7 +817,7 @@ class Cursor(Generic[_DocumentType]):
|
||||
raise TypeError("spec must be an instance of list or tuple")
|
||||
|
||||
self.__check_okay_to_chain()
|
||||
self.__min = SON(spec)
|
||||
self.__min = dict(spec)
|
||||
return self
|
||||
|
||||
def sort(
|
||||
|
||||
@ -33,7 +33,6 @@ from typing import (
|
||||
|
||||
from bson.codec_options import DEFAULT_CODEC_OPTIONS, CodecOptions
|
||||
from bson.dbref import DBRef
|
||||
from bson.son import SON
|
||||
from bson.timestamp import Timestamp
|
||||
from pymongo import _csot, common
|
||||
from pymongo.aggregation import _DatabaseAggregationCommand
|
||||
@ -729,7 +728,7 @@ class Database(common.BaseObject, Generic[_DocumentType]):
|
||||
) -> Union[dict[str, Any], _CodecDocumentType]:
|
||||
"""Internal command helper."""
|
||||
if isinstance(command, str):
|
||||
command = SON([(command, value)])
|
||||
command = {command: value}
|
||||
|
||||
command.update(kwargs)
|
||||
with self.__client._tmp_session(session) as s:
|
||||
@ -804,16 +803,22 @@ class Database(common.BaseObject, Generic[_DocumentType]):
|
||||
using:
|
||||
|
||||
>>> db.command("buildinfo")
|
||||
OR
|
||||
>>> db.command({"buildinfo": 1})
|
||||
|
||||
For a command where the value matters, like ``{count:
|
||||
collection_name}`` we can do:
|
||||
|
||||
>>> db.command("count", collection_name)
|
||||
OR
|
||||
>>> db.command({"count": collection_name})
|
||||
|
||||
For commands that take additional arguments we can use
|
||||
kwargs. So ``{filemd5: object_id, root: file_root}`` becomes:
|
||||
|
||||
>>> db.command("filemd5", object_id, root=file_root)
|
||||
OR
|
||||
>>> db.command({"filemd5": object_id, "root": file_root})
|
||||
|
||||
:param command: document representing the command to be issued,
|
||||
or the name of the command (for simple commands only).
|
||||
@ -821,8 +826,7 @@ class Database(common.BaseObject, Generic[_DocumentType]):
|
||||
.. note:: the order of keys in the `command` document is
|
||||
significant (the "verb" must come first), so commands
|
||||
which require multiple keys (e.g. `findandmodify`)
|
||||
should use an instance of :class:`~bson.son.SON` or
|
||||
a string and kwargs instead of a Python `dict`.
|
||||
should be done with this in mind.
|
||||
|
||||
:param value: value to use for the command verb when
|
||||
`command` is passed as a string
|
||||
@ -1028,7 +1032,7 @@ class Database(common.BaseObject, Generic[_DocumentType]):
|
||||
Collection[MutableMapping[str, Any]],
|
||||
self.get_collection("$cmd", read_preference=read_preference),
|
||||
)
|
||||
cmd = SON([("listCollections", 1), ("cursor", {})])
|
||||
cmd = {"listCollections": 1, "cursor": {}}
|
||||
cmd.update(kwargs)
|
||||
with self.__client._tmp_session(session, close=False) as tmp_session:
|
||||
cursor = self._command(conn, cmd, read_preference=read_preference, session=tmp_session)[
|
||||
@ -1137,7 +1141,7 @@ class Database(common.BaseObject, Generic[_DocumentType]):
|
||||
def _drop_helper(
|
||||
self, name: str, session: Optional[ClientSession] = None, comment: Optional[Any] = None
|
||||
) -> dict[str, Any]:
|
||||
command = SON([("drop", name)])
|
||||
command = {"drop": name}
|
||||
if comment is not None:
|
||||
command["comment"] = comment
|
||||
|
||||
@ -1277,7 +1281,7 @@ class Database(common.BaseObject, Generic[_DocumentType]):
|
||||
|
||||
if not isinstance(name, str):
|
||||
raise TypeError("name_or_collection must be an instance of str or Collection")
|
||||
cmd = SON([("validate", name), ("scandata", scandata), ("full", full)])
|
||||
cmd = {"validate": name, "scandata": scandata, "full": full}
|
||||
if comment is not None:
|
||||
cmd["comment"] = comment
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ from copy import deepcopy
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Dict,
|
||||
Generic,
|
||||
Iterator,
|
||||
Mapping,
|
||||
@ -49,7 +50,6 @@ from bson.binary import STANDARD, UUID_SUBTYPE, Binary
|
||||
from bson.codec_options import CodecOptions
|
||||
from bson.errors import BSONError
|
||||
from bson.raw_bson import DEFAULT_RAW_BSON_OPTIONS, RawBSONDocument, _inflate_bson
|
||||
from bson.son import SON
|
||||
from pymongo import _csot
|
||||
from pymongo.collection import Collection
|
||||
from pymongo.common import CONNECT_TIMEOUT
|
||||
@ -83,9 +83,8 @@ _HTTPS_PORT = 443
|
||||
_KMS_CONNECT_TIMEOUT = CONNECT_TIMEOUT # CDRIVER-3262 redefined this value to CONNECT_TIMEOUT
|
||||
_MONGOCRYPTD_TIMEOUT_MS = 10000
|
||||
|
||||
|
||||
_DATA_KEY_OPTS: CodecOptions[SON[str, Any]] = CodecOptions(
|
||||
document_class=SON[str, Any], uuid_representation=STANDARD
|
||||
_DATA_KEY_OPTS: CodecOptions[dict[str, Any]] = CodecOptions(
|
||||
document_class=Dict[str, Any], uuid_representation=STANDARD
|
||||
)
|
||||
# Use RawBSONDocument codec options to avoid needlessly decoding
|
||||
# documents from the key vault.
|
||||
@ -388,7 +387,7 @@ class _Encrypter:
|
||||
|
||||
def encrypt(
|
||||
self, database: str, cmd: Mapping[str, Any], codec_options: CodecOptions[_DocumentTypeArg]
|
||||
) -> MutableMapping[str, Any]:
|
||||
) -> dict[str, Any]:
|
||||
"""Encrypt a MongoDB command.
|
||||
|
||||
:param database: The database for this command.
|
||||
|
||||
@ -33,7 +33,6 @@ from typing import (
|
||||
cast,
|
||||
)
|
||||
|
||||
from bson.son import SON
|
||||
from pymongo import ASCENDING
|
||||
from pymongo.errors import (
|
||||
CursorNotFound,
|
||||
@ -124,7 +123,7 @@ def _index_list(
|
||||
return values
|
||||
|
||||
|
||||
def _index_document(index_list: _IndexList) -> SON[str, Any]:
|
||||
def _index_document(index_list: _IndexList) -> dict[str, Any]:
|
||||
"""Helper to generate an index specifying document.
|
||||
|
||||
Takes a list of (key, direction) pairs.
|
||||
@ -136,7 +135,7 @@ def _index_document(index_list: _IndexList) -> SON[str, Any]:
|
||||
if not len(index_list):
|
||||
raise ValueError("key_or_list must not be empty")
|
||||
|
||||
index: SON[str, Any] = SON()
|
||||
index: dict[str, Any] = {}
|
||||
|
||||
if isinstance(index_list, abc.Mapping):
|
||||
for key in index_list:
|
||||
|
||||
@ -35,7 +35,6 @@ from typing import (
|
||||
NoReturn,
|
||||
Optional,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
import bson
|
||||
@ -47,7 +46,6 @@ from bson.raw_bson import (
|
||||
RawBSONDocument,
|
||||
_inflate_bson,
|
||||
)
|
||||
from bson.son import SON
|
||||
|
||||
try:
|
||||
from pymongo import _cmessage # type: ignore[attr-defined]
|
||||
@ -129,7 +127,7 @@ def _maybe_add_read_preference(
|
||||
# the secondaryOkay bit has the same effect).
|
||||
if mode and (mode != ReadPreference.SECONDARY_PREFERRED.mode or len(document) > 1):
|
||||
if "$query" not in spec:
|
||||
spec = SON([("$query", spec)])
|
||||
spec = {"$query": spec}
|
||||
spec["$readPreference"] = document
|
||||
return spec
|
||||
|
||||
@ -175,33 +173,29 @@ def _convert_write_result(
|
||||
return res
|
||||
|
||||
|
||||
_OPTIONS = SON(
|
||||
[
|
||||
("tailable", 2),
|
||||
("oplogReplay", 8),
|
||||
("noCursorTimeout", 16),
|
||||
("awaitData", 32),
|
||||
("allowPartialResults", 128),
|
||||
]
|
||||
)
|
||||
_OPTIONS = {
|
||||
"tailable": 2,
|
||||
"oplogReplay": 8,
|
||||
"noCursorTimeout": 16,
|
||||
"awaitData": 32,
|
||||
"allowPartialResults": 128,
|
||||
}
|
||||
|
||||
|
||||
_MODIFIERS = SON(
|
||||
[
|
||||
("$query", "filter"),
|
||||
("$orderby", "sort"),
|
||||
("$hint", "hint"),
|
||||
("$comment", "comment"),
|
||||
("$maxScan", "maxScan"),
|
||||
("$maxTimeMS", "maxTimeMS"),
|
||||
("$max", "max"),
|
||||
("$min", "min"),
|
||||
("$returnKey", "returnKey"),
|
||||
("$showRecordId", "showRecordId"),
|
||||
("$showDiskLoc", "showRecordId"), # <= MongoDb 3.0
|
||||
("$snapshot", "snapshot"),
|
||||
]
|
||||
)
|
||||
_MODIFIERS = {
|
||||
"$query": "filter",
|
||||
"$orderby": "sort",
|
||||
"$hint": "hint",
|
||||
"$comment": "comment",
|
||||
"$maxScan": "maxScan",
|
||||
"$maxTimeMS": "maxTimeMS",
|
||||
"$max": "max",
|
||||
"$min": "min",
|
||||
"$returnKey": "returnKey",
|
||||
"$showRecordId": "showRecordId",
|
||||
"$showDiskLoc": "showRecordId", # <= MongoDb 3.0
|
||||
"$snapshot": "snapshot",
|
||||
}
|
||||
|
||||
|
||||
def _gen_find_command(
|
||||
@ -216,9 +210,9 @@ def _gen_find_command(
|
||||
collation: Optional[Mapping[str, Any]] = None,
|
||||
session: Optional[ClientSession] = None,
|
||||
allow_disk_use: Optional[bool] = None,
|
||||
) -> SON[str, Any]:
|
||||
) -> dict[str, Any]:
|
||||
"""Generate a find command document."""
|
||||
cmd: SON[str, Any] = SON([("find", coll)])
|
||||
cmd: dict[str, Any] = {"find": coll}
|
||||
if "$query" in spec:
|
||||
cmd.update(
|
||||
[
|
||||
@ -262,9 +256,9 @@ def _gen_get_more_command(
|
||||
max_await_time_ms: Optional[int],
|
||||
comment: Optional[Any],
|
||||
conn: Connection,
|
||||
) -> SON[str, Any]:
|
||||
) -> dict[str, Any]:
|
||||
"""Generate a getMore command document."""
|
||||
cmd: SON[str, Any] = SON([("getMore", cursor_id), ("collection", coll)])
|
||||
cmd: dict[str, Any] = {"getMore": cursor_id, "collection": coll}
|
||||
if batch_size:
|
||||
cmd["batchSize"] = batch_size
|
||||
if max_await_time_ms is not None:
|
||||
@ -337,7 +331,7 @@ class _Query:
|
||||
self.client = client
|
||||
self.allow_disk_use = allow_disk_use
|
||||
self.name = "find"
|
||||
self._as_command: Optional[tuple[SON[str, Any], str]] = None
|
||||
self._as_command: Optional[tuple[dict[str, Any], str]] = None
|
||||
self.exhaust = exhaust
|
||||
|
||||
def reset(self) -> None:
|
||||
@ -364,7 +358,7 @@ class _Query:
|
||||
|
||||
def as_command(
|
||||
self, conn: Connection, apply_timeout: bool = False
|
||||
) -> tuple[SON[str, Any], str]:
|
||||
) -> tuple[dict[str, Any], str]:
|
||||
"""Return a find command document for this query."""
|
||||
# We use the command twice: on the wire and for command monitoring.
|
||||
# Generate it once, for speed and to avoid repeating side-effects.
|
||||
@ -372,7 +366,7 @@ class _Query:
|
||||
return self._as_command
|
||||
|
||||
explain = "$explain" in self.spec
|
||||
cmd: SON[str, Any] = _gen_find_command(
|
||||
cmd: dict[str, Any] = _gen_find_command(
|
||||
self.coll,
|
||||
self.spec,
|
||||
self.fields,
|
||||
@ -387,7 +381,7 @@ class _Query:
|
||||
)
|
||||
if explain:
|
||||
self.name = "explain"
|
||||
cmd = SON([("explain", cmd)])
|
||||
cmd = {"explain": cmd}
|
||||
session = self.session
|
||||
conn.add_server_api(cmd)
|
||||
if session:
|
||||
@ -399,7 +393,7 @@ class _Query:
|
||||
# Support auto encryption
|
||||
client = self.client
|
||||
if client._encrypter and not client._encrypter._bypass_auto_encryption:
|
||||
cmd = cast(SON[str, Any], client._encrypter.encrypt(self.db, cmd, self.codec_options))
|
||||
cmd = client._encrypter.encrypt(self.db, cmd, self.codec_options)
|
||||
# Support CSOT
|
||||
if apply_timeout:
|
||||
conn.apply_timeout(client, cmd)
|
||||
@ -505,7 +499,7 @@ class _GetMore:
|
||||
self.client = client
|
||||
self.max_await_time_ms = max_await_time_ms
|
||||
self.conn_mgr = conn_mgr
|
||||
self._as_command: Optional[tuple[SON[str, Any], str]] = None
|
||||
self._as_command: Optional[tuple[dict[str, Any], str]] = None
|
||||
self.exhaust = exhaust
|
||||
self.comment = comment
|
||||
|
||||
@ -528,13 +522,13 @@ class _GetMore:
|
||||
|
||||
def as_command(
|
||||
self, conn: Connection, apply_timeout: bool = False
|
||||
) -> tuple[SON[str, Any], str]:
|
||||
) -> tuple[dict[str, Any], str]:
|
||||
"""Return a getMore command document for this query."""
|
||||
# See _Query.as_command for an explanation of this caching.
|
||||
if self._as_command is not None:
|
||||
return self._as_command
|
||||
|
||||
cmd: SON[str, Any] = _gen_get_more_command(
|
||||
cmd: dict[str, Any] = _gen_get_more_command(
|
||||
self.cursor_id,
|
||||
self.coll,
|
||||
self.ntoreturn,
|
||||
@ -549,7 +543,7 @@ class _GetMore:
|
||||
# Support auto encryption
|
||||
client = self.client
|
||||
if client._encrypter and not client._encrypter._bypass_auto_encryption:
|
||||
cmd = cast(SON[str, Any], client._encrypter.encrypt(self.db, cmd, self.codec_options))
|
||||
cmd = client._encrypter.encrypt(self.db, cmd, self.codec_options)
|
||||
# Support CSOT
|
||||
if apply_timeout:
|
||||
conn.apply_timeout(client, cmd=None)
|
||||
@ -1129,7 +1123,7 @@ class _EncryptedBulkWriteContext(_BulkWriteContext):
|
||||
|
||||
def __batch_command(
|
||||
self, cmd: MutableMapping[str, Any], docs: list[Mapping[str, Any]]
|
||||
) -> tuple[MutableMapping[str, Any], list[Mapping[str, Any]]]:
|
||||
) -> tuple[dict[str, Any], list[Mapping[str, Any]]]:
|
||||
namespace = self.db_name + ".$cmd"
|
||||
msg, to_send = _encode_batched_write_command(
|
||||
namespace, self.op_type, cmd, docs, self.codec, self
|
||||
|
||||
@ -57,7 +57,6 @@ from typing import (
|
||||
|
||||
import bson
|
||||
from bson.codec_options import DEFAULT_CODEC_OPTIONS, TypeRegistry
|
||||
from bson.son import SON
|
||||
from bson.timestamp import Timestamp
|
||||
from pymongo import (
|
||||
_csot,
|
||||
@ -1190,7 +1189,7 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
return
|
||||
|
||||
for i in range(0, len(session_ids), common._MAX_END_SESSIONS):
|
||||
spec = SON([("endSessions", session_ids[i : i + common._MAX_END_SESSIONS])])
|
||||
spec = {"endSessions": session_ids[i : i + common._MAX_END_SESSIONS]}
|
||||
conn.command("admin", spec, read_preference=read_pref, client=self)
|
||||
except PyMongoError:
|
||||
# Drivers MUST ignore any errors returned by the endSessions
|
||||
@ -1693,7 +1692,7 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
) -> None:
|
||||
namespace = address.namespace
|
||||
db, coll = namespace.split(".", 1)
|
||||
spec = SON([("killCursors", coll), ("cursors", cursor_ids)])
|
||||
spec = {"killCursors": coll, "cursors": cursor_ids}
|
||||
conn.command(db, spec, session=session, client=self)
|
||||
|
||||
def _process_kill_cursors(self) -> None:
|
||||
@ -1903,7 +1902,7 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
|
||||
.. versionadded:: 3.6
|
||||
"""
|
||||
cmd = SON([("listDatabases", 1)])
|
||||
cmd = {"listDatabases": 1}
|
||||
cmd.update(kwargs)
|
||||
if comment is not None:
|
||||
cmd["comment"] = comment
|
||||
|
||||
@ -35,7 +35,6 @@ from pymongo.typings import _CollationIn, _DocumentType, _Pipeline
|
||||
from pymongo.write_concern import validate_boolean
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bson.son import SON
|
||||
from pymongo.bulk import _Bulk
|
||||
|
||||
# Hint supports index name, "myIndex", a list of either strings or index pairs: [('x', 1), ('y', -1), 'z''], or a dictionary
|
||||
@ -109,7 +108,7 @@ class DeleteOne:
|
||||
if filter is not None:
|
||||
validate_is_mapping("filter", filter)
|
||||
if hint is not None and not isinstance(hint, str):
|
||||
self._hint: Union[str, SON[str, Any], None] = helpers._index_document(hint)
|
||||
self._hint: Union[str, dict[str, Any], None] = helpers._index_document(hint)
|
||||
else:
|
||||
self._hint = hint
|
||||
self._filter = filter
|
||||
@ -173,7 +172,7 @@ class DeleteMany:
|
||||
if filter is not None:
|
||||
validate_is_mapping("filter", filter)
|
||||
if hint is not None and not isinstance(hint, str):
|
||||
self._hint: Union[str, SON[str, Any], None] = helpers._index_document(hint)
|
||||
self._hint: Union[str, dict[str, Any], None] = helpers._index_document(hint)
|
||||
else:
|
||||
self._hint = hint
|
||||
self._filter = filter
|
||||
@ -244,7 +243,7 @@ class ReplaceOne(Generic[_DocumentType]):
|
||||
if upsert is not None:
|
||||
validate_boolean("upsert", upsert)
|
||||
if hint is not None and not isinstance(hint, str):
|
||||
self._hint: Union[str, SON[str, Any], None] = helpers._index_document(hint)
|
||||
self._hint: Union[str, dict[str, Any], None] = helpers._index_document(hint)
|
||||
else:
|
||||
self._hint = hint
|
||||
self._filter = filter
|
||||
@ -314,7 +313,7 @@ class _UpdateOp:
|
||||
if array_filters is not None:
|
||||
validate_list("array_filters", array_filters)
|
||||
if hint is not None and not isinstance(hint, str):
|
||||
self._hint: Union[str, SON[str, Any], None] = helpers._index_document(hint)
|
||||
self._hint: Union[str, dict[str, Any], None] = helpers._index_document(hint)
|
||||
else:
|
||||
self._hint = hint
|
||||
|
||||
|
||||
103
pymongo/pool.py
103
pymongo/pool.py
@ -40,7 +40,6 @@ from typing import (
|
||||
|
||||
import bson
|
||||
from bson import DEFAULT_CODEC_OPTIONS
|
||||
from bson.son import SON
|
||||
from pymongo import __version__, _csot, auth, helpers
|
||||
from pymongo.client_session import _validate_session_write_concern
|
||||
from pymongo.common import (
|
||||
@ -180,11 +179,7 @@ else:
|
||||
_set_tcp_option(sock, "TCP_KEEPCNT", _MAX_TCP_KEEPCNT)
|
||||
|
||||
|
||||
_METADATA: SON[str, Any] = SON(
|
||||
[
|
||||
("driver", SON([("name", "PyMongo"), ("version", __version__)])),
|
||||
]
|
||||
)
|
||||
_METADATA: dict[str, Any] = {"driver": {"name": "PyMongo", "version": __version__}}
|
||||
|
||||
if sys.platform.startswith("linux"):
|
||||
# platform.linux_distribution was deprecated in Python 3.5
|
||||
@ -192,61 +187,51 @@ if sys.platform.startswith("linux"):
|
||||
# raises DeprecationWarning
|
||||
# DeprecationWarning: dist() and linux_distribution() functions are deprecated in Python 3.5
|
||||
_name = platform.system()
|
||||
_METADATA["os"] = SON(
|
||||
[
|
||||
("type", _name),
|
||||
("name", _name),
|
||||
("architecture", platform.machine()),
|
||||
# Kernel version (e.g. 4.4.0-17-generic).
|
||||
("version", platform.release()),
|
||||
]
|
||||
)
|
||||
_METADATA["os"] = {
|
||||
"type": _name,
|
||||
"name": _name,
|
||||
"architecture": platform.machine(),
|
||||
# Kernel version (e.g. 4.4.0-17-generic).
|
||||
"version": platform.release(),
|
||||
}
|
||||
elif sys.platform == "darwin":
|
||||
_METADATA["os"] = SON(
|
||||
[
|
||||
("type", platform.system()),
|
||||
("name", platform.system()),
|
||||
("architecture", platform.machine()),
|
||||
# (mac|i|tv)OS(X) version (e.g. 10.11.6) instead of darwin
|
||||
# kernel version.
|
||||
("version", platform.mac_ver()[0]),
|
||||
]
|
||||
)
|
||||
_METADATA["os"] = {
|
||||
"type": platform.system(),
|
||||
"name": platform.system(),
|
||||
"architecture": platform.machine(),
|
||||
# (mac|i|tv)OS(X) version (e.g. 10.11.6) instead of darwin
|
||||
# kernel version.
|
||||
"version": platform.mac_ver()[0],
|
||||
}
|
||||
elif sys.platform == "win32":
|
||||
_METADATA["os"] = SON(
|
||||
[
|
||||
("type", platform.system()),
|
||||
# "Windows XP", "Windows 7", "Windows 10", etc.
|
||||
("name", " ".join((platform.system(), platform.release()))),
|
||||
("architecture", platform.machine()),
|
||||
# Windows patch level (e.g. 5.1.2600-SP3)
|
||||
("version", "-".join(platform.win32_ver()[1:3])),
|
||||
]
|
||||
)
|
||||
_METADATA["os"] = {
|
||||
"type": platform.system(),
|
||||
# "Windows XP", "Windows 7", "Windows 10", etc.
|
||||
"name": " ".join((platform.system(), platform.release())),
|
||||
"architecture": platform.machine(),
|
||||
# Windows patch level (e.g. 5.1.2600-SP3)
|
||||
"version": "-".join(platform.win32_ver()[1:3]),
|
||||
}
|
||||
elif sys.platform.startswith("java"):
|
||||
_name, _ver, _arch = platform.java_ver()[-1]
|
||||
_METADATA["os"] = SON(
|
||||
[
|
||||
# Linux, Windows 7, Mac OS X, etc.
|
||||
("type", _name),
|
||||
("name", _name),
|
||||
# x86, x86_64, AMD64, etc.
|
||||
("architecture", _arch),
|
||||
# Linux kernel version, OSX version, etc.
|
||||
("version", _ver),
|
||||
]
|
||||
)
|
||||
_METADATA["os"] = {
|
||||
# Linux, Windows 7, Mac OS X, etc.
|
||||
"type": _name,
|
||||
"name": _name,
|
||||
# x86, x86_64, AMD64, etc.
|
||||
"architecture": _arch,
|
||||
# Linux kernel version, OSX version, etc.
|
||||
"version": _ver,
|
||||
}
|
||||
else:
|
||||
# Get potential alias (e.g. SunOS 5.11 becomes Solaris 2.11)
|
||||
_aliased = platform.system_alias(platform.system(), platform.release(), platform.version())
|
||||
_METADATA["os"] = SON(
|
||||
[
|
||||
("type", platform.system()),
|
||||
("name", " ".join([part for part in _aliased[:2] if part])),
|
||||
("architecture", platform.machine()),
|
||||
("version", _aliased[2]),
|
||||
]
|
||||
)
|
||||
_METADATA["os"] = {
|
||||
"type": platform.system(),
|
||||
"name": " ".join([part for part in _aliased[:2] if part]),
|
||||
"architecture": platform.machine(),
|
||||
"version": _aliased[2],
|
||||
}
|
||||
|
||||
if platform.python_implementation().startswith("PyPy"):
|
||||
_METADATA["platform"] = " ".join(
|
||||
@ -682,7 +667,7 @@ class PoolOptions:
|
||||
return self.__compression_settings
|
||||
|
||||
@property
|
||||
def metadata(self) -> SON[str, Any]:
|
||||
def metadata(self) -> dict[str, Any]:
|
||||
"""A dict of metadata about the application, driver, os, and platform."""
|
||||
return self.__metadata.copy()
|
||||
|
||||
@ -824,14 +809,14 @@ class Connection:
|
||||
else:
|
||||
self.close_conn(ConnectionClosedReason.STALE)
|
||||
|
||||
def hello_cmd(self) -> SON[str, Any]:
|
||||
def hello_cmd(self) -> dict[str, Any]:
|
||||
# Handshake spec requires us to use OP_MSG+hello command for the
|
||||
# initial handshake in load balanced or stable API mode.
|
||||
if self.opts.server_api or self.hello_ok or self.opts.load_balanced:
|
||||
self.op_msg_enabled = True
|
||||
return SON([(HelloCompat.CMD, 1)])
|
||||
return {HelloCompat.CMD: 1}
|
||||
else:
|
||||
return SON([(HelloCompat.LEGACY_CMD, 1), ("helloOk", True)])
|
||||
return {HelloCompat.LEGACY_CMD: 1, "helloOk": True}
|
||||
|
||||
def hello(self) -> Hello[dict[str, Any]]:
|
||||
return self._hello(None, None, None)
|
||||
@ -974,7 +959,7 @@ class Connection:
|
||||
|
||||
# Ensure command name remains in first place.
|
||||
if not isinstance(spec, ORDERED_TYPES): # type:ignore[arg-type]
|
||||
spec = SON(spec)
|
||||
spec = dict(spec)
|
||||
|
||||
if not (write_concern is None or write_concern.acknowledged or collation is None):
|
||||
raise ConfigurationError("Collation is unsupported for unacknowledged writes.")
|
||||
|
||||
@ -2124,9 +2124,7 @@ class TestCollection(IntegrationTest):
|
||||
None,
|
||||
None,
|
||||
)
|
||||
self.assertEqual(
|
||||
cmd.to_dict(), SON([("find", "coll"), ("$dumb", 2), ("filter", {"foo": 1})]).to_dict()
|
||||
)
|
||||
self.assertEqual(cmd, {"find": "coll", "$dumb": 2, "filter": {"foo": 1}})
|
||||
|
||||
def test_bool(self):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
|
||||
@ -912,7 +912,8 @@ class TestCursor(IntegrationTest):
|
||||
# Ensure hints are cloned as the correct type
|
||||
cursor = self.db.test.find().hint([("z", 1), ("a", 1)])
|
||||
cursor2 = copy.deepcopy(cursor)
|
||||
self.assertTrue(isinstance(cursor2._Cursor__hint, SON))
|
||||
# Internal types are now dict rather than SON by default
|
||||
self.assertTrue(isinstance(cursor2._Cursor__hint, dict))
|
||||
self.assertEqual(cursor._Cursor__hint, cursor2._Cursor__hint)
|
||||
|
||||
def test_clone_empty(self):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user