Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,052 changes: 462 additions & 590 deletions .evergreen/generated_configs/tasks.yml

Large diffs are not rendered by default.

9 changes: 0 additions & 9 deletions .evergreen/generated_configs/variants.yml
Original file line number Diff line number Diff line change
Expand Up @@ -491,15 +491,6 @@ buildvariants:
- rhel87-small

# Server version tests
- name: mongodb-v4.2
tasks:
- name: .server-version
display_name: "* MongoDB v4.2"
run_on:
- rhel87-small
expansions:
VERSION: "4.2"
tags: [coverage_tag]
- name: mongodb-v4.4
tasks:
- name: .server-version
Expand Down
2 changes: 1 addition & 1 deletion .evergreen/scripts/generate_config_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# Globals
##############

ALL_VERSIONS = ["4.2", "4.4", "5.0", "6.0", "7.0", "8.0", "rapid", "latest"]
ALL_VERSIONS = ["4.4", "5.0", "6.0", "7.0", "8.0", "rapid", "latest"]
CPYTHONS = ["3.10", "3.11", "3.12", "3.13", "3.14t", "3.14"]
PYPYS = ["pypy3.11"]
MIN_SUPPORT_VERSIONS = ["3.9", "pypy3.9", "pypy3.10"]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ a native Python driver for MongoDB, offering both synchronous and asynchronous A
[gridfs](https://github.com/mongodb/specifications/blob/master/source/gridfs/gridfs-spec.md/)
implementation on top of `pymongo`.

PyMongo supports MongoDB 4.0, 4.2, 4.4, 5.0, 6.0, 7.0, and 8.0. PyMongo follows [semantic versioning](https://semver.org/spec/v2.0.0.html) for its releases.
PyMongo supports MongoDB 4.4, 5.0, 6.0, 7.0, and 8.0. PyMongo follows [semantic versioning](https://semver.org/spec/v2.0.0.html) for its releases.

## Documentation

Expand Down
1 change: 1 addition & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog
Changes in Version 4.18.0
-------------------------

- Dropped support for MongoDB 4.2. PyMongo now requires MongoDB 4.4 or later.
- Improved TLS connection performance by reusing TLS sessions across connections
to the same server, avoiding a full handshake on each new connection.
Session resumption is supported on all Python versions for synchronous clients
Expand Down
10 changes: 3 additions & 7 deletions pymongo/asynchronous/aggregation.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,9 @@ async def get_cursor(
cmd = {"aggregate": self._aggregation_target, "pipeline": self._pipeline}
cmd.update(self._options)

# Apply this target's read concern if:
# readConcern has not been specified as a kwarg and either
# - server version is >= 4.2 or
# - server version is >= 3.2 and pipeline doesn't use $out
if ("readConcern" not in cmd) and (
not self._performs_write or (conn.max_wire_version >= 8)
):
# Apply this target's read concern if readConcern has not been specified as a kwarg.
# $out/$merge pipelines also support readConcern on all supported server versions (4.4+).
if "readConcern" not in cmd:
read_concern = self._target.read_concern
else:
read_concern = None
Expand Down
4 changes: 0 additions & 4 deletions pymongo/asynchronous/bulk.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,10 +576,6 @@ async def execute_no_results(
raise ConfigurationError(
"Must be connected to MongoDB 4.4+ to use hint on unacknowledged delete commands."
)
if unack and self.uses_hint_update and conn.max_wire_version < 8:
raise ConfigurationError(
"Must be connected to MongoDB 4.2+ to use hint on unacknowledged update commands."
)
if unack and self.uses_sort and conn.max_wire_version < 25:
raise ConfigurationError(
"Must be connected to MongoDB 8.0+ to use sort on unacknowledged update commands."
Expand Down
12 changes: 0 additions & 12 deletions pymongo/asynchronous/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -1010,10 +1010,6 @@ async def _update(
else:
update_doc["arrayFilters"] = array_filters
if hint is not None:
if not acknowledged and conn.max_wire_version < 8:
raise ConfigurationError(
"Must be connected to MongoDB 4.2+ to use hint on unacknowledged update commands."
)
if not isinstance(hint, str):
hint = helpers_shared._index_document(hint)
update_doc["hint"] = hint
Expand Down Expand Up @@ -3295,14 +3291,6 @@ async def _find_and_modify_helper(
)
cmd["arrayFilters"] = list(array_filters)
if hint is not None:
if conn.max_wire_version < 8:
raise ConfigurationError(
"Must be connected to MongoDB 4.2+ to use hint on find and modify commands."
)
elif not acknowledged and conn.max_wire_version < 9:
raise ConfigurationError(
"Must be connected to MongoDB 4.4+ to use hint on unacknowledged find and modify commands."
)
cmd["hint"] = hint
out = await self._command(
conn,
Expand Down
8 changes: 0 additions & 8 deletions pymongo/asynchronous/mongo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1804,14 +1804,6 @@ async def _checkout(
):
session._pin(server, conn)
err_handler.contribute_socket(conn)
if (
self._encrypter
and not self._encrypter._bypass_auto_encryption
and conn.max_wire_version < 8
):
raise ConfigurationError(
"Auto-encryption requires a minimum MongoDB version of 4.2"
)
yield conn

async def _select_server(
Expand Down
10 changes: 4 additions & 6 deletions pymongo/asynchronous/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,18 @@ async def run_operation(
assert listeners is not None
start = datetime.now()

use_cmd = operation.use_command(conn)
conn.validate_session(operation.client, operation.session) # type: ignore[arg-type]
more_to_come = bool(operation.conn_mgr and operation.conn_mgr.more_to_come)
cmd, dbn = await self.operation_to_command(operation, conn, use_cmd)
cmd, dbn = await self.operation_to_command(operation, conn, True)
if more_to_come:
request_id = 0
data = b""
max_doc_size = 0
else:
message = operation.get_message(read_preference, conn, use_cmd)
message = operation.get_message(read_preference, conn)
request_id, data, max_doc_size = self._split_message(message)

user_fields = _CURSOR_DOC_FIELDS if use_cmd else None
user_fields = _CURSOR_DOC_FIELDS

docs, reply, duration = await run_cursor_command(
conn,
Expand Down Expand Up @@ -211,7 +211,6 @@ async def run_operation(
conn=conn,
duration=duration,
request_id=request_id,
from_command=use_cmd,
docs=docs, # type: ignore[arg-type]
more_to_come=more_to_come,
)
Expand All @@ -221,7 +220,6 @@ async def run_operation(
address=self._description.address,
duration=duration,
request_id=request_id,
from_command=use_cmd,
docs=docs, # type: ignore[arg-type]
)

Expand Down
4 changes: 2 additions & 2 deletions pymongo/asynchronous/topology.py
Original file line number Diff line number Diff line change
Expand Up @@ -899,10 +899,10 @@ async def _handle_error(self, address: _Address, err_ctx: _ErrorContext) -> None
err_code = error.details.get("code", default) # type: ignore[union-attr]
if err_code in helpers_shared._NOT_PRIMARY_CODES:
is_shutting_down = err_code in helpers_shared._SHUTDOWN_CODES
# Mark server Unknown, clear the pool, and request check.
# Mark server Unknown and request check. Clear the pool only on shutdown.
if not self._settings.load_balanced:
await self._process_change(ServerDescription(address, error=error))
if is_shutting_down or (err_ctx.max_wire_version <= 7):
if is_shutting_down:
# Clear the pool.
await server.reset(service_id)
server.request_check()
Expand Down
4 changes: 2 additions & 2 deletions pymongo/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@
MAX_WRITE_BATCH_SIZE = 100000

# What this version of PyMongo supports.
MIN_SUPPORTED_SERVER_VERSION = "4.2"
MIN_SUPPORTED_WIRE_VERSION = 8
MIN_SUPPORTED_SERVER_VERSION = "4.4"
MIN_SUPPORTED_WIRE_VERSION = 9
# MongoDB 9.0
MAX_SUPPORTED_WIRE_VERSION = 29

Expand Down
123 changes: 10 additions & 113 deletions pymongo/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
except ImportError:
_use_c = False
from pymongo.errors import (
ConfigurationError,
DocumentTooLarge,
InvalidOperation,
ProtocolError,
Expand Down Expand Up @@ -379,54 +378,6 @@ def _op_msg(
command[identifier] = docs


_pack_long_long = struct.Struct("<q").pack


def _get_more_impl(collection_name: str, num_to_return: int, cursor_id: int) -> bytes:
"""Get an OP_GET_MORE message."""
return b"".join(
[
_ZERO_32,
bson._make_c_string(collection_name),
_pack_int(num_to_return),
_pack_long_long(cursor_id),
]
)


def _get_more_compressed(
collection_name: str,
num_to_return: int,
cursor_id: int,
ctx: Union[SnappyContext, ZlibContext, ZstdContext],
) -> tuple[int, bytes]:
"""Internal compressed getMore message helper."""
return _compress(2005, _get_more_impl(collection_name, num_to_return, cursor_id), ctx)


def _get_more_uncompressed(
collection_name: str, num_to_return: int, cursor_id: int
) -> tuple[int, bytes]:
"""Internal getMore message helper."""
return __pack_message(2005, _get_more_impl(collection_name, num_to_return, cursor_id))


if _use_c:
_get_more_uncompressed = _cmessage._get_more_message


def _get_more(
collection_name: str,
num_to_return: int,
cursor_id: int,
ctx: Union[SnappyContext, ZlibContext, ZstdContext, None] = None,
) -> tuple[int, bytes]:
"""Get a **getMore** message."""
if ctx:
return _get_more_compressed(collection_name, num_to_return, cursor_id, ctx)
return _get_more_uncompressed(collection_name, num_to_return, cursor_id)


# OP_MSG -------------------------------------------------------------


Expand Down Expand Up @@ -1295,22 +1246,6 @@ def reset(self) -> None:
def namespace(self) -> str:
return f"{self.db}.{self.coll}"

def use_command(self, conn: _AgnosticConnection) -> bool:
use_find_cmd = False
if not self.exhaust:
use_find_cmd = True
elif conn.max_wire_version >= 8:
# OP_MSG supports exhaust on MongoDB 4.2+
use_find_cmd = True
elif not self.read_concern.ok_for_legacy:
raise ConfigurationError(
f"read concern level of {self.read_concern.level} is not valid "
f"with a max wire version of {conn.max_wire_version}."
)

conn.validate_session(self.client, self.session) # type: ignore[arg-type]
return use_find_cmd

def update_command(self, cmd: dict[str, Any]) -> None:
self._as_command = cmd, self.db

Expand Down Expand Up @@ -1354,7 +1289,7 @@ def as_command(
return self._as_command

def get_message(
self, read_preference: _ServerMode, conn: _AgnosticConnection, use_cmd: bool = False
self, read_preference: _ServerMode, conn: _AgnosticConnection
) -> tuple[int, bytes, int]:
"""Get a query message"""
# Use the read_preference decided by _socket_from_server.
Expand Down Expand Up @@ -1428,17 +1363,6 @@ def reset(self) -> None:
def namespace(self) -> str:
return f"{self.db}.{self.coll}"

def use_command(self, conn: _AgnosticConnection) -> bool:
use_cmd = False
if not self.exhaust:
use_cmd = True
elif conn.max_wire_version >= 8:
# OP_MSG supports exhaust on MongoDB 4.2+
use_cmd = True

conn.validate_session(self.client, self.session) # type: ignore[arg-type]
return use_cmd

def update_command(self, cmd: dict[str, Any]) -> None:
self._as_command = cmd, self.db

Expand Down Expand Up @@ -1468,49 +1392,22 @@ def as_command(
self._as_command = cmd, self.db
return self._as_command

def get_message(
self, dummy0: Any, conn: _AgnosticConnection, use_cmd: bool = False
) -> Union[tuple[int, bytes, int], tuple[int, bytes]]:
def get_message(self, dummy0: Any, conn: _AgnosticConnection) -> tuple[int, bytes, int]:
"""Get a getmore message."""
ns = self.namespace()
ctx = conn.compression_context

if use_cmd:
spec = self.as_command(conn)[0]
if self.conn_mgr and self.exhaust:
flags = _OpMsg.EXHAUST_ALLOWED
else:
flags = 0
request_id, msg, size, _ = _op_msg(
flags, spec, self.db, None, self.codec_options, ctx=conn.compression_context
)
return request_id, msg, size

return _get_more(ns, self.ntoreturn, self.cursor_id, ctx)
spec = self.as_command(conn)[0]
flags = _OpMsg.EXHAUST_ALLOWED if (self.conn_mgr and self.exhaust) else 0
request_id, msg, size, _ = _op_msg(
flags, spec, self.db, None, self.codec_options, ctx=conn.compression_context
)
return request_id, msg, size


class _RawBatchQuery(_Query):
def use_command(self, conn: _AgnosticConnection) -> bool:
# Compatibility checks.
super().use_command(conn)
if conn.max_wire_version >= 8:
# MongoDB 4.2+ supports exhaust over OP_MSG
return True
elif not self.exhaust:
return True
return False
pass


class _RawBatchGetMore(_GetMore):
def use_command(self, conn: _AgnosticConnection) -> bool:
# Compatibility checks.
super().use_command(conn)
if conn.max_wire_version >= 8:
# MongoDB 4.2+ supports exhaust over OP_MSG
return True
elif not self.exhaust:
return True
return False
pass


class _CursorAddress(tuple[Any, ...]):
Expand Down
Loading
Loading