Skip to content

Commit bf34fa0

Browse files
committed
Merge branch 'master' of github.com:mongodb/mongo-python-driver
2 parents 684a5ab + 24e9da6 commit bf34fa0

File tree

9 files changed

+96
-3
lines changed

9 files changed

+96
-3
lines changed

.github/workflows/codeql.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646

4747
# Initializes the CodeQL tools for scanning.
4848
- name: Initialize CodeQL
49-
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3
49+
uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3
5050
with:
5151
languages: ${{ matrix.language }}
5252
build-mode: ${{ matrix.build-mode }}
@@ -63,6 +63,6 @@ jobs:
6363
pip install -e .
6464
6565
- name: Perform CodeQL Analysis
66-
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3
66+
uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e # v3
6767
with:
6868
category: "/language:${{matrix.language}}"

.github/workflows/create-release-branch.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ jobs:
4343
aws_region_name: ${{ vars.AWS_REGION_NAME }}
4444
aws_secret_id: ${{ secrets.AWS_SECRET_ID }}
4545
artifactory_username: ${{ vars.ARTIFACTORY_USERNAME }}
46+
- name: Get hatch
47+
run: pip install hatch
4648
- uses: mongodb-labs/drivers-github-tools/create-branch@v2
4749
id: create-branch
4850
with:

.github/workflows/zizmor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
env:
2727
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2828
- name: Upload SARIF file
29-
uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3
29+
uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3
3030
with:
3131
sarif_file: results.sarif
3232
category: zizmor

pymongo/_csot.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@
3232
DEADLINE: ContextVar[float] = ContextVar("DEADLINE", default=float("inf"))
3333

3434

35+
def reset_all() -> None:
36+
TIMEOUT.set(None)
37+
RTT.set(0.0)
38+
DEADLINE.set(float("inf"))
39+
40+
3541
def get_timeout() -> Optional[float]:
3642
return TIMEOUT.get(None)
3743

pymongo/periodic_executor.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import weakref
2424
from typing import Any, Optional
2525

26+
from pymongo import _csot
2627
from pymongo._asyncio_task import create_task
2728
from pymongo.lock import _create_lock
2829

@@ -93,6 +94,8 @@ def skip_sleep(self) -> None:
9394
self._skip_sleep = True
9495

9596
async def _run(self) -> None:
97+
# The CSOT contextvars must be cleared inside the executor task before execution begins
98+
_csot.reset_all()
9699
while not self._stopped:
97100
if self._task and self._task.cancelling(): # type: ignore[unused-ignore, attr-defined]
98101
raise asyncio.CancelledError
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Copyright 2025-present MongoDB, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Test that AsyncPeriodicExecutors do not copy ContextVars from their parents."""
16+
from __future__ import annotations
17+
18+
import asyncio
19+
import sys
20+
from test.asynchronous.utils import async_get_pool
21+
from test.utils_shared import delay, one
22+
23+
sys.path[0:0] = [""]
24+
25+
from test.asynchronous import AsyncIntegrationTest
26+
27+
28+
class TestAsyncContextVarsReset(AsyncIntegrationTest):
29+
async def test_context_vars_are_reset_in_executor(self):
30+
if sys.version_info < (3, 11):
31+
self.skipTest("Test requires asyncio.Task.get_context (added in Python 3.11)")
32+
33+
client = self.simple_client()
34+
35+
await client.db.test.insert_one({"x": 1})
36+
for server in client._topology._servers.values():
37+
for context in [
38+
c
39+
for c in server._monitor._executor._task.get_context()
40+
if c.name in ["TIMEOUT", "RTT", "DEADLINE"]
41+
]:
42+
self.assertIn(context.get(), [None, float("inf"), 0.0])
43+
await client.db.test.delete_many({})

test/asynchronous/test_auth_oidc.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,25 @@ async def test_4_4_speculative_authentication_should_be_ignored_on_reauthenticat
10851085
# Assert there were `SaslStart` commands executed.
10861086
assert any(event.command_name.lower() == "saslstart" for event in listener.started_events)
10871087

1088+
async def test_4_5_reauthentication_succeeds_when_a_session_is_involved(self):
1089+
# Create an OIDC configured client.
1090+
client = await self.create_client()
1091+
1092+
# Set a fail point for `find` commands of the form:
1093+
async with self.fail_point(
1094+
{
1095+
"mode": {"times": 1},
1096+
"data": {"failCommands": ["find"], "errorCode": 391},
1097+
}
1098+
):
1099+
# Start a new session.
1100+
async with client.start_session() as session:
1101+
# In the started session perform a `find` operation that succeeds.
1102+
await client.test.test.find_one({}, session=session)
1103+
1104+
# Assert that the callback was called 2 times (once during the connection handshake, and again during reauthentication).
1105+
self.assertEqual(self.request_called, 2)
1106+
10881107
async def test_5_1_azure_with_no_username(self):
10891108
if ENVIRON != "azure":
10901109
raise unittest.SkipTest("Test is only supported on Azure")

test/test_auth_oidc.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,25 @@ def test_4_4_speculative_authentication_should_be_ignored_on_reauthentication(se
10831083
# Assert there were `SaslStart` commands executed.
10841084
assert any(event.command_name.lower() == "saslstart" for event in listener.started_events)
10851085

1086+
def test_4_5_reauthentication_succeeds_when_a_session_is_involved(self):
1087+
# Create an OIDC configured client.
1088+
client = self.create_client()
1089+
1090+
# Set a fail point for `find` commands of the form:
1091+
with self.fail_point(
1092+
{
1093+
"mode": {"times": 1},
1094+
"data": {"failCommands": ["find"], "errorCode": 391},
1095+
}
1096+
):
1097+
# Start a new session.
1098+
with client.start_session() as session:
1099+
# In the started session perform a `find` operation that succeeds.
1100+
client.test.test.find_one({}, session=session)
1101+
1102+
# Assert that the callback was called 2 times (once during the connection handshake, and again during reauthentication).
1103+
self.assertEqual(self.request_called, 2)
1104+
10861105
def test_5_1_azure_with_no_username(self):
10871106
if ENVIRON != "azure":
10881107
raise unittest.SkipTest("Test is only supported on Azure")

tools/synchro.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ def async_only_test(f: str) -> bool:
185185
"test_concurrency.py",
186186
"test_async_cancellation.py",
187187
"test_async_loop_safety.py",
188+
"test_async_contextvars_reset.py",
188189
]
189190

190191

0 commit comments

Comments
 (0)