Compare commits

...

15 Commits

Author SHA1 Message Date
mongodb-dbx-release-bot[bot]
27fac499c1
BUMP 4.14.2.dev0
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2025-08-19 18:26:36 +00:00
Iris
0d2a4b462e
Prep for 4.14.1 release (#2495) 2025-08-19 10:59:54 -07:00
mongodb-dbx-release-bot[bot]
550d234f71
PYTHON-5503 Use uv to install just in GitHub Actions (#2490) [v4.14] (#2493)
Co-authored-by: Steven Silvester <steven.silvester@ieee.org>
2025-08-19 11:38:30 -05:00
Steven Silvester
d98049c630
PYTHON-5502 [v4.14] Fix handling of c extensions in Azure and GCP VMs (#2487) 2025-08-19 11:27:51 -05:00
mongodb-dbx-release-bot[bot]
f66ec0ff23
PYTHON-5492 Fix handling of MaxTimeMS message (#2484) [v4.14] (#2485)
Co-authored-by: Steven Silvester <steven.silvester@ieee.org>
2025-08-18 20:28:27 -05:00
Steven Silvester
6611bec9e7
PYTHON-5493 [v4.14] Add a patch for the log order difference (#2482) 2025-08-18 15:03:15 -05:00
mongodb-dbx-release-bot[bot]
7692bd67b9
PYTHON-5488 append_metadata should not add duplicates (#2461) [v4.14] (#2483)
Co-authored-by: Iris <58442094+sleepyStick@users.noreply.github.com>
2025-08-18 15:03:02 -05:00
mongodb-dbx-release-bot[bot]
aa0b920566
PYTHON-5492 Fix handling of MaxTimeMSExpired responses (#2477) [v4.14] (#2479)
Co-authored-by: Steven Silvester <steven.silvester@ieee.org>
2025-08-18 06:32:51 -05:00
mongodb-dbx-release-bot[bot]
1c480163f4
PYTHON-5349 Use drivers-evergreen-tools to start servers in GitHub Actions (#2474) [v4.14] (#2476)
Co-authored-by: Steven Silvester <steven.silvester@ieee.org>
2025-08-15 12:03:01 -05:00
mongodb-dbx-release-bot[bot]
7055ad1944
PYTHON-5492 Mark test as flaky (#2472) [v4.14] (#2475)
Co-authored-by: Steven Silvester <steven.silvester@ieee.org>
2025-08-15 12:01:53 -05:00
mongodb-dbx-release-bot[bot]
e80f4f4199
PYTHON-5491 Skip non-idempotent dropIndex tests (#2467) [v4.14] (#2468)
Co-authored-by: Steven Silvester <steven.silvester@ieee.org>
2025-08-11 08:27:21 -05:00
Steven Silvester
c4e866d5d7
PYTHON-5487 [v4.14] Update 4.14 changelog to mention MongoDB 4.0 is no longer supported (#2464)
Co-authored-by: Jeffrey A. Clark <aclark@aclark.net>
Co-authored-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2025-08-07 13:26:50 -05:00
mongodb-dbx-release-bot[bot]
ce2812d974
Prep branch v4.14
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2025-08-07 15:40:53 +00:00
mongodb-dbx-release-bot[bot]
952fa82c24
BUMP 4.15.0.dev0
Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com>
2025-08-06 13:44:58 +00:00
Jeffrey A. Clark
6354d6cae7
Prepare 4.14.0 release (#2458) 2025-08-06 08:52:42 -04:00
32 changed files with 383 additions and 118 deletions

View File

@ -535,6 +535,8 @@ buildvariants:
display_name: "* MongoDB v4.2"
run_on:
- rhel87-small
expansions:
VERSION: "4.2"
tags: [coverage_tag]
- name: mongodb-v4.4
tasks:
@ -542,6 +544,8 @@ buildvariants:
display_name: "* MongoDB v4.4"
run_on:
- rhel87-small
expansions:
VERSION: "4.4"
tags: [coverage_tag]
- name: mongodb-v5.0
tasks:
@ -549,6 +553,8 @@ buildvariants:
display_name: "* MongoDB v5.0"
run_on:
- rhel87-small
expansions:
VERSION: "5.0"
tags: [coverage_tag]
- name: mongodb-v6.0
tasks:
@ -556,6 +562,8 @@ buildvariants:
display_name: "* MongoDB v6.0"
run_on:
- rhel87-small
expansions:
VERSION: "6.0"
tags: [coverage_tag]
- name: mongodb-v7.0
tasks:
@ -563,6 +571,8 @@ buildvariants:
display_name: "* MongoDB v7.0"
run_on:
- rhel87-small
expansions:
VERSION: "7.0"
tags: [coverage_tag]
- name: mongodb-v8.0
tasks:
@ -570,6 +580,8 @@ buildvariants:
display_name: "* MongoDB v8.0"
run_on:
- rhel87-small
expansions:
VERSION: "8.0"
tags: [coverage_tag]
- name: mongodb-rapid
tasks:
@ -577,6 +589,8 @@ buildvariants:
display_name: "* MongoDB rapid"
run_on:
- rhel87-small
expansions:
VERSION: rapid
tags: [coverage_tag]
- name: mongodb-latest
tasks:
@ -584,6 +598,8 @@ buildvariants:
display_name: "* MongoDB latest"
run_on:
- rhel87-small
expansions:
VERSION: latest
tags: [coverage_tag]
# Stable api tests

View File

@ -8,7 +8,9 @@ if [ ${OIDC_ENV} == "k8s" ]; then
SUB_TEST_NAME=$K8S_VARIANT-remote
else
SUB_TEST_NAME=$OIDC_ENV-remote
sudo apt-get install -y python3-dev build-essential
fi
bash ./.evergreen/just.sh setup-tests auth_oidc $SUB_TEST_NAME
bash ./.evergreen/just.sh run-tests "${@:1}"

View File

@ -74,7 +74,11 @@ def create_server_version_variants() -> list[BuildVariant]:
for version in ALL_VERSIONS:
display_name = get_variant_name("* MongoDB", version=version)
variant = create_variant(
[".server-version"], display_name, host=DEFAULT_HOST, tags=["coverage_tag"]
[".server-version"],
display_name,
version=version,
host=DEFAULT_HOST,
tags=["coverage_tag"],
)
variants.append(variant)
return variants

View File

@ -48,9 +48,12 @@ if ! command -v just &>/dev/null; then
_TARGET="--target x86_64-pc-windows-msvc"
fi
_BIN_DIR=$PYMONGO_BIN_DIR
mkdir -p ${_BIN_DIR}
echo "Installing just..."
mkdir -p "$_BIN_DIR" 2>/dev/null || true
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- $_TARGET --to "$_BIN_DIR" || {
# Remove just file if it exists (can be created if there was an install error).
rm -f ${_BIN_DIR}/just
_pip_install rust-just just
}
echo "Installing just... done."
@ -59,6 +62,7 @@ fi
# Ensure uv is installed.
if ! command -v uv &>/dev/null; then
_BIN_DIR=$PYMONGO_BIN_DIR
mkdir -p ${_BIN_DIR}
echo "Installing uv..."
# On most systems we can install directly.
curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR="$_BIN_DIR" INSTALLER_NO_MODIFY_PATH=1 sh || {

View File

@ -30,6 +30,9 @@ def _setup_azure_vm(base_env: dict[str, str]) -> None:
env["AZUREKMS_CMD"] = "tar xf mongo-python-driver.tgz"
run_command(f"{azure_dir}/run-command.sh", env=env)
env["AZUREKMS_CMD"] = "sudo apt-get install -y python3-dev build-essential"
run_command(f"{azure_dir}/run-command.sh", env=env)
env["AZUREKMS_CMD"] = "bash .evergreen/just.sh setup-tests kms azure-remote"
run_command(f"{azure_dir}/run-command.sh", env=env)
LOGGER.info("Setting up Azure VM... done.")
@ -47,6 +50,9 @@ def _setup_gcp_vm(base_env: dict[str, str]) -> None:
env["GCPKMS_CMD"] = "tar xf mongo-python-driver.tgz"
run_command(f"{gcp_dir}/run-command.sh", env=env)
env["GCPKMS_CMD"] = "sudo apt-get install -y python3-dev build-essential"
run_command(f"{gcp_dir}/run-command.sh", env=env)
env["GCPKMS_CMD"] = "bash ./.evergreen/just.sh setup-tests kms gcp-remote"
run_command(f"{gcp_dir}/run-command.sh", env=env)
LOGGER.info("Setting up GCP VM...")

View File

@ -2,7 +2,14 @@ from __future__ import annotations
import os
from utils import DRIVERS_TOOLS, TMP_DRIVER_FILE, create_archive, read_env, run_command, write_env
from utils import (
DRIVERS_TOOLS,
TMP_DRIVER_FILE,
create_archive,
read_env,
run_command,
write_env,
)
K8S_NAMES = ["aks", "gke", "eks"]
K8S_REMOTE_NAMES = [f"{n}-remote" for n in K8S_NAMES]

View File

@ -0,0 +1,99 @@
diff --git a/test/connection_logging/connection-logging.json b/test/connection_logging/connection-logging.json
index d40cfbb7e..5799e834d 100644
--- a/test/connection_logging/connection-logging.json
+++ b/test/connection_logging/connection-logging.json
@@ -272,7 +272,13 @@
"level": "debug",
"component": "connection",
"data": {
- "message": "Connection pool closed",
+ "message": "Connection closed",
+ "driverConnectionId": {
+ "$$type": [
+ "int",
+ "long"
+ ]
+ },
"serverHost": {
"$$type": "string"
},
@@ -281,20 +287,15 @@
"int",
"long"
]
- }
+ },
+ "reason": "Connection pool was closed"
}
},
{
"level": "debug",
"component": "connection",
"data": {
- "message": "Connection closed",
- "driverConnectionId": {
- "$$type": [
- "int",
- "long"
- ]
- },
+ "message": "Connection pool closed",
"serverHost": {
"$$type": "string"
},
@@ -303,8 +304,7 @@
"int",
"long"
]
- },
- "reason": "Connection pool was closed"
+ }
}
}
]
@@ -446,22 +446,6 @@
}
}
},
- {
- "level": "debug",
- "component": "connection",
- "data": {
- "message": "Connection pool cleared",
- "serverHost": {
- "$$type": "string"
- },
- "serverPort": {
- "$$type": [
- "int",
- "long"
- ]
- }
- }
- },
{
"level": "debug",
"component": "connection",
@@ -514,6 +498,22 @@
]
}
}
+ },
+ {
+ "level": "debug",
+ "component": "connection",
+ "data": {
+ "message": "Connection pool cleared",
+ "serverHost": {
+ "$$type": "string"
+ },
+ "serverPort": {
+ "$$type": [
+ "int",
+ "long"
+ ]
+ }
+ }
}
]
}

View File

@ -16,7 +16,7 @@ env:
# Changes per repo
PRODUCT_NAME: PyMongo
# Changes per branch
EVERGREEN_PROJECT: mongo-python-driver
EVERGREEN_PROJECT: mongo-python-driver-release
# Constant
# inputs will be empty on a scheduled run. so, we only set dry_run
# to 'false' when the input is set to 'false'.

View File

@ -22,13 +22,13 @@ jobs:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install just
uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
- name: Install uv
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v5
with:
enable-cache: true
python-version: "3.9"
- name: Install just
run: uv tool install rust-just
- name: Install Python dependencies
run: |
just install
@ -50,33 +50,31 @@ jobs:
cppcheck pymongo
build:
# supercharge/mongodb-github-action requires containers so we don't test other platforms
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# Tests currently only pass on ubuntu on GitHub Actions.
os: [ubuntu-latest]
python-version: ["3.9", "pypy-3.10", "3.13", "3.13t"]
python-version: ["3.9", "pypy-3.10", "3.13t"]
mongodb-version: ["8.0"]
name: CPython ${{ matrix.python-version }}-${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install just
uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
- name: Install uv
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v5
with:
enable-cache: true
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: just install
- name: Start MongoDB
uses: supercharge/mongodb-github-action@90004df786821b6308fb02299e5835d0dae05d0d # 1.12.0
- id: setup-mongodb
uses: mongodb-labs/drivers-evergreen-tools@master
with:
mongodb-version: 6.0
version: "${{ matrix.mongodb-version }}"
- name: Run tests
run: just test
run: uv run --extra test pytest -v
doctest:
runs-on: ubuntu-latest
@ -85,17 +83,17 @@ jobs:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install just
uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
- name: Install uv
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v5
with:
enable-cache: true
python-version: "3.9"
- name: Start MongoDB
uses: supercharge/mongodb-github-action@90004df786821b6308fb02299e5835d0dae05d0d # 1.12.0
- name: Install just
run: uv tool install rust-just
- id: setup-mongodb
uses: mongodb-labs/drivers-evergreen-tools@master
with:
mongodb-version: '8.0.0-rc4'
version: "8.0"
- name: Install dependencies
run: just install
- name: Run tests
@ -116,7 +114,7 @@ jobs:
enable-cache: true
python-version: "3.9"
- name: Install just
uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
run: uv tool install rust-just
- name: Install dependencies
run: just install
- name: Build docs
@ -135,7 +133,7 @@ jobs:
enable-cache: true
python-version: "3.9"
- name: Install just
uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
run: uv tool install rust-just
- name: Install dependencies
run: just install
- name: Build docs
@ -157,7 +155,7 @@ jobs:
enable-cache: true
python-version: "${{matrix.python}}"
- name: Install just
uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
run: uv tool install rust-just
- name: Install dependencies
run: |
just install
@ -210,8 +208,8 @@ jobs:
cache-dependency-path: 'sdist/test/pyproject.toml'
# Test sdist on lowest supported Python
python-version: '3.9'
- name: Start MongoDB
uses: supercharge/mongodb-github-action@90004df786821b6308fb02299e5835d0dae05d0d # 1.12.0
- id: setup-mongodb
uses: mongodb-labs/drivers-evergreen-tools@master
- name: Run connect test from sdist
shell: bash
run: |
@ -234,10 +232,10 @@ jobs:
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v5
with:
python-version: '3.9'
- name: Start MongoDB
uses: supercharge/mongodb-github-action@90004df786821b6308fb02299e5835d0dae05d0d # 1.12.0
- id: setup-mongodb
uses: mongodb-labs/drivers-evergreen-tools@master
with:
mongodb-version: 6.0
version: "8.0"
# Async and our test_dns do not support dnspython 1.X, so we don't run async or dns tests here
- name: Run tests
shell: bash
@ -260,10 +258,10 @@ jobs:
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v5
with:
python-version: '3.9'
- name: Start MongoDB
uses: supercharge/mongodb-github-action@90004df786821b6308fb02299e5835d0dae05d0d # 1.12.0
- id: setup-mongodb
uses: mongodb-labs/drivers-evergreen-tools@master
with:
mongodb-version: 6.0
version: "8.0"
# The lifetime kwarg we use in srv resolution was added to the async resolver API in dnspython 2.1.0
- name: Run tests
shell: bash

7
.github/zizmor.yml vendored Normal file
View File

@ -0,0 +1,7 @@
rules:
unpinned-uses:
config:
policies:
actions/*: ref-pin
mongodb-labs/drivers-github-tools/*: ref-pin
mongodb-labs/drivers-evergreen-tools: ref-pin

View File

@ -1,25 +1,61 @@
Changelog
=========
Changes in Version 4.14.0 (XXXX/XX/XX)
Changes in Version 4.14.1 (2025/08/19)
--------------------------------------
Version 4.14.1 is a bug fix release.
- Fixed a bug in ``MongoClient.append_metadata()`` and ``AsyncMongoClient.append_metadata()``
that allowed duplicate ``DriverInfo.name`` to be appended to the metadata.
Issues Resolved
...............
See the `PyMongo 4.14.1 release notes in JIRA`_ for the list of resolved issues
in this release.
.. _PyMongo 4.14.1 release notes in JIRA: https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=10004&version=45256
Changes in Version 4.14.0 (2025/08/06)
--------------------------------------
.. warning:: PyMongo 4.14 drops support for MongoDB 4.0. PyMongo now supports
MongoDB 4.2+.
PyMongo 4.14 brings a number of changes including:
- Added preliminary support for Python 3.14 and 3.14 with free-threading. We do not yet support the following with Python 3.14:
- Subinterpreters (``concurrent.interpreters``)
- Free-threading with Encryption
- mod_wsgi
- Removed experimental support for free-threading support in Python 3.13.
- Added :attr:`bson.codec_options.TypeRegistry.codecs` and :attr:`bson.codec_options.TypeRegistry.fallback_encoder` properties
to allow users to directly access the type codecs and fallback encoder for a given :class:`bson.codec_options.TypeRegistry`.
- Added :meth:`pymongo.asynchronous.mongo_client.AsyncMongoClient.append_metadata` and
:meth:`pymongo.mongo_client.MongoClient.append_metadata` to allow instantiated MongoClients to send client metadata
on-demand
- Improved performance of selecting a server with the Primary selector.
- Dropped support for MongoDB 4.0.
- Added preliminary support for Python 3.14 and 3.14 with free-threading. We do
not yet support the following with Python 3.14:
- Introduces a minor breaking change. When encoding :class:`bson.binary.BinaryVector`, a ``ValueError`` will be raised
if the 'padding' metadata field is < 0 or > 7, or non-zero for any type other than PACKED_BIT.
- Changed :meth:`~pymongo.uri_parser.parse_uri`'s ``options`` parameter to be type ``dict`` instead of ``_CaseInsensitiveDictionary``.
- Subinterpreters (``concurrent.interpreters``)
- Free-threading with Encryption
- mod_wsgi
- Removed experimental support for free-threading support in Python 3.13.
- Added :attr:`bson.codec_options.TypeRegistry.codecs` and
:attr:`bson.codec_options.TypeRegistry.fallback_encoder` properties
to allow users to directly access the type codecs and fallback encoder for a
given :class:`bson.codec_options.TypeRegistry`.
- Added
:meth:`pymongo.asynchronous.mongo_client.AsyncMongoClient.append_metadata` and
:meth:`pymongo.mongo_client.MongoClient.append_metadata` to allow instantiated
MongoClients to send client metadata on-demand
- Improved performance of selecting a server with the Primary selector.
- Introduces a minor breaking change. When encoding
:class:`bson.binary.BinaryVector`, a ``ValueError`` will be raised if the
'padding' metadata field is < 0 or > 7, or non-zero for any type other than
PACKED_BIT.
- Changed :meth:`~pymongo.uri_parser.parse_uri`'s ``options`` return value to be
type ``dict`` instead of ``_CaseInsensitiveDictionary``.
Issues Resolved
...............
See the `PyMongo 4.14 release notes in JIRA`_ for the list of resolved issues
in this release.
.. _PyMongo 4.14 release notes in JIRA: https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=10004&version=43041
Changes in Version 4.13.2 (2025/06/17)
--------------------------------------

View File

@ -18,7 +18,7 @@ from __future__ import annotations
import re
from typing import List, Tuple, Union
__version__ = "4.14.0.dev0"
__version__ = "4.14.2.dev0"
def get_version_tuple(version: str) -> Tuple[Union[int, str], ...]:

View File

@ -75,12 +75,12 @@ from pymongo.errors import (
NetworkTimeout,
ServerSelectionTimeoutError,
)
from pymongo.helpers_shared import _get_timeout_details
from pymongo.network_layer import async_socket_sendall
from pymongo.operations import UpdateOne
from pymongo.pool_options import PoolOptions
from pymongo.pool_shared import (
_async_configured_socket,
_get_timeout_details,
_raise_connection_failure,
)
from pymongo.read_concern import ReadConcern

View File

@ -58,6 +58,7 @@ from pymongo.errors import ( # type:ignore[attr-defined]
WaitQueueTimeoutError,
)
from pymongo.hello import Hello, HelloCompat
from pymongo.helpers_shared import _get_timeout_details, format_timeout_details
from pymongo.lock import (
_async_cond_wait,
_async_create_condition,
@ -79,9 +80,7 @@ from pymongo.pool_shared import (
SSLErrors,
_CancellationContext,
_configured_protocol_interface,
_get_timeout_details,
_raise_connection_failure,
format_timeout_details,
)
from pymongo.read_preferences import ReadPreference
from pymongo.server_api import _add_to_command

View File

@ -224,7 +224,7 @@ class Server:
if use_cmd:
first = docs[0]
await operation.client._process_response(first, operation.session) # type: ignore[misc, arg-type]
_check_command_response(first, conn.max_wire_version)
_check_command_response(first, conn.max_wire_version, pool_opts=conn.opts) # type:ignore[has-type]
except Exception as exc:
duration = datetime.now() - start
if isinstance(exc, (NotPrimaryError, OperationFailure)):

View File

@ -47,6 +47,7 @@ from pymongo.hello import HelloCompat
if TYPE_CHECKING:
from pymongo.cursor_shared import _Hint
from pymongo.operations import _IndexList
from pymongo.pool_options import PoolOptions
from pymongo.typings import _DocumentOut
@ -108,6 +109,34 @@ _SENSITIVE_COMMANDS: set = {
}
def _get_timeout_details(options: PoolOptions) -> dict[str, float]:
from pymongo import _csot
details = {}
timeout = _csot.get_timeout()
socket_timeout = options.socket_timeout
connect_timeout = options.connect_timeout
if timeout:
details["timeoutMS"] = timeout * 1000
if socket_timeout and not timeout:
details["socketTimeoutMS"] = socket_timeout * 1000
if connect_timeout:
details["connectTimeoutMS"] = connect_timeout * 1000
return details
def format_timeout_details(details: Optional[dict[str, float]]) -> str:
result = ""
if details:
result += " (configured timeouts:"
for timeout in ["socketTimeoutMS", "timeoutMS", "connectTimeoutMS"]:
if timeout in details:
result += f" {timeout}: {details[timeout]}ms,"
result = result[:-1]
result += ")"
return result
def _gen_index_name(keys: _IndexList) -> str:
"""Generate an index name from the set of fields it is over."""
return "_".join(["{}_{}".format(*item) for item in keys])
@ -188,6 +217,7 @@ def _check_command_response(
max_wire_version: Optional[int],
allowable_errors: Optional[Container[Union[int, str]]] = None,
parse_write_concern_error: bool = False,
pool_opts: Optional[PoolOptions] = None,
) -> None:
"""Check the response to a command for errors."""
if "ok" not in response:
@ -243,6 +273,10 @@ def _check_command_response(
if code in (11000, 11001, 12582):
raise DuplicateKeyError(errmsg, code, response, max_wire_version)
elif code == 50:
# Append timeout details to MaxTimeMSExpired responses.
if pool_opts:
timeout_details = _get_timeout_details(pool_opts)
errmsg += format_timeout_details(timeout_details)
raise ExecutionTimeout(errmsg, code, response, max_wire_version)
elif code == 43:
raise CursorNotFound(errmsg, code, response, max_wire_version)

View File

@ -386,8 +386,13 @@ class PoolOptions:
def _update_metadata(self, driver: DriverInfo) -> None:
"""Updates the client's metadata"""
if driver.name and driver.name.lower() in self.__metadata["driver"]["name"].lower().split(
"|"
):
return
metadata = copy.deepcopy(self.__metadata)
if driver.name:
metadata["driver"]["name"] = "{}|{}".format(
metadata["driver"]["name"],

View File

@ -36,6 +36,7 @@ from pymongo.errors import ( # type:ignore[attr-defined]
NetworkTimeout,
_CertificateError,
)
from pymongo.helpers_shared import _get_timeout_details, format_timeout_details
from pymongo.network_layer import AsyncNetworkingInterface, NetworkingInterface, PyMongoProtocol
from pymongo.pool_options import PoolOptions
from pymongo.ssl_support import PYSSLError, SSLError, _has_sni
@ -149,32 +150,6 @@ def _raise_connection_failure(
raise AutoReconnect(msg) from error
def _get_timeout_details(options: PoolOptions) -> dict[str, float]:
details = {}
timeout = _csot.get_timeout()
socket_timeout = options.socket_timeout
connect_timeout = options.connect_timeout
if timeout:
details["timeoutMS"] = timeout * 1000
if socket_timeout and not timeout:
details["socketTimeoutMS"] = socket_timeout * 1000
if connect_timeout:
details["connectTimeoutMS"] = connect_timeout * 1000
return details
def format_timeout_details(details: Optional[dict[str, float]]) -> str:
result = ""
if details:
result += " (configured timeouts:"
for timeout in ["socketTimeoutMS", "timeoutMS", "connectTimeoutMS"]:
if timeout in details:
result += f" {timeout}: {details[timeout]}ms,"
result = result[:-1]
result += ")"
return result
class _CancellationContext:
def __init__(self) -> None:
self._cancelled = False

View File

@ -70,12 +70,12 @@ from pymongo.errors import (
NetworkTimeout,
ServerSelectionTimeoutError,
)
from pymongo.helpers_shared import _get_timeout_details
from pymongo.network_layer import sendall
from pymongo.operations import UpdateOne
from pymongo.pool_options import PoolOptions
from pymongo.pool_shared import (
_configured_socket,
_get_timeout_details,
_raise_connection_failure,
)
from pymongo.read_concern import ReadConcern

View File

@ -55,6 +55,7 @@ from pymongo.errors import ( # type:ignore[attr-defined]
WaitQueueTimeoutError,
)
from pymongo.hello import Hello, HelloCompat
from pymongo.helpers_shared import _get_timeout_details, format_timeout_details
from pymongo.lock import (
_cond_wait,
_create_condition,
@ -76,9 +77,7 @@ from pymongo.pool_shared import (
SSLErrors,
_CancellationContext,
_configured_socket_interface,
_get_timeout_details,
_raise_connection_failure,
format_timeout_details,
)
from pymongo.read_preferences import ReadPreference
from pymongo.server_api import _add_to_command

View File

@ -224,7 +224,7 @@ class Server:
if use_cmd:
first = docs[0]
operation.client._process_response(first, operation.session) # type: ignore[misc, arg-type]
_check_command_response(first, conn.max_wire_version)
_check_command_response(first, conn.max_wire_version, pool_opts=conn.opts) # type:ignore[has-type]
except Exception as exc:
duration = datetime.now() - start
if isinstance(exc, (NotPrimaryError, OperationFailure)):

View File

@ -107,15 +107,20 @@ class TestClientMetadataProse(AsyncIntegrationTest):
new_name, new_version, new_platform, new_metadata = await self.send_ping_and_get_metadata(
client, True
)
self.assertEqual(new_name, f"{name}|{add_name}" if add_name is not None else name)
self.assertEqual(
new_version,
f"{version}|{add_version}" if add_version is not None else version,
)
self.assertEqual(
new_platform,
f"{platform}|{add_platform}" if add_platform is not None else platform,
)
if add_name is not None and add_name.lower() in name.lower().split("|"):
self.assertEqual(name, new_name)
self.assertEqual(version, new_version)
self.assertEqual(platform, new_platform)
else:
self.assertEqual(new_name, f"{name}|{add_name}" if add_name is not None else name)
self.assertEqual(
new_version,
f"{version}|{add_version}" if add_version is not None else version,
)
self.assertEqual(
new_platform,
f"{platform}|{add_platform}" if add_platform is not None else platform,
)
metadata.pop("driver")
metadata.pop("platform")
@ -210,6 +215,18 @@ class TestClientMetadataProse(AsyncIntegrationTest):
self.assertIsNone(self.handshake_req)
self.assertEqual(listener.event_count(ConnectionClosedEvent), 0)
async def test_duplicate_driver_name_no_op(self):
client = await self.async_rs_or_single_client(
"mongodb://" + self.server.address_string,
maxIdleTimeMS=1,
)
client.append_metadata(DriverInfo("library", "1.2", "Library Platform"))
await self.check_metadata_added(client, "framework", None, None)
# wait for connection to become idle
await asyncio.sleep(0.005)
# add same metadata again
await self.check_metadata_added(client, "Framework", None, None)
if __name__ == "__main__":
unittest.main()

View File

@ -335,6 +335,8 @@ class AsyncTestCollection(AsyncIntegrationTest):
await db.test.create_index(["hello", ("world", DESCENDING)])
await db.test.create_index({"hello": 1}.items()) # type:ignore[arg-type]
# TODO: PYTHON-5491 - remove version max
@async_client_context.require_version_max(8, 0, -1)
async def test_drop_index(self):
db = self.db
await db.test.drop_indexes()

View File

@ -43,6 +43,7 @@ from test.utils_shared import (
from bson import decode_all
from bson.code import Code
from bson.raw_bson import RawBSONDocument
from pymongo import ASCENDING, DESCENDING
from pymongo.asynchronous.cursor import AsyncCursor, CursorType
from pymongo.asynchronous.helpers import anext
@ -199,6 +200,21 @@ class TestCursor(AsyncIntegrationTest):
finally:
await client.admin.command("configureFailPoint", "maxTimeAlwaysTimeOut", mode="off")
async def test_maxtime_ms_message(self):
db = self.db
await db.t.insert_one({"x": 1})
with self.assertRaises(Exception) as error:
await db.t.find_one({"$where": delay(2)}, max_time_ms=1)
self.assertIn("(configured timeouts: connectTimeoutMS: 20000.0ms", str(error.exception))
client = await self.async_rs_client(document_class=RawBSONDocument)
await client.db.t.insert_one({"x": 1})
with self.assertRaises(Exception) as error:
await client.db.t.find_one({"$where": delay(2)}, max_time_ms=1)
self.assertIn("(configured timeouts: connectTimeoutMS: 20000.0ms", str(error.exception))
async def test_max_await_time_ms(self):
db = self.db
await db.pymongo_test.drop()

View File

@ -21,7 +21,7 @@ import random
import socket
import sys
import time
from test.asynchronous.utils import async_get_pool, async_joinall
from test.asynchronous.utils import async_get_pool, async_joinall, flaky
from bson.codec_options import DEFAULT_CODEC_OPTIONS
from bson.son import SON

View File

@ -564,6 +564,8 @@ class UnifiedSpecTestMixinV1(AsyncIntegrationTest):
self.skipTest("CSOT not implemented for watch()")
if "cursors" in class_name:
self.skipTest("CSOT not implemented for cursors")
if "dropindex on collection" in description:
self.skipTest("PYTHON-5491")
if (
"tailable" in class_name
or "tailable" in description

View File

@ -446,22 +446,6 @@
}
}
},
{
"level": "debug",
"component": "connection",
"data": {
"message": "Connection pool cleared",
"serverHost": {
"$$type": "string"
},
"serverPort": {
"$$type": [
"int",
"long"
]
}
}
},
{
"level": "debug",
"component": "connection",
@ -514,6 +498,22 @@
]
}
}
},
{
"level": "debug",
"component": "connection",
"data": {
"message": "Connection pool cleared",
"serverHost": {
"$$type": "string"
},
"serverPort": {
"$$type": [
"int",
"long"
]
}
}
}
]
}

View File

@ -107,15 +107,20 @@ class TestClientMetadataProse(IntegrationTest):
new_name, new_version, new_platform, new_metadata = self.send_ping_and_get_metadata(
client, True
)
self.assertEqual(new_name, f"{name}|{add_name}" if add_name is not None else name)
self.assertEqual(
new_version,
f"{version}|{add_version}" if add_version is not None else version,
)
self.assertEqual(
new_platform,
f"{platform}|{add_platform}" if add_platform is not None else platform,
)
if add_name is not None and add_name.lower() in name.lower().split("|"):
self.assertEqual(name, new_name)
self.assertEqual(version, new_version)
self.assertEqual(platform, new_platform)
else:
self.assertEqual(new_name, f"{name}|{add_name}" if add_name is not None else name)
self.assertEqual(
new_version,
f"{version}|{add_version}" if add_version is not None else version,
)
self.assertEqual(
new_platform,
f"{platform}|{add_platform}" if add_platform is not None else platform,
)
metadata.pop("driver")
metadata.pop("platform")
@ -210,6 +215,18 @@ class TestClientMetadataProse(IntegrationTest):
self.assertIsNone(self.handshake_req)
self.assertEqual(listener.event_count(ConnectionClosedEvent), 0)
def test_duplicate_driver_name_no_op(self):
client = self.rs_or_single_client(
"mongodb://" + self.server.address_string,
maxIdleTimeMS=1,
)
client.append_metadata(DriverInfo("library", "1.2", "Library Platform"))
self.check_metadata_added(client, "framework", None, None)
# wait for connection to become idle
time.sleep(0.005)
# add same metadata again
self.check_metadata_added(client, "Framework", None, None)
if __name__ == "__main__":
unittest.main()

View File

@ -333,6 +333,8 @@ class TestCollection(IntegrationTest):
db.test.create_index(["hello", ("world", DESCENDING)])
db.test.create_index({"hello": 1}.items()) # type:ignore[arg-type]
# TODO: PYTHON-5491 - remove version max
@client_context.require_version_max(8, 0, -1)
def test_drop_index(self):
db = self.db
db.test.drop_indexes()

View File

@ -43,6 +43,7 @@ from test.utils_shared import (
from bson import decode_all
from bson.code import Code
from bson.raw_bson import RawBSONDocument
from pymongo import ASCENDING, DESCENDING
from pymongo.collation import Collation
from pymongo.errors import ExecutionTimeout, InvalidOperation, OperationFailure, PyMongoError
@ -197,6 +198,21 @@ class TestCursor(IntegrationTest):
finally:
client.admin.command("configureFailPoint", "maxTimeAlwaysTimeOut", mode="off")
def test_maxtime_ms_message(self):
db = self.db
db.t.insert_one({"x": 1})
with self.assertRaises(Exception) as error:
db.t.find_one({"$where": delay(2)}, max_time_ms=1)
self.assertIn("(configured timeouts: connectTimeoutMS: 20000.0ms", str(error.exception))
client = self.rs_client(document_class=RawBSONDocument)
client.db.t.insert_one({"x": 1})
with self.assertRaises(Exception) as error:
client.db.t.find_one({"$where": delay(2)}, max_time_ms=1)
self.assertIn("(configured timeouts: connectTimeoutMS: 20000.0ms", str(error.exception))
def test_max_await_time_ms(self):
db = self.db
db.pymongo_test.drop()

View File

@ -21,7 +21,7 @@ import random
import socket
import sys
import time
from test.utils import get_pool, joinall
from test.utils import flaky, get_pool, joinall
from bson.codec_options import DEFAULT_CODEC_OPTIONS
from bson.son import SON

View File

@ -563,6 +563,8 @@ class UnifiedSpecTestMixinV1(IntegrationTest):
self.skipTest("CSOT not implemented for watch()")
if "cursors" in class_name:
self.skipTest("CSOT not implemented for cursors")
if "dropindex on collection" in description:
self.skipTest("PYTHON-5491")
if (
"tailable" in class_name
or "tailable" in description