Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27fac499c1 | ||
|
|
0d2a4b462e | ||
|
|
550d234f71 | ||
|
|
d98049c630 | ||
|
|
f66ec0ff23 | ||
|
|
6611bec9e7 | ||
|
|
7692bd67b9 | ||
|
|
aa0b920566 | ||
|
|
1c480163f4 | ||
|
|
7055ad1944 | ||
|
|
e80f4f4199 | ||
|
|
c4e866d5d7 | ||
|
|
ce2812d974 | ||
|
|
952fa82c24 | ||
|
|
6354d6cae7 |
@ -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
|
||||
|
||||
@ -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}"
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 || {
|
||||
|
||||
@ -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...")
|
||||
|
||||
@ -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]
|
||||
|
||||
99
.evergreen/spec-patch/PYTHON-5493.patch
Normal file
99
.evergreen/spec-patch/PYTHON-5493.patch
Normal 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"
|
||||
+ ]
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
]
|
||||
}
|
||||
2
.github/workflows/release-python.yml
vendored
2
.github/workflows/release-python.yml
vendored
@ -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'.
|
||||
|
||||
54
.github/workflows/test-python.yml
vendored
54
.github/workflows/test-python.yml
vendored
@ -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
7
.github/zizmor.yml
vendored
Normal 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
|
||||
@ -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)
|
||||
--------------------------------------
|
||||
|
||||
@ -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], ...]:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)):
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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"],
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)):
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user