From a490cfda68eb6fac2efa7d52ae9b33c1bda8435e Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 6 Aug 2025 14:24:37 -0500 Subject: [PATCH 01/30] PYTHON-5215 Add an asyncio.Protocol implementation for KMS --- pymongo/asynchronous/encryption.py | 31 +- pymongo/asynchronous/pool.py | 165 +++++----- pymongo/network_layer.py | 489 ++++++++++++----------------- pymongo/synchronous/encryption.py | 31 +- pymongo/synchronous/pool.py | 165 +++++----- tools/synchro.py | 1 + 6 files changed, 411 insertions(+), 471 deletions(-) diff --git a/pymongo/asynchronous/encryption.py b/pymongo/asynchronous/encryption.py index 9b0757b1a5..9e884734c0 100644 --- a/pymongo/asynchronous/encryption.py +++ b/pymongo/asynchronous/encryption.py @@ -64,6 +64,7 @@ from pymongo.asynchronous.cursor import AsyncCursor from pymongo.asynchronous.database import AsyncDatabase from pymongo.asynchronous.mongo_client import AsyncMongoClient +from pymongo.asynchronous.pool import AsyncBaseConnection from pymongo.common import CONNECT_TIMEOUT from pymongo.daemon import _spawn_daemon from pymongo.encryption_options import AutoEncryptionOpts, RangeOpts @@ -75,11 +76,12 @@ NetworkTimeout, ServerSelectionTimeoutError, ) -from pymongo.network_layer import async_socket_sendall +from pymongo.network_layer import async_receive_kms, async_sendall from pymongo.operations import UpdateOne from pymongo.pool_options import PoolOptions from pymongo.pool_shared import ( _async_configured_socket, + _configured_protocol_interface, _get_timeout_details, _raise_connection_failure, ) @@ -196,23 +198,16 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None: sleep_sec = float(sleep_u) / 1e6 await asyncio.sleep(sleep_sec) try: - conn = await _connect_kms(address, opts) + interface = await _configured_protocol_interface(address, opts) + conn = AsyncBaseConnection(interface, opts) + # Given a conn object, we want to send a message and then receive the bytes needed try: - await async_socket_sendall(conn, message) - while kms_context.bytes_needed > 0: - # CSOT: update timeout. - conn.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) - if _IS_SYNC: - data = conn.recv(kms_context.bytes_needed) - else: - from pymongo.network_layer import ( # type: ignore[attr-defined] - async_receive_data_socket, - ) - - data = await async_receive_data_socket(conn, kms_context.bytes_needed) - if not data: - raise OSError("KMS connection closed") - kms_context.feed(data) + await async_sendall(interface.get_conn, message) + interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) + data = await async_receive_kms(conn, kms_context.bytes_needed) + if not data: + raise OSError("KMS connection closed") + kms_context.feed(bytes) except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: @@ -228,7 +223,7 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None: address, exc, msg_prefix=msg_prefix, timeout_details=_get_timeout_details(opts) ) finally: - conn.close() + interface.get_conn.close() except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: diff --git a/pymongo/asynchronous/pool.py b/pymongo/asynchronous/pool.py index 9a39883fc2..1c07ca0816 100644 --- a/pymongo/asynchronous/pool.py +++ b/pymongo/asynchronous/pool.py @@ -124,7 +124,89 @@ def _set_non_inheritable_non_atomic(fd: int) -> None: # noqa: ARG001 _IS_SYNC = False -class AsyncConnection: +class AsyncBaseConnection: + """A base connection object for server and kms connections.""" + + def __init__(self, conn: AsyncNetworkingInterface, opts: PoolOptions): + self.conn = conn + self.socket_checker: SocketChecker = SocketChecker() + self.cancel_context: _CancellationContext = _CancellationContext() + self.is_sdam = False + self.closed = False + self.last_timeout: float | None = None + self.more_to_come = False + self.opts = opts + self.max_wire_version = -1 + + def set_conn_timeout(self, timeout: Optional[float]) -> None: + """Cache last timeout to avoid duplicate calls to conn.settimeout.""" + if timeout == self.last_timeout: + return + self.last_timeout = timeout + self.conn.get_conn.settimeout(timeout) + + def apply_timeout( + self, client: AsyncMongoClient, cmd: Optional[MutableMapping[str, Any]] + ) -> Optional[float]: + # CSOT: use remaining timeout when set. + timeout = _csot.remaining() + if timeout is None: + # Reset the socket timeout unless we're performing a streaming monitor check. + if not self.more_to_come: + self.set_conn_timeout(self.opts.socket_timeout) + return None + # RTT validation. + rtt = _csot.get_rtt() + if rtt is None: + rtt = self.connect_rtt + max_time_ms = timeout - rtt + if max_time_ms < 0: + timeout_details = _get_timeout_details(self.opts) + formatted = format_timeout_details(timeout_details) + # CSOT: raise an error without running the command since we know it will time out. + errmsg = f"operation would exceed time limit, remaining timeout:{timeout:.5f} <= network round trip time:{rtt:.5f} {formatted}" + if self.max_wire_version != -1: + raise ExecutionTimeout( + errmsg, + 50, + {"ok": 0, "errmsg": errmsg, "code": 50}, + self.max_wire_version, + ) + else: + raise TimeoutError(errmsg) + if cmd is not None: + cmd["maxTimeMS"] = int(max_time_ms * 1000) + self.set_conn_timeout(timeout) + return timeout + + async def close_conn(self, reason: Optional[str]) -> None: + """Close this connection with a reason.""" + if self.closed: + return + await self._close_conn() + + async def _close_conn(self) -> None: + """Close this connection.""" + if self.closed: + return + self.closed = True + self.cancel_context.cancel() + # Note: We catch exceptions to avoid spurious errors on interpreter + # shutdown. + try: + await self.conn.close() + except Exception: # noqa: S110 + pass + + def conn_closed(self) -> bool: + """Return True if we know socket has been closed, False otherwise.""" + if _IS_SYNC: + return self.socket_checker.socket_closed(self.conn.get_conn) + else: + return self.conn.is_closing() + + +class AsyncConnection(AsyncBaseConnection): """Store a connection with some metadata. :param conn: a raw connection object @@ -142,29 +224,27 @@ def __init__( id: int, is_sdam: bool, ): + super().__init__(conn, pool.opts) self.pool_ref = weakref.ref(pool) - self.conn = conn - self.address = address - self.id = id + self.address: tuple[str, int] = address + self.id: int = id self.is_sdam = is_sdam - self.closed = False self.last_checkin_time = time.monotonic() self.performed_handshake = False self.is_writable: bool = False self.max_wire_version = MAX_WIRE_VERSION - self.max_bson_size = MAX_BSON_SIZE - self.max_message_size = MAX_MESSAGE_SIZE - self.max_write_batch_size = MAX_WRITE_BATCH_SIZE + self.max_bson_size: int = MAX_BSON_SIZE + self.max_message_size: int = MAX_MESSAGE_SIZE + self.max_write_batch_size: int = MAX_WRITE_BATCH_SIZE self.supports_sessions = False self.hello_ok: bool = False - self.is_mongos = False + self.is_mongos: bool = False self.op_msg_enabled = False self.listeners = pool.opts._event_listeners self.enabled_for_cmap = pool.enabled_for_cmap self.enabled_for_logging = pool.enabled_for_logging self.compression_settings = pool.opts._compression_settings self.compression_context: Union[SnappyContext, ZlibContext, ZstdContext, None] = None - self.socket_checker: SocketChecker = SocketChecker() self.oidc_token_gen_id: Optional[int] = None # Support for mechanism negotiation on the initial handshake. self.negotiated_mechs: Optional[list[str]] = None @@ -175,9 +255,6 @@ def __init__( self.pool_gen = pool.gen self.generation = self.pool_gen.get_overall() self.ready = False - self.cancel_context: _CancellationContext = _CancellationContext() - self.opts = pool.opts - self.more_to_come: bool = False # For load balancer support. self.service_id: Optional[ObjectId] = None self.server_connection_id: Optional[int] = None @@ -193,44 +270,6 @@ def __init__( # For gossiping $clusterTime from the connection handshake to the client. self._cluster_time = None - def set_conn_timeout(self, timeout: Optional[float]) -> None: - """Cache last timeout to avoid duplicate calls to conn.settimeout.""" - if timeout == self.last_timeout: - return - self.last_timeout = timeout - self.conn.get_conn.settimeout(timeout) - - def apply_timeout( - self, client: AsyncMongoClient, cmd: Optional[MutableMapping[str, Any]] - ) -> Optional[float]: - # CSOT: use remaining timeout when set. - timeout = _csot.remaining() - if timeout is None: - # Reset the socket timeout unless we're performing a streaming monitor check. - if not self.more_to_come: - self.set_conn_timeout(self.opts.socket_timeout) - return None - # RTT validation. - rtt = _csot.get_rtt() - if rtt is None: - rtt = self.connect_rtt - max_time_ms = timeout - rtt - if max_time_ms < 0: - timeout_details = _get_timeout_details(self.opts) - formatted = format_timeout_details(timeout_details) - # CSOT: raise an error without running the command since we know it will time out. - errmsg = f"operation would exceed time limit, remaining timeout:{timeout:.5f} <= network round trip time:{rtt:.5f} {formatted}" - raise ExecutionTimeout( - errmsg, - 50, - {"ok": 0, "errmsg": errmsg, "code": 50}, - self.max_wire_version, - ) - if cmd is not None: - cmd["maxTimeMS"] = int(max_time_ms * 1000) - self.set_conn_timeout(timeout) - return timeout - def pin_txn(self) -> None: self.pinned_txn = True assert not self.pinned_cursor @@ -555,9 +594,7 @@ def validate_session( async def close_conn(self, reason: Optional[str]) -> None: """Close this connection with a reason.""" - if self.closed: - return - await self._close_conn() + await super().close_conn(reason) if reason: if self.enabled_for_cmap: assert self.listeners is not None @@ -574,26 +611,6 @@ async def close_conn(self, reason: Optional[str]) -> None: error=reason, ) - async def _close_conn(self) -> None: - """Close this connection.""" - if self.closed: - return - self.closed = True - self.cancel_context.cancel() - # Note: We catch exceptions to avoid spurious errors on interpreter - # shutdown. - try: - await self.conn.close() - except Exception: # noqa: S110 - pass - - def conn_closed(self) -> bool: - """Return True if we know socket has been closed, False otherwise.""" - if _IS_SYNC: - return self.socket_checker.socket_closed(self.conn.get_conn) - else: - return self.conn.is_closing() - def send_cluster_time( self, command: MutableMapping[str, Any], diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 78eefc7177..8ef80d3afc 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -22,10 +22,12 @@ import struct import sys import time -from asyncio import AbstractEventLoop, BaseTransport, BufferedProtocol, Future, Transport +from asyncio import BaseTransport, BufferedProtocol, Future, Transport +from dataclasses import dataclass from typing import ( TYPE_CHECKING, Any, + Callable, Optional, Union, ) @@ -38,208 +40,31 @@ from pymongo.message import _UNPACK_REPLY, _OpMsg, _OpReply from pymongo.socket_checker import _errno_from_exception -try: - from ssl import SSLError, SSLSocket - - _HAVE_SSL = True -except ImportError: - _HAVE_SSL = False - -try: - from pymongo.pyopenssl_context import _sslConn - - _HAVE_PYOPENSSL = True -except ImportError: - _HAVE_PYOPENSSL = False - _sslConn = SSLSocket # type: ignore[assignment, misc] - -from pymongo.ssl_support import ( - BLOCKING_IO_LOOKUP_ERROR, - BLOCKING_IO_READ_ERROR, - BLOCKING_IO_WRITE_ERROR, -) - if TYPE_CHECKING: - from pymongo.asynchronous.pool import AsyncConnection - from pymongo.synchronous.pool import Connection + from ssl import SSLSocket + + from pymongo.asynchronous.pool import AsyncBaseConnection + from pymongo.synchronous.pool import BaseConnection _UNPACK_HEADER = struct.Struct(" None: - timeout = sock.gettimeout() - sock.settimeout(0.0) - loop = asyncio.get_running_loop() - try: - if _HAVE_SSL and isinstance(sock, (SSLSocket, _sslConn)): - await asyncio.wait_for(_async_socket_sendall_ssl(sock, buf, loop), timeout=timeout) - else: - await asyncio.wait_for(loop.sock_sendall(sock, buf), timeout=timeout) # type: ignore[arg-type] - except asyncio.TimeoutError as exc: - # Convert the asyncio.wait_for timeout error to socket.timeout which pool.py understands. - raise socket.timeout("timed out") from exc - finally: - sock.settimeout(timeout) - - -if sys.platform != "win32": - - async def _async_socket_sendall_ssl( - sock: Union[socket.socket, _sslConn], buf: bytes, loop: AbstractEventLoop - ) -> None: - view = memoryview(buf) - sent = 0 - - def _is_ready(fut: Future) -> None: - if fut.done(): - return - fut.set_result(None) - - while sent < len(buf): - try: - sent += sock.send(view[sent:]) - except BLOCKING_IO_ERRORS as exc: - fd = sock.fileno() - # Check for closed socket. - if fd == -1: - raise SSLError("Underlying socket has been closed") from None - if isinstance(exc, BLOCKING_IO_READ_ERROR): - fut = loop.create_future() - loop.add_reader(fd, _is_ready, fut) - try: - await fut - finally: - loop.remove_reader(fd) - if isinstance(exc, BLOCKING_IO_WRITE_ERROR): - fut = loop.create_future() - loop.add_writer(fd, _is_ready, fut) - try: - await fut - finally: - loop.remove_writer(fd) - if _HAVE_PYOPENSSL and isinstance(exc, BLOCKING_IO_LOOKUP_ERROR): - fut = loop.create_future() - loop.add_reader(fd, _is_ready, fut) - try: - loop.add_writer(fd, _is_ready, fut) - await fut - finally: - loop.remove_reader(fd) - loop.remove_writer(fd) - - async def _async_socket_receive_ssl( - conn: _sslConn, length: int, loop: AbstractEventLoop, once: Optional[bool] = False - ) -> memoryview: - mv = memoryview(bytearray(length)) - total_read = 0 - - def _is_ready(fut: Future) -> None: - if fut.done(): - return - fut.set_result(None) +_PYPY = "PyPy" in sys.version +_WINDOWS = sys.platform == "win32" - while total_read < length: - try: - read = conn.recv_into(mv[total_read:]) - if read == 0: - raise OSError("connection closed") - # KMS responses update their expected size after the first batch, stop reading after one loop - if once: - return mv[:read] - total_read += read - except BLOCKING_IO_ERRORS as exc: - fd = conn.fileno() - # Check for closed socket. - if fd == -1: - raise SSLError("Underlying socket has been closed") from None - if isinstance(exc, BLOCKING_IO_READ_ERROR): - fut = loop.create_future() - loop.add_reader(fd, _is_ready, fut) - try: - await fut - finally: - loop.remove_reader(fd) - if isinstance(exc, BLOCKING_IO_WRITE_ERROR): - fut = loop.create_future() - loop.add_writer(fd, _is_ready, fut) - try: - await fut - finally: - loop.remove_writer(fd) - if _HAVE_PYOPENSSL and isinstance(exc, BLOCKING_IO_LOOKUP_ERROR): - fut = loop.create_future() - loop.add_reader(fd, _is_ready, fut) - try: - loop.add_writer(fd, _is_ready, fut) - await fut - finally: - loop.remove_reader(fd) - loop.remove_writer(fd) - return mv - -else: - # The default Windows asyncio event loop does not support loop.add_reader/add_writer: - # https://docs.python.org/3/library/asyncio-platforms.html#asyncio-platform-support - # Note: In PYTHON-4493 we plan to replace this code with asyncio streams. - async def _async_socket_sendall_ssl( - sock: Union[socket.socket, _sslConn], buf: bytes, dummy: AbstractEventLoop - ) -> None: - view = memoryview(buf) - total_length = len(buf) - total_sent = 0 - # Backoff starts at 1ms, doubles on timeout up to 512ms, and halves on success - # down to 1ms. - backoff = 0.001 - while total_sent < total_length: - try: - sent = sock.send(view[total_sent:]) - except BLOCKING_IO_ERRORS: - await asyncio.sleep(backoff) - sent = 0 - if sent > 0: - backoff = max(backoff / 2, 0.001) - else: - backoff = min(backoff * 2, 0.512) - total_sent += sent - - async def _async_socket_receive_ssl( - conn: _sslConn, length: int, dummy: AbstractEventLoop, once: Optional[bool] = False - ) -> memoryview: - mv = memoryview(bytearray(length)) - total_read = 0 - # Backoff starts at 1ms, doubles on timeout up to 512ms, and halves on success - # down to 1ms. - backoff = 0.001 - while total_read < length: - try: - read = conn.recv_into(mv[total_read:]) - if read == 0: - raise OSError("connection closed") - # KMS responses update their expected size after the first batch, stop reading after one loop - if once: - return mv[:read] - except BLOCKING_IO_ERRORS: - await asyncio.sleep(backoff) - read = 0 - if read > 0: - backoff = max(backoff / 2, 0.001) - else: - backoff = min(backoff * 2, 0.512) - total_read += read - return mv +# Errors raised by sockets (and TLS sockets) when in non-blocking mode. +BLOCKING_IO_ERRORS = ( + BlockingIOError, + *ssl_support.BLOCKING_IO_LOOKUP_ERROR, + *ssl_support.BLOCKING_IO_ERRORS, +) -def sendall(sock: Union[socket.socket, _sslConn], buf: bytes) -> None: +def sendall(sock: Union[socket.socket, SSLSocket], buf: bytes) -> None: sock.sendall(buf) -async def _poll_cancellation(conn: AsyncConnection) -> None: +async def _poll_cancellation(conn: AsyncBaseConnection) -> None: while True: if conn.cancel_context.cancelled: return @@ -247,49 +72,7 @@ async def _poll_cancellation(conn: AsyncConnection) -> None: await asyncio.sleep(_POLL_TIMEOUT) -async def async_receive_data_socket( - sock: Union[socket.socket, _sslConn], length: int -) -> memoryview: - sock_timeout = sock.gettimeout() - timeout = sock_timeout - - sock.settimeout(0.0) - loop = asyncio.get_running_loop() - try: - if _HAVE_SSL and isinstance(sock, (SSLSocket, _sslConn)): - return await asyncio.wait_for( - _async_socket_receive_ssl(sock, length, loop, once=True), # type: ignore[arg-type] - timeout=timeout, - ) - else: - return await asyncio.wait_for( - _async_socket_receive(sock, length, loop), # type: ignore[arg-type] - timeout=timeout, - ) - except asyncio.TimeoutError as err: - raise socket.timeout("timed out") from err - finally: - sock.settimeout(sock_timeout) - - -async def _async_socket_receive( - conn: socket.socket, length: int, loop: AbstractEventLoop -) -> memoryview: - mv = memoryview(bytearray(length)) - bytes_read = 0 - while bytes_read < length: - chunk_length = await loop.sock_recv_into(conn, mv[bytes_read:]) - if chunk_length == 0: - raise OSError("connection closed") - bytes_read += chunk_length - return mv - - -_PYPY = "PyPy" in sys.version -_WINDOWS = sys.platform == "win32" - - -def wait_for_read(conn: Connection, deadline: Optional[float]) -> None: +def wait_for_read(conn: BaseConnection, deadline: Optional[float]) -> None: """Block until at least one byte is read, or a timeout, or a cancel.""" sock = conn.conn.sock timed_out = False @@ -322,7 +105,7 @@ def wait_for_read(conn: Connection, deadline: Optional[float]) -> None: raise socket.timeout("timed out") -def receive_data(conn: Connection, length: int, deadline: Optional[float]) -> memoryview: +def receive_data(conn: BaseConnection, length: int, deadline: Optional[float]) -> memoryview: buf = bytearray(length) mv = memoryview(buf) bytes_read = 0 @@ -412,7 +195,7 @@ def sock(self) -> Any: class AsyncNetworkingInterface(NetworkingInterfaceBase): - def __init__(self, conn: tuple[Transport, PyMongoProtocol]): + def __init__(self, conn: tuple[Transport, PyMongoBaseProtocol]): super().__init__(conn) @property @@ -430,7 +213,7 @@ def is_closing(self) -> bool: return self.conn[0].is_closing() @property - def get_conn(self) -> PyMongoProtocol: + def get_conn(self) -> PyMongoBaseProtocol: return self.conn[1] @property @@ -439,7 +222,7 @@ def sock(self) -> socket.socket: class NetworkingInterface(NetworkingInterfaceBase): - def __init__(self, conn: Union[socket.socket, _sslConn]): + def __init__(self, conn: Union[socket.socket, SSLSocket]): super().__init__(conn) def gettimeout(self) -> float | None: @@ -455,11 +238,11 @@ def is_closing(self) -> bool: return self.conn.is_closing() @property - def get_conn(self) -> Union[socket.socket, _sslConn]: + def get_conn(self) -> Union[socket.socket, SSLSocket]: return self.conn @property - def sock(self) -> Union[socket.socket, _sslConn]: + def sock(self) -> Union[socket.socket, SSLSocket]: return self.conn def fileno(self) -> int: @@ -469,9 +252,50 @@ def recv_into(self, buffer: bytes) -> int: return self.conn.recv_into(buffer) -class PyMongoProtocol(BufferedProtocol): +class PyMongoBaseProtocol(BufferedProtocol): def __init__(self, timeout: Optional[float] = None): self.transport: Transport = None # type: ignore[assignment] + self._timeout = timeout + self._closed = asyncio.get_running_loop().create_future() + + def settimeout(self, timeout: float | None) -> None: + self._timeout = timeout + + @property + def gettimeout(self) -> float | None: + """The configured timeout for the socket that underlies our protocol pair.""" + return self._timeout + + def close(self, exc: Optional[Exception] = None) -> None: + self.transport.abort() + self._resolve_pending(exc) + self._connection_lost = True + + def connection_lost(self, exc: Optional[Exception] = None) -> None: + self._resolve_pending(exc) + if not self._closed.done(): + self._closed.set_result(None) + + def _resolve_pending(self, exc: Optional[Exception] = None) -> None: + pass + + async def wait_closed(self) -> None: + await self._closed + + async def write(self, message: bytes) -> None: + """Write a message to this connection's transport.""" + if self.transport.is_closing(): + raise OSError("Connection is closed") + self.transport.write(message) + self.transport.resume_reading() + + async def read(self, *args: Any) -> Any: + raise NotImplementedError + + +class PyMongoProtocol(PyMongoBaseProtocol): + def __init__(self, timeout: Optional[float] = None): + super().__init__(timeout) # Each message is reader in 2-3 parts: header, compression header, and message body # The message buffer is allocated after the header is read. self._header = memoryview(bytearray(16)) @@ -487,23 +311,13 @@ def __init__(self, timeout: Optional[float] = None): self._op_code = 0 self._connection_lost = False self._read_waiter: Optional[Future] = None - self._timeout = timeout self._is_compressed = False self._compressor_id: Optional[int] = None self._max_message_size = MAX_MESSAGE_SIZE self._response_to: Optional[int] = None - self._closed = asyncio.get_running_loop().create_future() self._pending_messages: collections.deque[Future] = collections.deque() self._done_messages: collections.deque[Future] = collections.deque() - def settimeout(self, timeout: float | None) -> None: - self._timeout = timeout - - @property - def gettimeout(self) -> float | None: - """The configured timeout for the socket that underlies our protocol pair.""" - return self._timeout - def connection_made(self, transport: BaseTransport) -> None: """Called exactly once when a connection is made. The transport argument is the transport representing the write side of the connection. @@ -511,13 +325,6 @@ def connection_made(self, transport: BaseTransport) -> None: self.transport = transport # type: ignore[assignment] self.transport.set_write_buffer_limits(MAX_MESSAGE_SIZE, MAX_MESSAGE_SIZE) - async def write(self, message: bytes) -> None: - """Write a message to this connection's transport.""" - if self.transport.is_closing(): - raise OSError("Connection is closed") - self.transport.write(message) - self.transport.resume_reading() - async def read(self, request_id: Optional[int], max_message_size: int) -> tuple[bytes, int]: """Read a single MongoDB Wire Protocol message from this connection.""" if self.transport: @@ -660,7 +467,7 @@ def process_compression_header(self) -> tuple[int, int]: op_code, _, compressor_id = _UNPACK_COMPRESSION_HEADER(self._compression_header) return op_code, compressor_id - def _resolve_pending_messages(self, exc: Optional[Exception] = None) -> None: + def _resolve_pending(self, exc: Optional[Exception] = None) -> None: pending = list(self._pending_messages) for msg in pending: if not msg.done(): @@ -670,21 +477,97 @@ def _resolve_pending_messages(self, exc: Optional[Exception] = None) -> None: msg.set_exception(exc) self._done_messages.append(msg) - def close(self, exc: Optional[Exception] = None) -> None: - self.transport.abort() - self._resolve_pending_messages(exc) - self._connection_lost = True - def connection_lost(self, exc: Optional[Exception] = None) -> None: - self._resolve_pending_messages(exc) - if not self._closed.done(): - self._closed.set_result(None) +@dataclass +class KMSBuffer: + buffer: memoryview + start_index: int - async def wait_closed(self) -> None: - await self._closed +class PyMongoKMSProtocol(PyMongoBaseProtocol): + def __init__(self, timeout: Optional[float] = None): + super().__init__(timeout) + self._buffers: collections.deque[KMSBuffer] = collections.deque() + # pool for buffers that have been exhausted and can be reused. + self._buffer_pool: collections.deque[KMSBuffer] = collections.deque(maxlen=3) + self._bytes_ready = 0 + self._bytes_requested = 0 + self._pending_reads: collections.deque[int] = collections.deque() + self._pending_listeners: collections.deque[Future] = collections.deque() -async def async_sendall(conn: PyMongoProtocol, buf: bytes) -> None: + def connection_made(self, transport: BaseTransport) -> None: + """Called exactly once when a connection is made. + The transport argument is the transport representing the write side of the connection. + """ + self.transport = transport # type: ignore[assignment] + + async def read(self, bytes_needed: int) -> bytes: + """Read the requested bytes from this connection.""" + if self.transport and self.transport.is_closing(): + raise OSError("connection is already closed") + self._pending_reads.append(bytes_needed) + read_waiter = asyncio.get_running_loop().create_future() + self._pending_listeners.append(read_waiter) + return await read_waiter + + def get_buffer(self, sizehint: int) -> memoryview: + """Called to allocate a new receive buffer. + The asyncio loop calls this method expecting to receive a non-empty buffer to fill with data. + If any data does not fit into the returned buffer, this method will be called again until + either no data remains or an empty buffer is returned. + """ + if self._buffer_pool: + buffer = self._buffer_pool.popleft() + else: + buffer = KMSBuffer(memoryview(bytearray(sizehint)), 0) + self._buffers.append(buffer) + return buffer.buffer + + def buffer_updated(self, nbytes: int) -> None: + """Called when the buffer was updated with the received data""" + # Wrote 0 bytes into a non-empty buffer, signal connection closed + if nbytes == 0: + self.close(OSError("connection closed")) + return + if self._connection_lost: + return + self._bytes_ready += nbytes + + # Bail we don't have the current requested number of bytes. + n_requested = self._bytes_requested + if n_requested == 0 and self._pending_reads: + n_requested = self._pending_reads.popleft() + if n_requested == 0 or self._bytes_ready < n_requested: + return + + # Send the bytes to the listener. + self._bytes_ready -= n_requested + self._bytes_requested = 0 + waiter = self._pending_listeners.popleft() + output_buf = bytearray(n_requested) + n_remaining = n_requested + out_index = 0 + while n_remaining > 0: + buffer = self._buffers.popleft() + buffer_remaining = len(buffer.buffer) - buffer.start_index + # if we didn't exhaust the buffer, read the partial data and put it back. + if buffer_remaining <= n_remaining: + output_buf[out_index : n_remaining + out_index] = buffer.buffer[ + buffer.start_index : buffer.start_index + n_remaining + ] + buffer.start_index += n_remaining + self._buffers.appendleft(buffer) + # otherwise exhaust the buffer and return it to the pool. + else: + output_buf[out_index : out_index + buffer_remaining] = buffer.buffer[ + buffer.start_index : + ] + buffer.start_index = 0 + self._buffer_pool.append(buffer) + waiter.set_result(output_buf) + + +async def async_sendall(conn: PyMongoBaseProtocol, buf: bytes) -> None: try: await asyncio.wait_for(conn.write(buf), timeout=conn.gettimeout) except asyncio.TimeoutError as exc: @@ -692,12 +575,18 @@ async def async_sendall(conn: PyMongoProtocol, buf: bytes) -> None: raise socket.timeout("timed out") from exc -async def async_receive_message( - conn: AsyncConnection, - request_id: Optional[int], - max_message_size: int = MAX_MESSAGE_SIZE, -) -> Union[_OpReply, _OpMsg]: - """Receive a raw BSON message or raise socket.error.""" +async def async_receive_kms(conn: AsyncBaseConnection, bytes_needed: int) -> bytes: + """Receive raw bytes from the kms connection.""" + + def callback(result: Any) -> bytes: + return result + + return await _async_receive_data(conn, callback, bytes_needed) + + +async def _async_receive_data( + conn: AsyncBaseConnection, callback: Callable[..., Any], *args: Any +) -> Any: timeout: Optional[Union[float, int]] timeout = conn.conn.gettimeout if _csot.get_timeout(): @@ -713,8 +602,8 @@ async def async_receive_message( # timeouts on AWS Lambda and other FaaS environments. timeout = max(deadline - time.monotonic(), 0) + read_task = create_task(conn.conn.get_conn.read(*args)) cancellation_task = create_task(_poll_cancellation(conn)) - read_task = create_task(conn.conn.get_conn.read(request_id, max_message_size)) tasks = [read_task, cancellation_task] try: done, pending = await asyncio.wait( @@ -727,14 +616,7 @@ async def async_receive_message( if len(done) == 0: raise socket.timeout("timed out") if read_task in done: - data, op_code = read_task.result() - try: - unpack_reply = _UNPACK_REPLY[op_code] - except KeyError: - raise ProtocolError( - f"Got opcode {op_code!r} but expected {_UNPACK_REPLY.keys()!r}" - ) from None - return unpack_reply(data) + return callback(read_task.result()) raise _OperationCancelled("operation cancelled") except asyncio.CancelledError: for task in tasks: @@ -743,8 +625,41 @@ async def async_receive_message( raise +async def async_receive_message( + conn: AsyncBaseConnection, + request_id: Optional[int], + max_message_size: int = MAX_MESSAGE_SIZE, +) -> Union[_OpReply, _OpMsg]: + """Receive a raw BSON message or raise socket.error.""" + + def callback(result: Any) -> _OpMsg | _OpReply: + data, op_code = result + try: + unpack_reply = _UNPACK_REPLY[op_code] + except KeyError: + raise ProtocolError( + f"Got opcode {op_code!r} but expected {_UNPACK_REPLY.keys()!r}" + ) from None + return unpack_reply(data) + + return await _async_receive_data(conn, callback, request_id, max_message_size) + + +def receive_kms(conn: BaseConnection, bytes_needed: int) -> bytes: + """Receive raw bytes from the kms connection.""" + if _csot.get_timeout(): + deadline = _csot.get_deadline() + else: + timeout = conn.conn.gettimeout() + if timeout: + deadline = time.monotonic() + timeout + else: + deadline = None + return receive_data(conn, bytes_needed, deadline) + + def receive_message( - conn: Connection, request_id: Optional[int], max_message_size: int = MAX_MESSAGE_SIZE + conn: BaseConnection, request_id: Optional[int], max_message_size: int = MAX_MESSAGE_SIZE ) -> Union[_OpReply, _OpMsg]: """Receive a raw BSON message or raise socket.error.""" if _csot.get_timeout(): diff --git a/pymongo/synchronous/encryption.py b/pymongo/synchronous/encryption.py index 5f9bdac4b7..8c5bfc9763 100644 --- a/pymongo/synchronous/encryption.py +++ b/pymongo/synchronous/encryption.py @@ -70,11 +70,12 @@ NetworkTimeout, ServerSelectionTimeoutError, ) -from pymongo.network_layer import sendall +from pymongo.network_layer import receive_kms, sendall from pymongo.operations import UpdateOne from pymongo.pool_options import PoolOptions from pymongo.pool_shared import ( _configured_socket, + _configured_socket_interface, _get_timeout_details, _raise_connection_failure, ) @@ -85,6 +86,7 @@ from pymongo.synchronous.cursor import Cursor from pymongo.synchronous.database import Database from pymongo.synchronous.mongo_client import MongoClient +from pymongo.synchronous.pool import BaseConnection from pymongo.typings import _DocumentType, _DocumentTypeArg from pymongo.uri_parser_shared import _parse_kms_tls_options, parse_host from pymongo.write_concern import WriteConcern @@ -195,23 +197,16 @@ def kms_request(self, kms_context: MongoCryptKmsContext) -> None: sleep_sec = float(sleep_u) / 1e6 time.sleep(sleep_sec) try: - conn = _connect_kms(address, opts) + interface = _configured_socket_interface(address, opts) + conn = BaseConnection(interface, opts) + # Given a conn object, we want to send a message and then receive the bytes needed try: - sendall(conn, message) - while kms_context.bytes_needed > 0: - # CSOT: update timeout. - conn.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) - if _IS_SYNC: - data = conn.recv(kms_context.bytes_needed) - else: - from pymongo.network_layer import ( # type: ignore[attr-defined] - receive_data_socket, - ) - - data = receive_data_socket(conn, kms_context.bytes_needed) - if not data: - raise OSError("KMS connection closed") - kms_context.feed(data) + sendall(interface.get_conn, message) + interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) + data = receive_kms(conn, kms_context.bytes_needed) + if not data: + raise OSError("KMS connection closed") + kms_context.feed(bytes) except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: @@ -227,7 +222,7 @@ def kms_request(self, kms_context: MongoCryptKmsContext) -> None: address, exc, msg_prefix=msg_prefix, timeout_details=_get_timeout_details(opts) ) finally: - conn.close() + interface.get_conn.close() except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: diff --git a/pymongo/synchronous/pool.py b/pymongo/synchronous/pool.py index 505f58c60f..e1ebe3b859 100644 --- a/pymongo/synchronous/pool.py +++ b/pymongo/synchronous/pool.py @@ -124,7 +124,89 @@ def _set_non_inheritable_non_atomic(fd: int) -> None: # noqa: ARG001 _IS_SYNC = True -class Connection: +class BaseConnection: + """A base connection object for server and kms connections.""" + + def __init__(self, conn: NetworkingInterface, opts: PoolOptions): + self.conn = conn + self.socket_checker: SocketChecker = SocketChecker() + self.cancel_context: _CancellationContext = _CancellationContext() + self.is_sdam = False + self.closed = False + self.last_timeout: float | None = None + self.more_to_come = False + self.opts = opts + self.max_wire_version = -1 + + def set_conn_timeout(self, timeout: Optional[float]) -> None: + """Cache last timeout to avoid duplicate calls to conn.settimeout.""" + if timeout == self.last_timeout: + return + self.last_timeout = timeout + self.conn.get_conn.settimeout(timeout) + + def apply_timeout( + self, client: MongoClient, cmd: Optional[MutableMapping[str, Any]] + ) -> Optional[float]: + # CSOT: use remaining timeout when set. + timeout = _csot.remaining() + if timeout is None: + # Reset the socket timeout unless we're performing a streaming monitor check. + if not self.more_to_come: + self.set_conn_timeout(self.opts.socket_timeout) + return None + # RTT validation. + rtt = _csot.get_rtt() + if rtt is None: + rtt = self.connect_rtt + max_time_ms = timeout - rtt + if max_time_ms < 0: + timeout_details = _get_timeout_details(self.opts) + formatted = format_timeout_details(timeout_details) + # CSOT: raise an error without running the command since we know it will time out. + errmsg = f"operation would exceed time limit, remaining timeout:{timeout:.5f} <= network round trip time:{rtt:.5f} {formatted}" + if self.max_wire_version != -1: + raise ExecutionTimeout( + errmsg, + 50, + {"ok": 0, "errmsg": errmsg, "code": 50}, + self.max_wire_version, + ) + else: + raise TimeoutError(errmsg) + if cmd is not None: + cmd["maxTimeMS"] = int(max_time_ms * 1000) + self.set_conn_timeout(timeout) + return timeout + + def close_conn(self, reason: Optional[str]) -> None: + """Close this connection with a reason.""" + if self.closed: + return + self._close_conn() + + def _close_conn(self) -> None: + """Close this connection.""" + if self.closed: + return + self.closed = True + self.cancel_context.cancel() + # Note: We catch exceptions to avoid spurious errors on interpreter + # shutdown. + try: + self.conn.close() + except Exception: # noqa: S110 + pass + + def conn_closed(self) -> bool: + """Return True if we know socket has been closed, False otherwise.""" + if _IS_SYNC: + return self.socket_checker.socket_closed(self.conn.get_conn) + else: + return self.conn.is_closing() + + +class Connection(BaseConnection): """Store a connection with some metadata. :param conn: a raw connection object @@ -142,29 +224,27 @@ def __init__( id: int, is_sdam: bool, ): + super().__init__(conn, pool.opts) self.pool_ref = weakref.ref(pool) - self.conn = conn - self.address = address - self.id = id + self.address: tuple[str, int] = address + self.id: int = id self.is_sdam = is_sdam - self.closed = False self.last_checkin_time = time.monotonic() self.performed_handshake = False self.is_writable: bool = False self.max_wire_version = MAX_WIRE_VERSION - self.max_bson_size = MAX_BSON_SIZE - self.max_message_size = MAX_MESSAGE_SIZE - self.max_write_batch_size = MAX_WRITE_BATCH_SIZE + self.max_bson_size: int = MAX_BSON_SIZE + self.max_message_size: int = MAX_MESSAGE_SIZE + self.max_write_batch_size: int = MAX_WRITE_BATCH_SIZE self.supports_sessions = False self.hello_ok: bool = False - self.is_mongos = False + self.is_mongos: bool = False self.op_msg_enabled = False self.listeners = pool.opts._event_listeners self.enabled_for_cmap = pool.enabled_for_cmap self.enabled_for_logging = pool.enabled_for_logging self.compression_settings = pool.opts._compression_settings self.compression_context: Union[SnappyContext, ZlibContext, ZstdContext, None] = None - self.socket_checker: SocketChecker = SocketChecker() self.oidc_token_gen_id: Optional[int] = None # Support for mechanism negotiation on the initial handshake. self.negotiated_mechs: Optional[list[str]] = None @@ -175,9 +255,6 @@ def __init__( self.pool_gen = pool.gen self.generation = self.pool_gen.get_overall() self.ready = False - self.cancel_context: _CancellationContext = _CancellationContext() - self.opts = pool.opts - self.more_to_come: bool = False # For load balancer support. self.service_id: Optional[ObjectId] = None self.server_connection_id: Optional[int] = None @@ -193,44 +270,6 @@ def __init__( # For gossiping $clusterTime from the connection handshake to the client. self._cluster_time = None - def set_conn_timeout(self, timeout: Optional[float]) -> None: - """Cache last timeout to avoid duplicate calls to conn.settimeout.""" - if timeout == self.last_timeout: - return - self.last_timeout = timeout - self.conn.get_conn.settimeout(timeout) - - def apply_timeout( - self, client: MongoClient, cmd: Optional[MutableMapping[str, Any]] - ) -> Optional[float]: - # CSOT: use remaining timeout when set. - timeout = _csot.remaining() - if timeout is None: - # Reset the socket timeout unless we're performing a streaming monitor check. - if not self.more_to_come: - self.set_conn_timeout(self.opts.socket_timeout) - return None - # RTT validation. - rtt = _csot.get_rtt() - if rtt is None: - rtt = self.connect_rtt - max_time_ms = timeout - rtt - if max_time_ms < 0: - timeout_details = _get_timeout_details(self.opts) - formatted = format_timeout_details(timeout_details) - # CSOT: raise an error without running the command since we know it will time out. - errmsg = f"operation would exceed time limit, remaining timeout:{timeout:.5f} <= network round trip time:{rtt:.5f} {formatted}" - raise ExecutionTimeout( - errmsg, - 50, - {"ok": 0, "errmsg": errmsg, "code": 50}, - self.max_wire_version, - ) - if cmd is not None: - cmd["maxTimeMS"] = int(max_time_ms * 1000) - self.set_conn_timeout(timeout) - return timeout - def pin_txn(self) -> None: self.pinned_txn = True assert not self.pinned_cursor @@ -553,9 +592,7 @@ def validate_session( def close_conn(self, reason: Optional[str]) -> None: """Close this connection with a reason.""" - if self.closed: - return - self._close_conn() + super().close_conn(reason) if reason: if self.enabled_for_cmap: assert self.listeners is not None @@ -572,26 +609,6 @@ def close_conn(self, reason: Optional[str]) -> None: error=reason, ) - def _close_conn(self) -> None: - """Close this connection.""" - if self.closed: - return - self.closed = True - self.cancel_context.cancel() - # Note: We catch exceptions to avoid spurious errors on interpreter - # shutdown. - try: - self.conn.close() - except Exception: # noqa: S110 - pass - - def conn_closed(self) -> bool: - """Return True if we know socket has been closed, False otherwise.""" - if _IS_SYNC: - return self.socket_checker.socket_closed(self.conn.get_conn) - else: - return self.conn.is_closing() - def send_cluster_time( self, command: MutableMapping[str, Any], diff --git a/tools/synchro.py b/tools/synchro.py index e502f96281..e2ce65d861 100644 --- a/tools/synchro.py +++ b/tools/synchro.py @@ -120,6 +120,7 @@ "_async_create_lock": "_create_lock", "_async_create_condition": "_create_condition", "_async_cond_wait": "_cond_wait", + "async_receive_kms": "receive_kms", "AsyncNetworkingInterface": "NetworkingInterface", "_configured_protocol_interface": "_configured_socket_interface", "_async_configured_socket": "_configured_socket", From 404c1fc2e9cdb587fda26e4aa35a10da9f2d50db Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 6 Aug 2025 14:31:48 -0500 Subject: [PATCH 02/30] cleanup --- pymongo/asynchronous/encryption.py | 12 ------------ pymongo/synchronous/encryption.py | 12 ------------ 2 files changed, 24 deletions(-) diff --git a/pymongo/asynchronous/encryption.py b/pymongo/asynchronous/encryption.py index 9e884734c0..ac6a6efe34 100644 --- a/pymongo/asynchronous/encryption.py +++ b/pymongo/asynchronous/encryption.py @@ -80,7 +80,6 @@ from pymongo.operations import UpdateOne from pymongo.pool_options import PoolOptions from pymongo.pool_shared import ( - _async_configured_socket, _configured_protocol_interface, _get_timeout_details, _raise_connection_failure, @@ -95,9 +94,6 @@ if TYPE_CHECKING: from pymongocrypt.mongocrypt import MongoCryptKmsContext - from pymongo.pyopenssl_context import _sslConn - from pymongo.typings import _Address - _IS_SYNC = False @@ -113,13 +109,6 @@ _KEY_VAULT_OPTS = CodecOptions(document_class=RawBSONDocument) -async def _connect_kms(address: _Address, opts: PoolOptions) -> Union[socket.socket, _sslConn]: - try: - return await _async_configured_socket(address, opts) - except Exception as exc: - _raise_connection_failure(address, exc, timeout_details=_get_timeout_details(opts)) - - @contextlib.contextmanager def _wrap_encryption_errors() -> Iterator[None]: """Context manager to wrap encryption related errors.""" @@ -200,7 +189,6 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None: try: interface = await _configured_protocol_interface(address, opts) conn = AsyncBaseConnection(interface, opts) - # Given a conn object, we want to send a message and then receive the bytes needed try: await async_sendall(interface.get_conn, message) interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) diff --git a/pymongo/synchronous/encryption.py b/pymongo/synchronous/encryption.py index 8c5bfc9763..882360538c 100644 --- a/pymongo/synchronous/encryption.py +++ b/pymongo/synchronous/encryption.py @@ -74,7 +74,6 @@ from pymongo.operations import UpdateOne from pymongo.pool_options import PoolOptions from pymongo.pool_shared import ( - _configured_socket, _configured_socket_interface, _get_timeout_details, _raise_connection_failure, @@ -94,9 +93,6 @@ if TYPE_CHECKING: from pymongocrypt.mongocrypt import MongoCryptKmsContext - from pymongo.pyopenssl_context import _sslConn - from pymongo.typings import _Address - _IS_SYNC = True @@ -112,13 +108,6 @@ _KEY_VAULT_OPTS = CodecOptions(document_class=RawBSONDocument) -def _connect_kms(address: _Address, opts: PoolOptions) -> Union[socket.socket, _sslConn]: - try: - return _configured_socket(address, opts) - except Exception as exc: - _raise_connection_failure(address, exc, timeout_details=_get_timeout_details(opts)) - - @contextlib.contextmanager def _wrap_encryption_errors() -> Iterator[None]: """Context manager to wrap encryption related errors.""" @@ -199,7 +188,6 @@ def kms_request(self, kms_context: MongoCryptKmsContext) -> None: try: interface = _configured_socket_interface(address, opts) conn = BaseConnection(interface, opts) - # Given a conn object, we want to send a message and then receive the bytes needed try: sendall(interface.get_conn, message) interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) From e4a588bbc6217211258ca69b468a979396a89f4e Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 6 Aug 2025 14:32:47 -0500 Subject: [PATCH 03/30] restore comment --- pymongo/asynchronous/encryption.py | 1 + pymongo/synchronous/encryption.py | 1 + 2 files changed, 2 insertions(+) diff --git a/pymongo/asynchronous/encryption.py b/pymongo/asynchronous/encryption.py index ac6a6efe34..addae1c48f 100644 --- a/pymongo/asynchronous/encryption.py +++ b/pymongo/asynchronous/encryption.py @@ -191,6 +191,7 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None: conn = AsyncBaseConnection(interface, opts) try: await async_sendall(interface.get_conn, message) + # CSOT: update timeout. interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) data = await async_receive_kms(conn, kms_context.bytes_needed) if not data: diff --git a/pymongo/synchronous/encryption.py b/pymongo/synchronous/encryption.py index 882360538c..84fba676f2 100644 --- a/pymongo/synchronous/encryption.py +++ b/pymongo/synchronous/encryption.py @@ -190,6 +190,7 @@ def kms_request(self, kms_context: MongoCryptKmsContext) -> None: conn = BaseConnection(interface, opts) try: sendall(interface.get_conn, message) + # CSOT: update timeout. interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) data = receive_kms(conn, kms_context.bytes_needed) if not data: From 2af62a34355742067717d88485e17f64a4be05be Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 6 Aug 2025 14:33:55 -0500 Subject: [PATCH 04/30] fix close --- pymongo/asynchronous/encryption.py | 2 +- pymongo/synchronous/encryption.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pymongo/asynchronous/encryption.py b/pymongo/asynchronous/encryption.py index addae1c48f..a02a5b90df 100644 --- a/pymongo/asynchronous/encryption.py +++ b/pymongo/asynchronous/encryption.py @@ -212,7 +212,7 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None: address, exc, msg_prefix=msg_prefix, timeout_details=_get_timeout_details(opts) ) finally: - interface.get_conn.close() + conn.close_conn() except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: diff --git a/pymongo/synchronous/encryption.py b/pymongo/synchronous/encryption.py index 84fba676f2..d1a1372761 100644 --- a/pymongo/synchronous/encryption.py +++ b/pymongo/synchronous/encryption.py @@ -211,7 +211,7 @@ def kms_request(self, kms_context: MongoCryptKmsContext) -> None: address, exc, msg_prefix=msg_prefix, timeout_details=_get_timeout_details(opts) ) finally: - interface.get_conn.close() + conn.close_conn() except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: From 0a434772c33e8f3ff59f97bfad08b11f646cce3d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 6 Aug 2025 17:57:31 -0500 Subject: [PATCH 05/30] wip --- pymongo/asynchronous/encryption.py | 19 ++++++++++--------- pymongo/network_layer.py | 6 ++++-- pymongo/pool_shared.py | 13 +++++++++---- pymongo/synchronous/encryption.py | 19 ++++++++++--------- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/pymongo/asynchronous/encryption.py b/pymongo/asynchronous/encryption.py index a02a5b90df..51c9b3b802 100644 --- a/pymongo/asynchronous/encryption.py +++ b/pymongo/asynchronous/encryption.py @@ -76,7 +76,7 @@ NetworkTimeout, ServerSelectionTimeoutError, ) -from pymongo.network_layer import async_receive_kms, async_sendall +from pymongo.network_layer import PyMongoKMSProtocol, async_receive_kms, async_sendall from pymongo.operations import UpdateOne from pymongo.pool_options import PoolOptions from pymongo.pool_shared import ( @@ -187,16 +187,17 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None: sleep_sec = float(sleep_u) / 1e6 await asyncio.sleep(sleep_sec) try: - interface = await _configured_protocol_interface(address, opts) + interface = await _configured_protocol_interface(address, opts, PyMongoKMSProtocol) conn = AsyncBaseConnection(interface, opts) try: await async_sendall(interface.get_conn, message) - # CSOT: update timeout. - interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) - data = await async_receive_kms(conn, kms_context.bytes_needed) - if not data: - raise OSError("KMS connection closed") - kms_context.feed(bytes) + while kms_context.bytes_needed > 0: + # CSOT: update timeout. + interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) + data = await async_receive_kms(conn, kms_context.bytes_needed) + if not data: + raise OSError("KMS connection closed") + kms_context.feed(data) except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: @@ -212,7 +213,7 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None: address, exc, msg_prefix=msg_prefix, timeout_details=_get_timeout_details(opts) ) finally: - conn.close_conn() + conn.close_conn(None) except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 54d355f176..d266b3fc43 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -257,6 +257,7 @@ def __init__(self, timeout: Optional[float] = None): self.transport: Transport = None # type: ignore[assignment] self._timeout = timeout self._closed = asyncio.get_running_loop().create_future() + self._connection_lost = False def settimeout(self, timeout: float | None) -> None: self._timeout = timeout @@ -309,7 +310,6 @@ def __init__(self, timeout: Optional[float] = None): self._expecting_compression = False self._message_size = 0 self._op_code = 0 - self._connection_lost = False self._read_waiter: Optional[Future[Any]] = None self._is_compressed = False self._compressor_id: Optional[int] = None @@ -551,7 +551,8 @@ def buffer_updated(self, nbytes: int) -> None: buffer = self._buffers.popleft() buffer_remaining = len(buffer.buffer) - buffer.start_index # if we didn't exhaust the buffer, read the partial data and put it back. - if buffer_remaining <= n_remaining: + if buffer_remaining > n_remaining: + n_remaining = 0 output_buf[out_index : n_remaining + out_index] = buffer.buffer[ buffer.start_index : buffer.start_index + n_remaining ] @@ -562,6 +563,7 @@ def buffer_updated(self, nbytes: int) -> None: output_buf[out_index : out_index + buffer_remaining] = buffer.buffer[ buffer.start_index : ] + n_remaining -= buffer_remaining buffer.start_index = 0 self._buffer_pool.append(buffer) waiter.set_result(output_buf) diff --git a/pymongo/pool_shared.py b/pymongo/pool_shared.py index 905f1a4d18..ace3aa9d9d 100644 --- a/pymongo/pool_shared.py +++ b/pymongo/pool_shared.py @@ -36,7 +36,12 @@ NetworkTimeout, _CertificateError, ) -from pymongo.network_layer import AsyncNetworkingInterface, NetworkingInterface, PyMongoProtocol +from pymongo.network_layer import ( + AsyncNetworkingInterface, + NetworkingInterface, + PyMongoBaseProtocol, + PyMongoProtocol, +) from pymongo.pool_options import PoolOptions from pymongo.ssl_support import PYSSLError, SSLError, _has_sni @@ -326,7 +331,7 @@ async def _async_configured_socket( async def _configured_protocol_interface( - address: _Address, options: PoolOptions + address: _Address, options: PoolOptions, protocol_kls: PyMongoBaseProtocol = PyMongoProtocol ) -> AsyncNetworkingInterface: """Given (host, port) and PoolOptions, return a configured AsyncNetworkingInterface. @@ -341,7 +346,7 @@ async def _configured_protocol_interface( if ssl_context is None: return AsyncNetworkingInterface( await asyncio.get_running_loop().create_connection( - lambda: PyMongoProtocol(timeout=timeout), sock=sock + lambda: protocol_kls(timeout=timeout), sock=sock ) ) @@ -350,7 +355,7 @@ async def _configured_protocol_interface( # We have to pass hostname / ip address to wrap_socket # to use SSLContext.check_hostname. transport, protocol = await asyncio.get_running_loop().create_connection( # type: ignore[call-overload] - lambda: PyMongoProtocol(timeout=timeout), + lambda: protocol_kls(timeout=timeout), sock=sock, server_hostname=host, ssl=ssl_context, diff --git a/pymongo/synchronous/encryption.py b/pymongo/synchronous/encryption.py index d1a1372761..820b93c9b6 100644 --- a/pymongo/synchronous/encryption.py +++ b/pymongo/synchronous/encryption.py @@ -70,7 +70,7 @@ NetworkTimeout, ServerSelectionTimeoutError, ) -from pymongo.network_layer import receive_kms, sendall +from pymongo.network_layer import PyMongoKMSProtocol, receive_kms, sendall from pymongo.operations import UpdateOne from pymongo.pool_options import PoolOptions from pymongo.pool_shared import ( @@ -186,16 +186,17 @@ def kms_request(self, kms_context: MongoCryptKmsContext) -> None: sleep_sec = float(sleep_u) / 1e6 time.sleep(sleep_sec) try: - interface = _configured_socket_interface(address, opts) + interface = _configured_socket_interface(address, opts, PyMongoKMSProtocol) conn = BaseConnection(interface, opts) try: sendall(interface.get_conn, message) - # CSOT: update timeout. - interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) - data = receive_kms(conn, kms_context.bytes_needed) - if not data: - raise OSError("KMS connection closed") - kms_context.feed(bytes) + while kms_context.bytes_needed > 0: + # CSOT: update timeout. + interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) + data = receive_kms(conn, kms_context.bytes_needed) + if not data: + raise OSError("KMS connection closed") + kms_context.feed(data) except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: @@ -211,7 +212,7 @@ def kms_request(self, kms_context: MongoCryptKmsContext) -> None: address, exc, msg_prefix=msg_prefix, timeout_details=_get_timeout_details(opts) ) finally: - conn.close_conn() + conn.close_conn(None) except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: From 13537cbb3ac4b51dca9a9aa77b6f62c0c2af556d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 6 Aug 2025 19:29:12 -0500 Subject: [PATCH 06/30] wip --- pymongo/asynchronous/encryption.py | 2 +- pymongo/network_layer.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pymongo/asynchronous/encryption.py b/pymongo/asynchronous/encryption.py index 51c9b3b802..b8175b3f04 100644 --- a/pymongo/asynchronous/encryption.py +++ b/pymongo/asynchronous/encryption.py @@ -213,7 +213,7 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None: address, exc, msg_prefix=msg_prefix, timeout_details=_get_timeout_details(opts) ) finally: - conn.close_conn(None) + await conn.close_conn(None) except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index d266b3fc43..ad058c3c99 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -503,6 +503,12 @@ def connection_made(self, transport: BaseTransport) -> None: async def read(self, bytes_needed: int) -> bytes: """Read the requested bytes from this connection.""" + if self.transport: + try: + self.transport.resume_reading() + # Known bug in SSL Protocols, fixed in Python 3.11: https://github.com/python/cpython/issues/89322 + except AttributeError: + raise OSError("connection is already closed") from None if self.transport and self.transport.is_closing(): raise OSError("connection is already closed") self._pending_reads.append(bytes_needed) @@ -552,11 +558,11 @@ def buffer_updated(self, nbytes: int) -> None: buffer_remaining = len(buffer.buffer) - buffer.start_index # if we didn't exhaust the buffer, read the partial data and put it back. if buffer_remaining > n_remaining: - n_remaining = 0 output_buf[out_index : n_remaining + out_index] = buffer.buffer[ buffer.start_index : buffer.start_index + n_remaining ] buffer.start_index += n_remaining + n_remaining = 0 self._buffers.appendleft(buffer) # otherwise exhaust the buffer and return it to the pool. else: From 18c51cb8e0d9de8251fe1d98f26bf655b221533b Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 6 Aug 2025 20:06:22 -0500 Subject: [PATCH 07/30] wip --- pymongo/network_layer.py | 26 +++++++---- uv.lock | 98 +++++++++++++++++++++++----------------- 2 files changed, 74 insertions(+), 50 deletions(-) diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index ad058c3c99..8a26988703 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -511,6 +511,8 @@ async def read(self, bytes_needed: int) -> bytes: raise OSError("connection is already closed") from None if self.transport and self.transport.is_closing(): raise OSError("connection is already closed") + if self._bytes_ready >= bytes_needed: + return self._read(bytes_needed) self._pending_reads.append(bytes_needed) read_waiter = asyncio.get_running_loop().create_future() self._pending_listeners.append(read_waiter) @@ -540,18 +542,24 @@ def buffer_updated(self, nbytes: int) -> None: self._bytes_ready += nbytes # Bail we don't have the current requested number of bytes. - n_requested = self._bytes_requested - if n_requested == 0 and self._pending_reads: - n_requested = self._pending_reads.popleft() - if n_requested == 0 or self._bytes_ready < n_requested: + bytes_needed = self._bytes_requested + if bytes_needed == 0 and self._pending_reads: + bytes_needed = self._pending_reads.popleft() + if bytes_needed == 0 or self._bytes_ready < bytes_needed: return + data = self._read(bytes_needed) + waiter = self._pending_listeners.popleft() + waiter.set_result(data) + + def _read(self, bytes_needed): + """Read bytes from the buffer.""" # Send the bytes to the listener. - self._bytes_ready -= n_requested + self._bytes_ready -= bytes_needed self._bytes_requested = 0 - waiter = self._pending_listeners.popleft() - output_buf = bytearray(n_requested) - n_remaining = n_requested + + output_buf = bytearray(bytes_needed) + n_remaining = bytes_needed out_index = 0 while n_remaining > 0: buffer = self._buffers.popleft() @@ -572,7 +580,7 @@ def buffer_updated(self, nbytes: int) -> None: n_remaining -= buffer_remaining buffer.start_index = 0 self._buffer_pool.append(buffer) - waiter.set_result(output_buf) + return output_buf async def async_sendall(conn: PyMongoBaseProtocol, buf: bytes) -> None: diff --git a/uv.lock b/uv.lock index 9c45c4cdb9..3472ab3578 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.9" resolution-markers = [ "python_full_version == '3.14.*'", @@ -1047,46 +1047,53 @@ dependencies = [ [[package]] name = "mypy" -version = "1.14.1" +version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, + { name = "pathspec" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051, upload-time = "2024-12-30T16:39:07.335Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/7a/87ae2adb31d68402da6da1e5f30c07ea6063e9f09b5e7cfc9dfa44075e74/mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb", size = 11211002, upload-time = "2024-12-30T16:37:22.435Z" }, - { url = "https://files.pythonhosted.org/packages/e1/23/eada4c38608b444618a132be0d199b280049ded278b24cbb9d3fc59658e4/mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0", size = 10358400, upload-time = "2024-12-30T16:37:53.526Z" }, - { url = "https://files.pythonhosted.org/packages/43/c9/d6785c6f66241c62fd2992b05057f404237deaad1566545e9f144ced07f5/mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d", size = 12095172, upload-time = "2024-12-30T16:37:50.332Z" }, - { url = "https://files.pythonhosted.org/packages/c3/62/daa7e787770c83c52ce2aaf1a111eae5893de9e004743f51bfcad9e487ec/mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b", size = 12828732, upload-time = "2024-12-30T16:37:29.96Z" }, - { url = "https://files.pythonhosted.org/packages/1b/a2/5fb18318a3637f29f16f4e41340b795da14f4751ef4f51c99ff39ab62e52/mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427", size = 13012197, upload-time = "2024-12-30T16:38:05.037Z" }, - { url = "https://files.pythonhosted.org/packages/28/99/e153ce39105d164b5f02c06c35c7ba958aaff50a2babba7d080988b03fe7/mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f", size = 9780836, upload-time = "2024-12-30T16:37:19.726Z" }, - { url = "https://files.pythonhosted.org/packages/da/11/a9422850fd506edbcdc7f6090682ecceaf1f87b9dd847f9df79942da8506/mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", size = 11120432, upload-time = "2024-12-30T16:37:11.533Z" }, - { url = "https://files.pythonhosted.org/packages/b6/9e/47e450fd39078d9c02d620545b2cb37993a8a8bdf7db3652ace2f80521ca/mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", size = 10279515, upload-time = "2024-12-30T16:37:40.724Z" }, - { url = "https://files.pythonhosted.org/packages/01/b5/6c8d33bd0f851a7692a8bfe4ee75eb82b6983a3cf39e5e32a5d2a723f0c1/mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", size = 12025791, upload-time = "2024-12-30T16:36:58.73Z" }, - { url = "https://files.pythonhosted.org/packages/f0/4c/e10e2c46ea37cab5c471d0ddaaa9a434dc1d28650078ac1b56c2d7b9b2e4/mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", size = 12749203, upload-time = "2024-12-30T16:37:03.741Z" }, - { url = "https://files.pythonhosted.org/packages/88/55/beacb0c69beab2153a0f57671ec07861d27d735a0faff135a494cd4f5020/mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", size = 12885900, upload-time = "2024-12-30T16:37:57.948Z" }, - { url = "https://files.pythonhosted.org/packages/a2/75/8c93ff7f315c4d086a2dfcde02f713004357d70a163eddb6c56a6a5eff40/mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", size = 9777869, upload-time = "2024-12-30T16:37:33.428Z" }, - { url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668, upload-time = "2024-12-30T16:38:02.211Z" }, - { url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060, upload-time = "2024-12-30T16:37:46.131Z" }, - { url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167, upload-time = "2024-12-30T16:37:43.534Z" }, - { url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341, upload-time = "2024-12-30T16:37:36.249Z" }, - { url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991, upload-time = "2024-12-30T16:37:06.743Z" }, - { url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016, upload-time = "2024-12-30T16:37:15.02Z" }, - { url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097, upload-time = "2024-12-30T16:37:25.144Z" }, - { url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728, upload-time = "2024-12-30T16:38:08.634Z" }, - { url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965, upload-time = "2024-12-30T16:38:12.132Z" }, - { url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660, upload-time = "2024-12-30T16:38:17.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198, upload-time = "2024-12-30T16:38:32.839Z" }, - { url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276, upload-time = "2024-12-30T16:38:20.828Z" }, - { url = "https://files.pythonhosted.org/packages/ca/1f/186d133ae2514633f8558e78cd658070ba686c0e9275c5a5c24a1e1f0d67/mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35", size = 11200493, upload-time = "2024-12-30T16:38:26.935Z" }, - { url = "https://files.pythonhosted.org/packages/af/fc/4842485d034e38a4646cccd1369f6b1ccd7bc86989c52770d75d719a9941/mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc", size = 10357702, upload-time = "2024-12-30T16:38:50.623Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e6/457b83f2d701e23869cfec013a48a12638f75b9d37612a9ddf99072c1051/mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9", size = 12091104, upload-time = "2024-12-30T16:38:53.735Z" }, - { url = "https://files.pythonhosted.org/packages/f1/bf/76a569158db678fee59f4fd30b8e7a0d75bcbaeef49edd882a0d63af6d66/mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb", size = 12830167, upload-time = "2024-12-30T16:38:56.437Z" }, - { url = "https://files.pythonhosted.org/packages/43/bc/0bc6b694b3103de9fed61867f1c8bd33336b913d16831431e7cb48ef1c92/mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60", size = 13013834, upload-time = "2024-12-30T16:38:59.204Z" }, - { url = "https://files.pythonhosted.org/packages/b0/79/5f5ec47849b6df1e6943d5fd8e6632fbfc04b4fd4acfa5a5a9535d11b4e2/mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c", size = 9781231, upload-time = "2024-12-30T16:39:05.124Z" }, - { url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905, upload-time = "2024-12-30T16:38:42.021Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/a9/3d7aa83955617cdf02f94e50aab5c830d205cfa4320cf124ff64acce3a8e/mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972", size = 11003299, upload-time = "2025-07-31T07:54:06.425Z" }, + { url = "https://files.pythonhosted.org/packages/83/e8/72e62ff837dd5caaac2b4a5c07ce769c8e808a00a65e5d8f94ea9c6f20ab/mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7", size = 10125451, upload-time = "2025-07-31T07:53:52.974Z" }, + { url = "https://files.pythonhosted.org/packages/7d/10/f3f3543f6448db11881776f26a0ed079865926b0c841818ee22de2c6bbab/mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df", size = 11916211, upload-time = "2025-07-31T07:53:18.879Z" }, + { url = "https://files.pythonhosted.org/packages/06/bf/63e83ed551282d67bb3f7fea2cd5561b08d2bb6eb287c096539feb5ddbc5/mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390", size = 12652687, upload-time = "2025-07-31T07:53:30.544Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/68f2eeef11facf597143e85b694a161868b3b006a5fbad50e09ea117ef24/mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94", size = 12896322, upload-time = "2025-07-31T07:53:50.74Z" }, + { url = "https://files.pythonhosted.org/packages/a3/87/8e3e9c2c8bd0d7e071a89c71be28ad088aaecbadf0454f46a540bda7bca6/mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b", size = 9507962, upload-time = "2025-07-31T07:53:08.431Z" }, + { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" }, + { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" }, + { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" }, + { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" }, + { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" }, + { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" }, + { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" }, + { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" }, + { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" }, + { url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" }, + { url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" }, + { url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" }, + { url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296, upload-time = "2025-07-31T07:53:03.896Z" }, + { url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657, upload-time = "2025-07-31T07:54:08.576Z" }, + { url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320, upload-time = "2025-07-31T07:53:01.341Z" }, + { url = "https://files.pythonhosted.org/packages/38/56/79c2fac86da57c7d8c48622a05873eaab40b905096c33597462713f5af90/mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733", size = 11040037, upload-time = "2025-07-31T07:54:10.942Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c3/adabe6ff53638e3cad19e3547268482408323b1e68bf082c9119000cd049/mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd", size = 10131550, upload-time = "2025-07-31T07:53:41.307Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c5/2e234c22c3bdeb23a7817af57a58865a39753bde52c74e2c661ee0cfc640/mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0", size = 11872963, upload-time = "2025-07-31T07:53:16.878Z" }, + { url = "https://files.pythonhosted.org/packages/ab/26/c13c130f35ca8caa5f2ceab68a247775648fdcd6c9a18f158825f2bc2410/mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a", size = 12710189, upload-time = "2025-07-31T07:54:01.962Z" }, + { url = "https://files.pythonhosted.org/packages/82/df/c7d79d09f6de8383fe800521d066d877e54d30b4fb94281c262be2df84ef/mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91", size = 12900322, upload-time = "2025-07-31T07:53:10.551Z" }, + { url = "https://files.pythonhosted.org/packages/b8/98/3d5a48978b4f708c55ae832619addc66d677f6dc59f3ebad71bae8285ca6/mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed", size = 9751879, upload-time = "2025-07-31T07:52:56.683Z" }, + { url = "https://files.pythonhosted.org/packages/29/cb/673e3d34e5d8de60b3a61f44f80150a738bff568cd6b7efb55742a605e98/mypy-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9", size = 10992466, upload-time = "2025-07-31T07:53:57.574Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d0/fe1895836eea3a33ab801561987a10569df92f2d3d4715abf2cfeaa29cb2/mypy-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99", size = 10117638, upload-time = "2025-07-31T07:53:34.256Z" }, + { url = "https://files.pythonhosted.org/packages/97/f3/514aa5532303aafb95b9ca400a31054a2bd9489de166558c2baaeea9c522/mypy-1.17.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8", size = 11915673, upload-time = "2025-07-31T07:52:59.361Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c3/c0805f0edec96fe8e2c048b03769a6291523d509be8ee7f56ae922fa3882/mypy-1.17.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8", size = 12649022, upload-time = "2025-07-31T07:53:45.92Z" }, + { url = "https://files.pythonhosted.org/packages/45/3e/d646b5a298ada21a8512fa7e5531f664535a495efa672601702398cea2b4/mypy-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259", size = 12895536, upload-time = "2025-07-31T07:53:06.17Z" }, + { url = "https://files.pythonhosted.org/packages/14/55/e13d0dcd276975927d1f4e9e2ec4fd409e199f01bdc671717e673cc63a22/mypy-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7598cf74c3e16539d4e2f0b8d8c318e00041553d83d4861f87c7a72e95ac24d", size = 9512564, upload-time = "2025-07-31T07:53:12.346Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, ] [[package]] @@ -1116,6 +1123,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + [[package]] name = "pip" version = "25.2" @@ -1315,7 +1331,7 @@ provides-extras = ["aws", "docs", "encryption", "gssapi", "ocsp", "snappy", "tes [package.metadata.requires-dev] coverage = [ - { name = "coverage", specifier = ">=5,<=7.5" }, + { name = "coverage", specifier = ">=5,<=7.10.2" }, { name = "pytest-cov" }, ] dev = [{ name = "pre-commit", specifier = ">=4.0" }] @@ -1329,9 +1345,9 @@ perf = [{ name = "simplejson" }] pip = [{ name = "pip" }] pymongocrypt-source = [{ name = "pymongocrypt", git = "https://github.com/mongodb/libmongocrypt?subdirectory=bindings%2Fpython&rev=master" }] typing = [ - { name = "mypy", specifier = "==1.14.1" }, + { name = "mypy", specifier = "==1.17.1" }, { name = "pip" }, - { name = "pyright", specifier = "==1.1.392.post0" }, + { name = "pyright", specifier = "==1.1.403" }, { name = "typing-extensions" }, ] @@ -1375,15 +1391,15 @@ wheels = [ [[package]] name = "pyright" -version = "1.1.392.post0" +version = "1.1.403" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/df/3c6f6b08fba7ccf49b114dfc4bb33e25c299883fd763f93fad47ef8bc58d/pyright-1.1.392.post0.tar.gz", hash = "sha256:3b7f88de74a28dcfa90c7d90c782b6569a48c2be5f9d4add38472bdaac247ebd", size = 3789911, upload-time = "2025-01-15T15:01:20.913Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/f6/35f885264ff08c960b23d1542038d8da86971c5d8c955cfab195a4f672d7/pyright-1.1.403.tar.gz", hash = "sha256:3ab69b9f41c67fb5bbb4d7a36243256f0d549ed3608678d381d5f51863921104", size = 3913526, upload-time = "2025-07-09T07:15:52.882Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/b1/a18de17f40e4f61ca58856b9ef9b0febf74ff88978c3f7776f910071f567/pyright-1.1.392.post0-py3-none-any.whl", hash = "sha256:252f84458a46fa2f0fd4e2f91fc74f50b9ca52c757062e93f6c250c0d8329eb2", size = 5595487, upload-time = "2025-01-15T15:01:17.775Z" }, + { url = "https://files.pythonhosted.org/packages/49/b6/b04e5c2f41a5ccad74a1a4759da41adb20b4bc9d59a5e08d29ba60084d07/pyright-1.1.403-py3-none-any.whl", hash = "sha256:c0eeca5aa76cbef3fcc271259bbd785753c7ad7bcac99a9162b4c4c7daed23b3", size = 5684504, upload-time = "2025-07-09T07:15:50.958Z" }, ] [[package]] From 24aa73341a8eceba7448afec38caefab8f500c44 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 6 Aug 2025 20:35:10 -0500 Subject: [PATCH 08/30] wip --- pymongo/asynchronous/encryption.py | 4 +++- pymongo/network_layer.py | 27 +++++++++++++++++---------- pymongo/synchronous/encryption.py | 4 +++- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/pymongo/asynchronous/encryption.py b/pymongo/asynchronous/encryption.py index b8175b3f04..c16b0f6efb 100644 --- a/pymongo/asynchronous/encryption.py +++ b/pymongo/asynchronous/encryption.py @@ -191,13 +191,15 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None: conn = AsyncBaseConnection(interface, opts) try: await async_sendall(interface.get_conn, message) + first = True while kms_context.bytes_needed > 0: # CSOT: update timeout. interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) - data = await async_receive_kms(conn, kms_context.bytes_needed) + data = await async_receive_kms(conn, kms_context.bytes_needed, first) if not data: raise OSError("KMS connection closed") kms_context.feed(data) + first = False except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 8a26988703..9924b2bc0d 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -501,8 +501,13 @@ def connection_made(self, transport: BaseTransport) -> None: """ self.transport = transport # type: ignore[assignment] - async def read(self, bytes_needed: int) -> bytes: + async def read(self, bytes_needed: int, first=False) -> bytes: """Read the requested bytes from this connection.""" + if self._bytes_ready >= bytes_needed or (self._bytes_ready > 0 and first): + # Wait for other listeners first. + if len(self._pending_listeners): + await asyncio.gather(*self._pending_listeners) + return self._read(bytes_needed) if self.transport: try: self.transport.resume_reading() @@ -511,9 +516,7 @@ async def read(self, bytes_needed: int) -> bytes: raise OSError("connection is already closed") from None if self.transport and self.transport.is_closing(): raise OSError("connection is already closed") - if self._bytes_ready >= bytes_needed: - return self._read(bytes_needed) - self._pending_reads.append(bytes_needed) + self._pending_reads.append((bytes_needed, first)) read_waiter = asyncio.get_running_loop().create_future() self._pending_listeners.append(read_waiter) return await read_waiter @@ -543,18 +546,22 @@ def buffer_updated(self, nbytes: int) -> None: # Bail we don't have the current requested number of bytes. bytes_needed = self._bytes_requested + first = False if bytes_needed == 0 and self._pending_reads: - bytes_needed = self._pending_reads.popleft() - if bytes_needed == 0 or self._bytes_ready < bytes_needed: + bytes_needed, first = self._pending_reads.popleft() + read_first = first and self._bytes_ready > 0 + if not read_first and (bytes_needed == 0 or self._bytes_ready < bytes_needed): return - data = self._read(bytes_needed) + data = self._read(bytes_needed, first) waiter = self._pending_listeners.popleft() waiter.set_result(data) - def _read(self, bytes_needed): + def _read(self, bytes_needed, first=False): """Read bytes from the buffer.""" # Send the bytes to the listener. + if first and self._bytes_ready < bytes_needed: + bytes_needed = self._bytes_ready self._bytes_ready -= bytes_needed self._bytes_requested = 0 @@ -591,13 +598,13 @@ async def async_sendall(conn: PyMongoBaseProtocol, buf: bytes) -> None: raise socket.timeout("timed out") from exc -async def async_receive_kms(conn: AsyncBaseConnection, bytes_needed: int) -> bytes: +async def async_receive_kms(conn: AsyncBaseConnection, bytes_needed: int, first=False) -> bytes: """Receive raw bytes from the kms connection.""" def callback(result: Any) -> bytes: return result - return await _async_receive_data(conn, callback, bytes_needed) + return await _async_receive_data(conn, callback, bytes_needed, first) async def _async_receive_data( diff --git a/pymongo/synchronous/encryption.py b/pymongo/synchronous/encryption.py index 820b93c9b6..a4f1d5c4db 100644 --- a/pymongo/synchronous/encryption.py +++ b/pymongo/synchronous/encryption.py @@ -190,13 +190,15 @@ def kms_request(self, kms_context: MongoCryptKmsContext) -> None: conn = BaseConnection(interface, opts) try: sendall(interface.get_conn, message) + first = True while kms_context.bytes_needed > 0: # CSOT: update timeout. interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) - data = receive_kms(conn, kms_context.bytes_needed) + data = receive_kms(conn, kms_context.bytes_needed, first) if not data: raise OSError("KMS connection closed") kms_context.feed(data) + first = False except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: From b33f78e0be10833b707f8488412306fd37cd241c Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 6 Aug 2025 20:50:07 -0500 Subject: [PATCH 09/30] wip --- pymongo/network_layer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 9924b2bc0d..8308eb416e 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -584,6 +584,7 @@ def _read(self, bytes_needed, first=False): output_buf[out_index : out_index + buffer_remaining] = buffer.buffer[ buffer.start_index : ] + out_index += buffer_remaining n_remaining -= buffer_remaining buffer.start_index = 0 self._buffer_pool.append(buffer) From 4b1bdd624576ed934ac7f56c49248480ca409118 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 6 Aug 2025 21:44:54 -0500 Subject: [PATCH 10/30] fixup --- pymongo/network_layer.py | 41 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 8308eb416e..115505cb31 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -482,16 +482,14 @@ def _resolve_pending(self, exc: Optional[Exception] = None) -> None: class KMSBuffer: buffer: memoryview start_index: int + length: int class PyMongoKMSProtocol(PyMongoBaseProtocol): def __init__(self, timeout: Optional[float] = None): super().__init__(timeout) self._buffers: collections.deque[KMSBuffer] = collections.deque() - # pool for buffers that have been exhausted and can be reused. - self._buffer_pool: collections.deque[KMSBuffer] = collections.deque(maxlen=3) self._bytes_ready = 0 - self._bytes_requested = 0 self._pending_reads: collections.deque[int] = collections.deque() self._pending_listeners: collections.deque[Future[Any]] = collections.deque() @@ -507,7 +505,7 @@ async def read(self, bytes_needed: int, first=False) -> bytes: # Wait for other listeners first. if len(self._pending_listeners): await asyncio.gather(*self._pending_listeners) - return self._read(bytes_needed) + return self._read(bytes_needed, first) if self.transport: try: self.transport.resume_reading() @@ -527,10 +525,8 @@ def get_buffer(self, sizehint: int) -> memoryview: If any data does not fit into the returned buffer, this method will be called again until either no data remains or an empty buffer is returned. """ - if self._buffer_pool: - buffer = self._buffer_pool.popleft() - else: - buffer = KMSBuffer(memoryview(bytearray(sizehint)), 0) + sizehint = max(sizehint, 1024) + buffer = KMSBuffer(memoryview(bytearray(sizehint)), 0, 0) self._buffers.append(buffer) return buffer.buffer @@ -544,13 +540,17 @@ def buffer_updated(self, nbytes: int) -> None: return self._bytes_ready += nbytes - # Bail we don't have the current requested number of bytes. - bytes_needed = self._bytes_requested - first = False - if bytes_needed == 0 and self._pending_reads: - bytes_needed, first = self._pending_reads.popleft() - read_first = first and self._bytes_ready > 0 - if not read_first and (bytes_needed == 0 or self._bytes_ready < bytes_needed): + # Update the length of the current buffer. + current_buffer = self._buffers.pop() + current_buffer.length += nbytes + self._buffers.append(current_buffer) + + if not len(self._pending_reads): + return + + bytes_needed, first = self._pending_reads.popleft() + if not first and (bytes_needed == 0 or self._bytes_ready < bytes_needed): + self._pending_reads.appendleft((bytes_needed, first)) return data = self._read(bytes_needed, first) @@ -563,15 +563,14 @@ def _read(self, bytes_needed, first=False): if first and self._bytes_ready < bytes_needed: bytes_needed = self._bytes_ready self._bytes_ready -= bytes_needed - self._bytes_requested = 0 output_buf = bytearray(bytes_needed) n_remaining = bytes_needed out_index = 0 while n_remaining > 0: buffer = self._buffers.popleft() - buffer_remaining = len(buffer.buffer) - buffer.start_index - # if we didn't exhaust the buffer, read the partial data and put it back. + buffer_remaining = buffer.length - buffer.start_index + # if we didn't exhaust the buffer, read the partial data and return the buffer. if buffer_remaining > n_remaining: output_buf[out_index : n_remaining + out_index] = buffer.buffer[ buffer.start_index : buffer.start_index + n_remaining @@ -579,15 +578,13 @@ def _read(self, bytes_needed, first=False): buffer.start_index += n_remaining n_remaining = 0 self._buffers.appendleft(buffer) - # otherwise exhaust the buffer and return it to the pool. + # otherwise exhaust the buffer. else: output_buf[out_index : out_index + buffer_remaining] = buffer.buffer[ - buffer.start_index : + buffer.start_index : buffer.length ] out_index += buffer_remaining n_remaining -= buffer_remaining - buffer.start_index = 0 - self._buffer_pool.append(buffer) return output_buf From fa0dd8dcec5566d8dae4a8c86633804003901d25 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 7 Aug 2025 12:43:42 -0500 Subject: [PATCH 11/30] always allow partial reads --- pymongo/asynchronous/encryption.py | 4 +--- pymongo/network_layer.py | 26 +++++++++++--------------- pymongo/synchronous/encryption.py | 4 +--- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/pymongo/asynchronous/encryption.py b/pymongo/asynchronous/encryption.py index c16b0f6efb..b8175b3f04 100644 --- a/pymongo/asynchronous/encryption.py +++ b/pymongo/asynchronous/encryption.py @@ -191,15 +191,13 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None: conn = AsyncBaseConnection(interface, opts) try: await async_sendall(interface.get_conn, message) - first = True while kms_context.bytes_needed > 0: # CSOT: update timeout. interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) - data = await async_receive_kms(conn, kms_context.bytes_needed, first) + data = await async_receive_kms(conn, kms_context.bytes_needed) if not data: raise OSError("KMS connection closed") kms_context.feed(data) - first = False except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 115505cb31..26f9fe7bbc 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -499,13 +499,13 @@ def connection_made(self, transport: BaseTransport) -> None: """ self.transport = transport # type: ignore[assignment] - async def read(self, bytes_needed: int, first=False) -> bytes: - """Read the requested bytes from this connection.""" - if self._bytes_ready >= bytes_needed or (self._bytes_ready > 0 and first): + async def read(self, bytes_needed: int) -> bytes: + """Read up to the requested bytes from this connection.""" + if self._bytes_ready > 0: # Wait for other listeners first. if len(self._pending_listeners): await asyncio.gather(*self._pending_listeners) - return self._read(bytes_needed, first) + return self._read(bytes_needed) if self.transport: try: self.transport.resume_reading() @@ -514,7 +514,7 @@ async def read(self, bytes_needed: int, first=False) -> bytes: raise OSError("connection is already closed") from None if self.transport and self.transport.is_closing(): raise OSError("connection is already closed") - self._pending_reads.append((bytes_needed, first)) + self._pending_reads.append(bytes_needed) read_waiter = asyncio.get_running_loop().create_future() self._pending_listeners.append(read_waiter) return await read_waiter @@ -548,19 +548,15 @@ def buffer_updated(self, nbytes: int) -> None: if not len(self._pending_reads): return - bytes_needed, first = self._pending_reads.popleft() - if not first and (bytes_needed == 0 or self._bytes_ready < bytes_needed): - self._pending_reads.appendleft((bytes_needed, first)) - return - - data = self._read(bytes_needed, first) + bytes_needed = self._pending_reads.popleft() + data = self._read(bytes_needed) waiter = self._pending_listeners.popleft() waiter.set_result(data) - def _read(self, bytes_needed, first=False): + def _read(self, bytes_needed): """Read bytes from the buffer.""" # Send the bytes to the listener. - if first and self._bytes_ready < bytes_needed: + if self._bytes_ready < bytes_needed: bytes_needed = self._bytes_ready self._bytes_ready -= bytes_needed @@ -596,13 +592,13 @@ async def async_sendall(conn: PyMongoBaseProtocol, buf: bytes) -> None: raise socket.timeout("timed out") from exc -async def async_receive_kms(conn: AsyncBaseConnection, bytes_needed: int, first=False) -> bytes: +async def async_receive_kms(conn: AsyncBaseConnection, bytes_needed: int) -> bytes: """Receive raw bytes from the kms connection.""" def callback(result: Any) -> bytes: return result - return await _async_receive_data(conn, callback, bytes_needed, first) + return await _async_receive_data(conn, callback, bytes_needed) async def _async_receive_data( diff --git a/pymongo/synchronous/encryption.py b/pymongo/synchronous/encryption.py index a4f1d5c4db..820b93c9b6 100644 --- a/pymongo/synchronous/encryption.py +++ b/pymongo/synchronous/encryption.py @@ -190,15 +190,13 @@ def kms_request(self, kms_context: MongoCryptKmsContext) -> None: conn = BaseConnection(interface, opts) try: sendall(interface.get_conn, message) - first = True while kms_context.bytes_needed > 0: # CSOT: update timeout. interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) - data = receive_kms(conn, kms_context.bytes_needed, first) + data = receive_kms(conn, kms_context.bytes_needed) if not data: raise OSError("KMS connection closed") kms_context.feed(data) - first = False except MongoCryptError: raise # Propagate MongoCryptError errors directly. except Exception as exc: From 432380ee62bb8aa99314fca42b9748571f73fd26 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 7 Aug 2025 18:40:31 -0500 Subject: [PATCH 12/30] fixup --- pymongo/pool_shared.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pymongo/pool_shared.py b/pymongo/pool_shared.py index ace3aa9d9d..688e712da1 100644 --- a/pymongo/pool_shared.py +++ b/pymongo/pool_shared.py @@ -504,7 +504,9 @@ def _configured_socket(address: _Address, options: PoolOptions) -> Union[socket. return ssl_sock -def _configured_socket_interface(address: _Address, options: PoolOptions) -> NetworkingInterface: +def _configured_socket_interface( + address: _Address, options: PoolOptions, *args: Any +) -> NetworkingInterface: """Given (host, port) and PoolOptions, return a NetworkingInterface wrapping a configured socket. Can raise socket.error, ConnectionFailure, or _CertificateError. From 484aa9f0bf8ece0fea1aac16c4384059ce484400 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 7 Aug 2025 19:36:00 -0500 Subject: [PATCH 13/30] cleanup --- justfile | 2 +- pymongo/network_layer.py | 15 +++--- pymongo/pool_shared.py | 112 ++------------------------------------- tools/synchro.py | 1 - uv.lock | 98 ++++++++++++++-------------------- 5 files changed, 52 insertions(+), 176 deletions(-) diff --git a/justfile b/justfile index 74ebb48823..5519066d87 100644 --- a/justfile +++ b/justfile @@ -2,7 +2,7 @@ set shell := ["bash", "-c"] # Commonly used command segments. -uv_run := "uv run --isolated --frozen " +uv_run := "uv run --frozen " typing_run := uv_run + "--group typing --extra aws --extra encryption --extra ocsp --extra snappy --extra test --extra zstd" docs_run := uv_run + "--extra docs" doc_build := "./doc/_build" diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 26f9fe7bbc..eec657a38e 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -41,9 +41,8 @@ from pymongo.socket_checker import _errno_from_exception if TYPE_CHECKING: - from ssl import SSLSocket - from pymongo.asynchronous.pool import AsyncBaseConnection + from pymongo.pyopenssl_context import _sslConn from pymongo.synchronous.pool import BaseConnection _UNPACK_HEADER = struct.Struct(" None: +def sendall(sock: Union[socket.socket, _sslConn], buf: bytes) -> None: sock.sendall(buf) @@ -222,7 +221,7 @@ def sock(self) -> socket.socket: class NetworkingInterface(NetworkingInterfaceBase): - def __init__(self, conn: Union[socket.socket, SSLSocket]): + def __init__(self, conn: Union[socket.socket, _sslConn]): super().__init__(conn) def gettimeout(self) -> float | None: @@ -238,11 +237,11 @@ def is_closing(self) -> bool: return self.conn.is_closing() @property - def get_conn(self) -> Union[socket.socket, SSLSocket]: + def get_conn(self) -> Union[socket.socket, _sslConn]: return self.conn @property - def sock(self) -> Union[socket.socket, SSLSocket]: + def sock(self) -> Union[socket.socket, _sslConn]: return self.conn def fileno(self) -> int: @@ -553,7 +552,7 @@ def buffer_updated(self, nbytes: int) -> None: waiter = self._pending_listeners.popleft() waiter.set_result(data) - def _read(self, bytes_needed): + def _read(self, bytes_needed: int) -> memoryview: """Read bytes from the buffer.""" # Send the bytes to the listener. if self._bytes_ready < bytes_needed: @@ -581,7 +580,7 @@ def _read(self, bytes_needed): ] out_index += buffer_remaining n_remaining -= buffer_remaining - return output_buf + return memoryview(output_buf) async def async_sendall(conn: PyMongoBaseProtocol, buf: bytes) -> None: diff --git a/pymongo/pool_shared.py b/pymongo/pool_shared.py index 688e712da1..f28226b791 100644 --- a/pymongo/pool_shared.py +++ b/pymongo/pool_shared.py @@ -16,7 +16,6 @@ from __future__ import annotations import asyncio -import functools import socket import ssl import sys @@ -25,7 +24,6 @@ Any, NoReturn, Optional, - Union, ) from pymongo import _csot @@ -47,7 +45,6 @@ SSLErrors = (PYSSLError, SSLError) if TYPE_CHECKING: - from pymongo.pyopenssl_context import _sslConn from pymongo.typings import _Address try: @@ -274,64 +271,10 @@ async def _async_create_connection(address: _Address, options: PoolOptions) -> s raise OSError("getaddrinfo failed") -async def _async_configured_socket( - address: _Address, options: PoolOptions -) -> Union[socket.socket, _sslConn]: - """Given (host, port) and PoolOptions, return a raw configured socket. - - Can raise socket.error, ConnectionFailure, or _CertificateError. - - Sets socket's SSL and timeout options. - """ - sock = await _async_create_connection(address, options) - ssl_context = options._ssl_context - - if ssl_context is None: - sock.settimeout(options.socket_timeout) - return sock - - host = address[0] - try: - # We have to pass hostname / ip address to wrap_socket - # to use SSLContext.check_hostname. - if _has_sni(False): - loop = asyncio.get_running_loop() - ssl_sock = await loop.run_in_executor( - None, - functools.partial(ssl_context.wrap_socket, sock, server_hostname=host), # type: ignore[assignment, misc, unused-ignore] - ) - else: - loop = asyncio.get_running_loop() - ssl_sock = await loop.run_in_executor(None, ssl_context.wrap_socket, sock) # type: ignore[assignment, misc, unused-ignore] - except _CertificateError: - sock.close() - # Raise _CertificateError directly like we do after match_hostname - # below. - raise - except (OSError, *SSLErrors) as exc: - sock.close() - # We raise AutoReconnect for transient and permanent SSL handshake - # failures alike. Permanent handshake failures, like protocol - # mismatch, will be turned into ServerSelectionTimeoutErrors later. - details = _get_timeout_details(options) - _raise_connection_failure(address, exc, "SSL handshake failed: ", timeout_details=details) - if ( - ssl_context.verify_mode - and not ssl_context.check_hostname - and not options.tls_allow_invalid_hostnames - ): - try: - ssl.match_hostname(ssl_sock.getpeercert(), hostname=host) # type:ignore[attr-defined, unused-ignore] - except _CertificateError: - ssl_sock.close() - raise - - ssl_sock.settimeout(options.socket_timeout) - return ssl_sock - - async def _configured_protocol_interface( - address: _Address, options: PoolOptions, protocol_kls: PyMongoBaseProtocol = PyMongoProtocol + address: _Address, + options: PoolOptions, + protocol_kls: type[PyMongoBaseProtocol] = PyMongoProtocol, ) -> AsyncNetworkingInterface: """Given (host, port) and PoolOptions, return a configured AsyncNetworkingInterface. @@ -455,55 +398,6 @@ def _create_connection(address: _Address, options: PoolOptions) -> socket.socket raise OSError("getaddrinfo failed") -def _configured_socket(address: _Address, options: PoolOptions) -> Union[socket.socket, _sslConn]: - """Given (host, port) and PoolOptions, return a raw configured socket. - - Can raise socket.error, ConnectionFailure, or _CertificateError. - - Sets socket's SSL and timeout options. - """ - sock = _create_connection(address, options) - ssl_context = options._ssl_context - - if ssl_context is None: - sock.settimeout(options.socket_timeout) - return sock - - host = address[0] - try: - # We have to pass hostname / ip address to wrap_socket - # to use SSLContext.check_hostname. - if _has_sni(True): - ssl_sock = ssl_context.wrap_socket(sock, server_hostname=host) # type: ignore[assignment, misc, unused-ignore] - else: - ssl_sock = ssl_context.wrap_socket(sock) # type: ignore[assignment, misc, unused-ignore] - except _CertificateError: - sock.close() - # Raise _CertificateError directly like we do after match_hostname - # below. - raise - except (OSError, *SSLErrors) as exc: - sock.close() - # We raise AutoReconnect for transient and permanent SSL handshake - # failures alike. Permanent handshake failures, like protocol - # mismatch, will be turned into ServerSelectionTimeoutErrors later. - details = _get_timeout_details(options) - _raise_connection_failure(address, exc, "SSL handshake failed: ", timeout_details=details) - if ( - ssl_context.verify_mode - and not ssl_context.check_hostname - and not options.tls_allow_invalid_hostnames - ): - try: - ssl.match_hostname(ssl_sock.getpeercert(), hostname=host) # type:ignore[attr-defined, unused-ignore] - except _CertificateError: - ssl_sock.close() - raise - - ssl_sock.settimeout(options.socket_timeout) - return ssl_sock - - def _configured_socket_interface( address: _Address, options: PoolOptions, *args: Any ) -> NetworkingInterface: diff --git a/tools/synchro.py b/tools/synchro.py index e2ce65d861..9a760c0ad7 100644 --- a/tools/synchro.py +++ b/tools/synchro.py @@ -123,7 +123,6 @@ "async_receive_kms": "receive_kms", "AsyncNetworkingInterface": "NetworkingInterface", "_configured_protocol_interface": "_configured_socket_interface", - "_async_configured_socket": "_configured_socket", "SpecRunnerTask": "SpecRunnerThread", "AsyncMockConnection": "MockConnection", "AsyncMockPool": "MockPool", diff --git a/uv.lock b/uv.lock index 3472ab3578..9c45c4cdb9 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.9" resolution-markers = [ "python_full_version == '3.14.*'", @@ -1047,53 +1047,46 @@ dependencies = [ [[package]] name = "mypy" -version = "1.17.1" +version = "1.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, - { name = "pathspec" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/a9/3d7aa83955617cdf02f94e50aab5c830d205cfa4320cf124ff64acce3a8e/mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972", size = 11003299, upload-time = "2025-07-31T07:54:06.425Z" }, - { url = "https://files.pythonhosted.org/packages/83/e8/72e62ff837dd5caaac2b4a5c07ce769c8e808a00a65e5d8f94ea9c6f20ab/mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7", size = 10125451, upload-time = "2025-07-31T07:53:52.974Z" }, - { url = "https://files.pythonhosted.org/packages/7d/10/f3f3543f6448db11881776f26a0ed079865926b0c841818ee22de2c6bbab/mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df", size = 11916211, upload-time = "2025-07-31T07:53:18.879Z" }, - { url = "https://files.pythonhosted.org/packages/06/bf/63e83ed551282d67bb3f7fea2cd5561b08d2bb6eb287c096539feb5ddbc5/mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390", size = 12652687, upload-time = "2025-07-31T07:53:30.544Z" }, - { url = "https://files.pythonhosted.org/packages/69/66/68f2eeef11facf597143e85b694a161868b3b006a5fbad50e09ea117ef24/mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94", size = 12896322, upload-time = "2025-07-31T07:53:50.74Z" }, - { url = "https://files.pythonhosted.org/packages/a3/87/8e3e9c2c8bd0d7e071a89c71be28ad088aaecbadf0454f46a540bda7bca6/mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b", size = 9507962, upload-time = "2025-07-31T07:53:08.431Z" }, - { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" }, - { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" }, - { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" }, - { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" }, - { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" }, - { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" }, - { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" }, - { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" }, - { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" }, - { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" }, - { url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" }, - { url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" }, - { url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" }, - { url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296, upload-time = "2025-07-31T07:53:03.896Z" }, - { url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657, upload-time = "2025-07-31T07:54:08.576Z" }, - { url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320, upload-time = "2025-07-31T07:53:01.341Z" }, - { url = "https://files.pythonhosted.org/packages/38/56/79c2fac86da57c7d8c48622a05873eaab40b905096c33597462713f5af90/mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733", size = 11040037, upload-time = "2025-07-31T07:54:10.942Z" }, - { url = "https://files.pythonhosted.org/packages/4d/c3/adabe6ff53638e3cad19e3547268482408323b1e68bf082c9119000cd049/mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd", size = 10131550, upload-time = "2025-07-31T07:53:41.307Z" }, - { url = "https://files.pythonhosted.org/packages/b8/c5/2e234c22c3bdeb23a7817af57a58865a39753bde52c74e2c661ee0cfc640/mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0", size = 11872963, upload-time = "2025-07-31T07:53:16.878Z" }, - { url = "https://files.pythonhosted.org/packages/ab/26/c13c130f35ca8caa5f2ceab68a247775648fdcd6c9a18f158825f2bc2410/mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a", size = 12710189, upload-time = "2025-07-31T07:54:01.962Z" }, - { url = "https://files.pythonhosted.org/packages/82/df/c7d79d09f6de8383fe800521d066d877e54d30b4fb94281c262be2df84ef/mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91", size = 12900322, upload-time = "2025-07-31T07:53:10.551Z" }, - { url = "https://files.pythonhosted.org/packages/b8/98/3d5a48978b4f708c55ae832619addc66d677f6dc59f3ebad71bae8285ca6/mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed", size = 9751879, upload-time = "2025-07-31T07:52:56.683Z" }, - { url = "https://files.pythonhosted.org/packages/29/cb/673e3d34e5d8de60b3a61f44f80150a738bff568cd6b7efb55742a605e98/mypy-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9", size = 10992466, upload-time = "2025-07-31T07:53:57.574Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d0/fe1895836eea3a33ab801561987a10569df92f2d3d4715abf2cfeaa29cb2/mypy-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99", size = 10117638, upload-time = "2025-07-31T07:53:34.256Z" }, - { url = "https://files.pythonhosted.org/packages/97/f3/514aa5532303aafb95b9ca400a31054a2bd9489de166558c2baaeea9c522/mypy-1.17.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8", size = 11915673, upload-time = "2025-07-31T07:52:59.361Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c3/c0805f0edec96fe8e2c048b03769a6291523d509be8ee7f56ae922fa3882/mypy-1.17.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8", size = 12649022, upload-time = "2025-07-31T07:53:45.92Z" }, - { url = "https://files.pythonhosted.org/packages/45/3e/d646b5a298ada21a8512fa7e5531f664535a495efa672601702398cea2b4/mypy-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259", size = 12895536, upload-time = "2025-07-31T07:53:06.17Z" }, - { url = "https://files.pythonhosted.org/packages/14/55/e13d0dcd276975927d1f4e9e2ec4fd409e199f01bdc671717e673cc63a22/mypy-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7598cf74c3e16539d4e2f0b8d8c318e00041553d83d4861f87c7a72e95ac24d", size = 9512564, upload-time = "2025-07-31T07:53:12.346Z" }, - { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051, upload-time = "2024-12-30T16:39:07.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/7a/87ae2adb31d68402da6da1e5f30c07ea6063e9f09b5e7cfc9dfa44075e74/mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb", size = 11211002, upload-time = "2024-12-30T16:37:22.435Z" }, + { url = "https://files.pythonhosted.org/packages/e1/23/eada4c38608b444618a132be0d199b280049ded278b24cbb9d3fc59658e4/mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0", size = 10358400, upload-time = "2024-12-30T16:37:53.526Z" }, + { url = "https://files.pythonhosted.org/packages/43/c9/d6785c6f66241c62fd2992b05057f404237deaad1566545e9f144ced07f5/mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d", size = 12095172, upload-time = "2024-12-30T16:37:50.332Z" }, + { url = "https://files.pythonhosted.org/packages/c3/62/daa7e787770c83c52ce2aaf1a111eae5893de9e004743f51bfcad9e487ec/mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b", size = 12828732, upload-time = "2024-12-30T16:37:29.96Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a2/5fb18318a3637f29f16f4e41340b795da14f4751ef4f51c99ff39ab62e52/mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427", size = 13012197, upload-time = "2024-12-30T16:38:05.037Z" }, + { url = "https://files.pythonhosted.org/packages/28/99/e153ce39105d164b5f02c06c35c7ba958aaff50a2babba7d080988b03fe7/mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f", size = 9780836, upload-time = "2024-12-30T16:37:19.726Z" }, + { url = "https://files.pythonhosted.org/packages/da/11/a9422850fd506edbcdc7f6090682ecceaf1f87b9dd847f9df79942da8506/mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", size = 11120432, upload-time = "2024-12-30T16:37:11.533Z" }, + { url = "https://files.pythonhosted.org/packages/b6/9e/47e450fd39078d9c02d620545b2cb37993a8a8bdf7db3652ace2f80521ca/mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", size = 10279515, upload-time = "2024-12-30T16:37:40.724Z" }, + { url = "https://files.pythonhosted.org/packages/01/b5/6c8d33bd0f851a7692a8bfe4ee75eb82b6983a3cf39e5e32a5d2a723f0c1/mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", size = 12025791, upload-time = "2024-12-30T16:36:58.73Z" }, + { url = "https://files.pythonhosted.org/packages/f0/4c/e10e2c46ea37cab5c471d0ddaaa9a434dc1d28650078ac1b56c2d7b9b2e4/mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", size = 12749203, upload-time = "2024-12-30T16:37:03.741Z" }, + { url = "https://files.pythonhosted.org/packages/88/55/beacb0c69beab2153a0f57671ec07861d27d735a0faff135a494cd4f5020/mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", size = 12885900, upload-time = "2024-12-30T16:37:57.948Z" }, + { url = "https://files.pythonhosted.org/packages/a2/75/8c93ff7f315c4d086a2dfcde02f713004357d70a163eddb6c56a6a5eff40/mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", size = 9777869, upload-time = "2024-12-30T16:37:33.428Z" }, + { url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668, upload-time = "2024-12-30T16:38:02.211Z" }, + { url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060, upload-time = "2024-12-30T16:37:46.131Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167, upload-time = "2024-12-30T16:37:43.534Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341, upload-time = "2024-12-30T16:37:36.249Z" }, + { url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991, upload-time = "2024-12-30T16:37:06.743Z" }, + { url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016, upload-time = "2024-12-30T16:37:15.02Z" }, + { url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097, upload-time = "2024-12-30T16:37:25.144Z" }, + { url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728, upload-time = "2024-12-30T16:38:08.634Z" }, + { url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965, upload-time = "2024-12-30T16:38:12.132Z" }, + { url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660, upload-time = "2024-12-30T16:38:17.342Z" }, + { url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198, upload-time = "2024-12-30T16:38:32.839Z" }, + { url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276, upload-time = "2024-12-30T16:38:20.828Z" }, + { url = "https://files.pythonhosted.org/packages/ca/1f/186d133ae2514633f8558e78cd658070ba686c0e9275c5a5c24a1e1f0d67/mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35", size = 11200493, upload-time = "2024-12-30T16:38:26.935Z" }, + { url = "https://files.pythonhosted.org/packages/af/fc/4842485d034e38a4646cccd1369f6b1ccd7bc86989c52770d75d719a9941/mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc", size = 10357702, upload-time = "2024-12-30T16:38:50.623Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e6/457b83f2d701e23869cfec013a48a12638f75b9d37612a9ddf99072c1051/mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9", size = 12091104, upload-time = "2024-12-30T16:38:53.735Z" }, + { url = "https://files.pythonhosted.org/packages/f1/bf/76a569158db678fee59f4fd30b8e7a0d75bcbaeef49edd882a0d63af6d66/mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb", size = 12830167, upload-time = "2024-12-30T16:38:56.437Z" }, + { url = "https://files.pythonhosted.org/packages/43/bc/0bc6b694b3103de9fed61867f1c8bd33336b913d16831431e7cb48ef1c92/mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60", size = 13013834, upload-time = "2024-12-30T16:38:59.204Z" }, + { url = "https://files.pythonhosted.org/packages/b0/79/5f5ec47849b6df1e6943d5fd8e6632fbfc04b4fd4acfa5a5a9535d11b4e2/mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c", size = 9781231, upload-time = "2024-12-30T16:39:05.124Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905, upload-time = "2024-12-30T16:38:42.021Z" }, ] [[package]] @@ -1123,15 +1116,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, -] - [[package]] name = "pip" version = "25.2" @@ -1331,7 +1315,7 @@ provides-extras = ["aws", "docs", "encryption", "gssapi", "ocsp", "snappy", "tes [package.metadata.requires-dev] coverage = [ - { name = "coverage", specifier = ">=5,<=7.10.2" }, + { name = "coverage", specifier = ">=5,<=7.5" }, { name = "pytest-cov" }, ] dev = [{ name = "pre-commit", specifier = ">=4.0" }] @@ -1345,9 +1329,9 @@ perf = [{ name = "simplejson" }] pip = [{ name = "pip" }] pymongocrypt-source = [{ name = "pymongocrypt", git = "https://github.com/mongodb/libmongocrypt?subdirectory=bindings%2Fpython&rev=master" }] typing = [ - { name = "mypy", specifier = "==1.17.1" }, + { name = "mypy", specifier = "==1.14.1" }, { name = "pip" }, - { name = "pyright", specifier = "==1.1.403" }, + { name = "pyright", specifier = "==1.1.392.post0" }, { name = "typing-extensions" }, ] @@ -1391,15 +1375,15 @@ wheels = [ [[package]] name = "pyright" -version = "1.1.403" +version = "1.1.392.post0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fe/f6/35f885264ff08c960b23d1542038d8da86971c5d8c955cfab195a4f672d7/pyright-1.1.403.tar.gz", hash = "sha256:3ab69b9f41c67fb5bbb4d7a36243256f0d549ed3608678d381d5f51863921104", size = 3913526, upload-time = "2025-07-09T07:15:52.882Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/df/3c6f6b08fba7ccf49b114dfc4bb33e25c299883fd763f93fad47ef8bc58d/pyright-1.1.392.post0.tar.gz", hash = "sha256:3b7f88de74a28dcfa90c7d90c782b6569a48c2be5f9d4add38472bdaac247ebd", size = 3789911, upload-time = "2025-01-15T15:01:20.913Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/b6/b04e5c2f41a5ccad74a1a4759da41adb20b4bc9d59a5e08d29ba60084d07/pyright-1.1.403-py3-none-any.whl", hash = "sha256:c0eeca5aa76cbef3fcc271259bbd785753c7ad7bcac99a9162b4c4c7daed23b3", size = 5684504, upload-time = "2025-07-09T07:15:50.958Z" }, + { url = "https://files.pythonhosted.org/packages/e7/b1/a18de17f40e4f61ca58856b9ef9b0febf74ff88978c3f7776f910071f567/pyright-1.1.392.post0-py3-none-any.whl", hash = "sha256:252f84458a46fa2f0fd4e2f91fc74f50b9ca52c757062e93f6c250c0d8329eb2", size = 5595487, upload-time = "2025-01-15T15:01:17.775Z" }, ] [[package]] From e50685cc8cde44f49f4479a325f63ffe89e493a2 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 7 Aug 2025 21:02:15 -0500 Subject: [PATCH 14/30] fix sync kms --- pymongo/asynchronous/encryption.py | 21 +++++-- pymongo/network_layer.py | 18 ++---- pymongo/synchronous/encryption.py | 21 +++++-- uv.lock | 98 +++++++++++++++++------------- 4 files changed, 96 insertions(+), 62 deletions(-) diff --git a/pymongo/asynchronous/encryption.py b/pymongo/asynchronous/encryption.py index b8175b3f04..2d3e702a79 100644 --- a/pymongo/asynchronous/encryption.py +++ b/pymongo/asynchronous/encryption.py @@ -94,6 +94,7 @@ if TYPE_CHECKING: from pymongocrypt.mongocrypt import MongoCryptKmsContext + from pymongo.typings import _Address _IS_SYNC = False @@ -109,6 +110,14 @@ _KEY_VAULT_OPTS = CodecOptions(document_class=RawBSONDocument) +async def _connect_kms(address: _Address, opts: PoolOptions) -> AsyncBaseConnection: + try: + interface = await _configured_protocol_interface(address, opts, PyMongoKMSProtocol) + return AsyncBaseConnection(interface, opts) + except Exception as exc: + _raise_connection_failure(address, exc, timeout_details=_get_timeout_details(opts)) + + @contextlib.contextmanager def _wrap_encryption_errors() -> Iterator[None]: """Context manager to wrap encryption related errors.""" @@ -187,13 +196,17 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None: sleep_sec = float(sleep_u) / 1e6 await asyncio.sleep(sleep_sec) try: - interface = await _configured_protocol_interface(address, opts, PyMongoKMSProtocol) - conn = AsyncBaseConnection(interface, opts) + conn = await _connect_kms(address, opts) try: - await async_sendall(interface.get_conn, message) + await async_sendall(conn.conn.get_conn, message) while kms_context.bytes_needed > 0: # CSOT: update timeout. - interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) + conn.set_conn_timeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) + # if _IS_SYNC: + # # TODO: why can't we use receive_kms? + # data = conn.conn.sock.recv(kms_context.bytes_needed) + # else: + # data = await async_receive_kms(conn, kms_context.bytes_needed) data = await async_receive_kms(conn, kms_context.bytes_needed) if not data: raise OSError("KMS connection closed") diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index eec657a38e..345ebc692a 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -41,9 +41,9 @@ from pymongo.socket_checker import _errno_from_exception if TYPE_CHECKING: - from pymongo.asynchronous.pool import AsyncBaseConnection + from pymongo.asynchronous.pool import AsyncBaseConnection, AsyncConnection from pymongo.pyopenssl_context import _sslConn - from pymongo.synchronous.pool import BaseConnection + from pymongo.synchronous.pool import BaseConnection, Connection _UNPACK_HEADER = struct.Struct(" Union[_OpReply, _OpMsg]: @@ -663,19 +663,11 @@ def callback(result: Any) -> _OpMsg | _OpReply: def receive_kms(conn: BaseConnection, bytes_needed: int) -> bytes: """Receive raw bytes from the kms connection.""" - if _csot.get_timeout(): - deadline = _csot.get_deadline() - else: - timeout = conn.conn.gettimeout() - if timeout: - deadline = time.monotonic() + timeout - else: - deadline = None - return receive_data(conn, bytes_needed, deadline) + return conn.conn.sock.recv(bytes_needed) def receive_message( - conn: BaseConnection, request_id: Optional[int], max_message_size: int = MAX_MESSAGE_SIZE + conn: Connection, request_id: Optional[int], max_message_size: int = MAX_MESSAGE_SIZE ) -> Union[_OpReply, _OpMsg]: """Receive a raw BSON message or raise socket.error.""" if _csot.get_timeout(): diff --git a/pymongo/synchronous/encryption.py b/pymongo/synchronous/encryption.py index 820b93c9b6..2da3cd6669 100644 --- a/pymongo/synchronous/encryption.py +++ b/pymongo/synchronous/encryption.py @@ -93,6 +93,7 @@ if TYPE_CHECKING: from pymongocrypt.mongocrypt import MongoCryptKmsContext + from pymongo.typings import _Address _IS_SYNC = True @@ -108,6 +109,14 @@ _KEY_VAULT_OPTS = CodecOptions(document_class=RawBSONDocument) +def _connect_kms(address: _Address, opts: PoolOptions) -> BaseConnection: + try: + interface = _configured_socket_interface(address, opts, PyMongoKMSProtocol) + return BaseConnection(interface, opts) + except Exception as exc: + _raise_connection_failure(address, exc, timeout_details=_get_timeout_details(opts)) + + @contextlib.contextmanager def _wrap_encryption_errors() -> Iterator[None]: """Context manager to wrap encryption related errors.""" @@ -186,13 +195,17 @@ def kms_request(self, kms_context: MongoCryptKmsContext) -> None: sleep_sec = float(sleep_u) / 1e6 time.sleep(sleep_sec) try: - interface = _configured_socket_interface(address, opts, PyMongoKMSProtocol) - conn = BaseConnection(interface, opts) + conn = _connect_kms(address, opts) try: - sendall(interface.get_conn, message) + sendall(conn.conn.get_conn, message) while kms_context.bytes_needed > 0: # CSOT: update timeout. - interface.settimeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) + conn.set_conn_timeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) + # if _IS_SYNC: + # # TODO: why can't we use receive_kms? + # data = conn.conn.sock.recv(kms_context.bytes_needed) + # else: + # data = receive_kms(conn, kms_context.bytes_needed) data = receive_kms(conn, kms_context.bytes_needed) if not data: raise OSError("KMS connection closed") diff --git a/uv.lock b/uv.lock index 9c45c4cdb9..3472ab3578 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.9" resolution-markers = [ "python_full_version == '3.14.*'", @@ -1047,46 +1047,53 @@ dependencies = [ [[package]] name = "mypy" -version = "1.14.1" +version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, + { name = "pathspec" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051, upload-time = "2024-12-30T16:39:07.335Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/7a/87ae2adb31d68402da6da1e5f30c07ea6063e9f09b5e7cfc9dfa44075e74/mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb", size = 11211002, upload-time = "2024-12-30T16:37:22.435Z" }, - { url = "https://files.pythonhosted.org/packages/e1/23/eada4c38608b444618a132be0d199b280049ded278b24cbb9d3fc59658e4/mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0", size = 10358400, upload-time = "2024-12-30T16:37:53.526Z" }, - { url = "https://files.pythonhosted.org/packages/43/c9/d6785c6f66241c62fd2992b05057f404237deaad1566545e9f144ced07f5/mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d", size = 12095172, upload-time = "2024-12-30T16:37:50.332Z" }, - { url = "https://files.pythonhosted.org/packages/c3/62/daa7e787770c83c52ce2aaf1a111eae5893de9e004743f51bfcad9e487ec/mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b", size = 12828732, upload-time = "2024-12-30T16:37:29.96Z" }, - { url = "https://files.pythonhosted.org/packages/1b/a2/5fb18318a3637f29f16f4e41340b795da14f4751ef4f51c99ff39ab62e52/mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427", size = 13012197, upload-time = "2024-12-30T16:38:05.037Z" }, - { url = "https://files.pythonhosted.org/packages/28/99/e153ce39105d164b5f02c06c35c7ba958aaff50a2babba7d080988b03fe7/mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f", size = 9780836, upload-time = "2024-12-30T16:37:19.726Z" }, - { url = "https://files.pythonhosted.org/packages/da/11/a9422850fd506edbcdc7f6090682ecceaf1f87b9dd847f9df79942da8506/mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", size = 11120432, upload-time = "2024-12-30T16:37:11.533Z" }, - { url = "https://files.pythonhosted.org/packages/b6/9e/47e450fd39078d9c02d620545b2cb37993a8a8bdf7db3652ace2f80521ca/mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", size = 10279515, upload-time = "2024-12-30T16:37:40.724Z" }, - { url = "https://files.pythonhosted.org/packages/01/b5/6c8d33bd0f851a7692a8bfe4ee75eb82b6983a3cf39e5e32a5d2a723f0c1/mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", size = 12025791, upload-time = "2024-12-30T16:36:58.73Z" }, - { url = "https://files.pythonhosted.org/packages/f0/4c/e10e2c46ea37cab5c471d0ddaaa9a434dc1d28650078ac1b56c2d7b9b2e4/mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", size = 12749203, upload-time = "2024-12-30T16:37:03.741Z" }, - { url = "https://files.pythonhosted.org/packages/88/55/beacb0c69beab2153a0f57671ec07861d27d735a0faff135a494cd4f5020/mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", size = 12885900, upload-time = "2024-12-30T16:37:57.948Z" }, - { url = "https://files.pythonhosted.org/packages/a2/75/8c93ff7f315c4d086a2dfcde02f713004357d70a163eddb6c56a6a5eff40/mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", size = 9777869, upload-time = "2024-12-30T16:37:33.428Z" }, - { url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668, upload-time = "2024-12-30T16:38:02.211Z" }, - { url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060, upload-time = "2024-12-30T16:37:46.131Z" }, - { url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167, upload-time = "2024-12-30T16:37:43.534Z" }, - { url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341, upload-time = "2024-12-30T16:37:36.249Z" }, - { url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991, upload-time = "2024-12-30T16:37:06.743Z" }, - { url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016, upload-time = "2024-12-30T16:37:15.02Z" }, - { url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097, upload-time = "2024-12-30T16:37:25.144Z" }, - { url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728, upload-time = "2024-12-30T16:38:08.634Z" }, - { url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965, upload-time = "2024-12-30T16:38:12.132Z" }, - { url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660, upload-time = "2024-12-30T16:38:17.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198, upload-time = "2024-12-30T16:38:32.839Z" }, - { url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276, upload-time = "2024-12-30T16:38:20.828Z" }, - { url = "https://files.pythonhosted.org/packages/ca/1f/186d133ae2514633f8558e78cd658070ba686c0e9275c5a5c24a1e1f0d67/mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35", size = 11200493, upload-time = "2024-12-30T16:38:26.935Z" }, - { url = "https://files.pythonhosted.org/packages/af/fc/4842485d034e38a4646cccd1369f6b1ccd7bc86989c52770d75d719a9941/mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc", size = 10357702, upload-time = "2024-12-30T16:38:50.623Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e6/457b83f2d701e23869cfec013a48a12638f75b9d37612a9ddf99072c1051/mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9", size = 12091104, upload-time = "2024-12-30T16:38:53.735Z" }, - { url = "https://files.pythonhosted.org/packages/f1/bf/76a569158db678fee59f4fd30b8e7a0d75bcbaeef49edd882a0d63af6d66/mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb", size = 12830167, upload-time = "2024-12-30T16:38:56.437Z" }, - { url = "https://files.pythonhosted.org/packages/43/bc/0bc6b694b3103de9fed61867f1c8bd33336b913d16831431e7cb48ef1c92/mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60", size = 13013834, upload-time = "2024-12-30T16:38:59.204Z" }, - { url = "https://files.pythonhosted.org/packages/b0/79/5f5ec47849b6df1e6943d5fd8e6632fbfc04b4fd4acfa5a5a9535d11b4e2/mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c", size = 9781231, upload-time = "2024-12-30T16:39:05.124Z" }, - { url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905, upload-time = "2024-12-30T16:38:42.021Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/a9/3d7aa83955617cdf02f94e50aab5c830d205cfa4320cf124ff64acce3a8e/mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972", size = 11003299, upload-time = "2025-07-31T07:54:06.425Z" }, + { url = "https://files.pythonhosted.org/packages/83/e8/72e62ff837dd5caaac2b4a5c07ce769c8e808a00a65e5d8f94ea9c6f20ab/mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7", size = 10125451, upload-time = "2025-07-31T07:53:52.974Z" }, + { url = "https://files.pythonhosted.org/packages/7d/10/f3f3543f6448db11881776f26a0ed079865926b0c841818ee22de2c6bbab/mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df", size = 11916211, upload-time = "2025-07-31T07:53:18.879Z" }, + { url = "https://files.pythonhosted.org/packages/06/bf/63e83ed551282d67bb3f7fea2cd5561b08d2bb6eb287c096539feb5ddbc5/mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390", size = 12652687, upload-time = "2025-07-31T07:53:30.544Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/68f2eeef11facf597143e85b694a161868b3b006a5fbad50e09ea117ef24/mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94", size = 12896322, upload-time = "2025-07-31T07:53:50.74Z" }, + { url = "https://files.pythonhosted.org/packages/a3/87/8e3e9c2c8bd0d7e071a89c71be28ad088aaecbadf0454f46a540bda7bca6/mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b", size = 9507962, upload-time = "2025-07-31T07:53:08.431Z" }, + { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" }, + { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" }, + { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" }, + { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" }, + { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" }, + { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" }, + { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" }, + { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" }, + { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" }, + { url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" }, + { url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" }, + { url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" }, + { url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296, upload-time = "2025-07-31T07:53:03.896Z" }, + { url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657, upload-time = "2025-07-31T07:54:08.576Z" }, + { url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320, upload-time = "2025-07-31T07:53:01.341Z" }, + { url = "https://files.pythonhosted.org/packages/38/56/79c2fac86da57c7d8c48622a05873eaab40b905096c33597462713f5af90/mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733", size = 11040037, upload-time = "2025-07-31T07:54:10.942Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c3/adabe6ff53638e3cad19e3547268482408323b1e68bf082c9119000cd049/mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd", size = 10131550, upload-time = "2025-07-31T07:53:41.307Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c5/2e234c22c3bdeb23a7817af57a58865a39753bde52c74e2c661ee0cfc640/mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0", size = 11872963, upload-time = "2025-07-31T07:53:16.878Z" }, + { url = "https://files.pythonhosted.org/packages/ab/26/c13c130f35ca8caa5f2ceab68a247775648fdcd6c9a18f158825f2bc2410/mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a", size = 12710189, upload-time = "2025-07-31T07:54:01.962Z" }, + { url = "https://files.pythonhosted.org/packages/82/df/c7d79d09f6de8383fe800521d066d877e54d30b4fb94281c262be2df84ef/mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91", size = 12900322, upload-time = "2025-07-31T07:53:10.551Z" }, + { url = "https://files.pythonhosted.org/packages/b8/98/3d5a48978b4f708c55ae832619addc66d677f6dc59f3ebad71bae8285ca6/mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed", size = 9751879, upload-time = "2025-07-31T07:52:56.683Z" }, + { url = "https://files.pythonhosted.org/packages/29/cb/673e3d34e5d8de60b3a61f44f80150a738bff568cd6b7efb55742a605e98/mypy-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9", size = 10992466, upload-time = "2025-07-31T07:53:57.574Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d0/fe1895836eea3a33ab801561987a10569df92f2d3d4715abf2cfeaa29cb2/mypy-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99", size = 10117638, upload-time = "2025-07-31T07:53:34.256Z" }, + { url = "https://files.pythonhosted.org/packages/97/f3/514aa5532303aafb95b9ca400a31054a2bd9489de166558c2baaeea9c522/mypy-1.17.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8", size = 11915673, upload-time = "2025-07-31T07:52:59.361Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c3/c0805f0edec96fe8e2c048b03769a6291523d509be8ee7f56ae922fa3882/mypy-1.17.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8", size = 12649022, upload-time = "2025-07-31T07:53:45.92Z" }, + { url = "https://files.pythonhosted.org/packages/45/3e/d646b5a298ada21a8512fa7e5531f664535a495efa672601702398cea2b4/mypy-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259", size = 12895536, upload-time = "2025-07-31T07:53:06.17Z" }, + { url = "https://files.pythonhosted.org/packages/14/55/e13d0dcd276975927d1f4e9e2ec4fd409e199f01bdc671717e673cc63a22/mypy-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7598cf74c3e16539d4e2f0b8d8c318e00041553d83d4861f87c7a72e95ac24d", size = 9512564, upload-time = "2025-07-31T07:53:12.346Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, ] [[package]] @@ -1116,6 +1123,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + [[package]] name = "pip" version = "25.2" @@ -1315,7 +1331,7 @@ provides-extras = ["aws", "docs", "encryption", "gssapi", "ocsp", "snappy", "tes [package.metadata.requires-dev] coverage = [ - { name = "coverage", specifier = ">=5,<=7.5" }, + { name = "coverage", specifier = ">=5,<=7.10.2" }, { name = "pytest-cov" }, ] dev = [{ name = "pre-commit", specifier = ">=4.0" }] @@ -1329,9 +1345,9 @@ perf = [{ name = "simplejson" }] pip = [{ name = "pip" }] pymongocrypt-source = [{ name = "pymongocrypt", git = "https://github.com/mongodb/libmongocrypt?subdirectory=bindings%2Fpython&rev=master" }] typing = [ - { name = "mypy", specifier = "==1.14.1" }, + { name = "mypy", specifier = "==1.17.1" }, { name = "pip" }, - { name = "pyright", specifier = "==1.1.392.post0" }, + { name = "pyright", specifier = "==1.1.403" }, { name = "typing-extensions" }, ] @@ -1375,15 +1391,15 @@ wheels = [ [[package]] name = "pyright" -version = "1.1.392.post0" +version = "1.1.403" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/df/3c6f6b08fba7ccf49b114dfc4bb33e25c299883fd763f93fad47ef8bc58d/pyright-1.1.392.post0.tar.gz", hash = "sha256:3b7f88de74a28dcfa90c7d90c782b6569a48c2be5f9d4add38472bdaac247ebd", size = 3789911, upload-time = "2025-01-15T15:01:20.913Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/f6/35f885264ff08c960b23d1542038d8da86971c5d8c955cfab195a4f672d7/pyright-1.1.403.tar.gz", hash = "sha256:3ab69b9f41c67fb5bbb4d7a36243256f0d549ed3608678d381d5f51863921104", size = 3913526, upload-time = "2025-07-09T07:15:52.882Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/b1/a18de17f40e4f61ca58856b9ef9b0febf74ff88978c3f7776f910071f567/pyright-1.1.392.post0-py3-none-any.whl", hash = "sha256:252f84458a46fa2f0fd4e2f91fc74f50b9ca52c757062e93f6c250c0d8329eb2", size = 5595487, upload-time = "2025-01-15T15:01:17.775Z" }, + { url = "https://files.pythonhosted.org/packages/49/b6/b04e5c2f41a5ccad74a1a4759da41adb20b4bc9d59a5e08d29ba60084d07/pyright-1.1.403-py3-none-any.whl", hash = "sha256:c0eeca5aa76cbef3fcc271259bbd785753c7ad7bcac99a9162b4c4c7daed23b3", size = 5684504, upload-time = "2025-07-09T07:15:50.958Z" }, ] [[package]] From 2622a7a101a729d571e5f81f5c06f2f690a8e189 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 7 Aug 2025 21:02:45 -0500 Subject: [PATCH 15/30] undo change to justfile --- justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/justfile b/justfile index 5519066d87..74ebb48823 100644 --- a/justfile +++ b/justfile @@ -2,7 +2,7 @@ set shell := ["bash", "-c"] # Commonly used command segments. -uv_run := "uv run --frozen " +uv_run := "uv run --isolated --frozen " typing_run := uv_run + "--group typing --extra aws --extra encryption --extra ocsp --extra snappy --extra test --extra zstd" docs_run := uv_run + "--extra docs" doc_build := "./doc/_build" From 73b430915e3013702dd95081cac7ed0e78ebe2ad Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 7 Aug 2025 21:09:48 -0500 Subject: [PATCH 16/30] remove unused code --- pymongo/asynchronous/encryption.py | 5 ----- pymongo/synchronous/encryption.py | 5 ----- 2 files changed, 10 deletions(-) diff --git a/pymongo/asynchronous/encryption.py b/pymongo/asynchronous/encryption.py index 2d3e702a79..5263d55b98 100644 --- a/pymongo/asynchronous/encryption.py +++ b/pymongo/asynchronous/encryption.py @@ -202,11 +202,6 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None: while kms_context.bytes_needed > 0: # CSOT: update timeout. conn.set_conn_timeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) - # if _IS_SYNC: - # # TODO: why can't we use receive_kms? - # data = conn.conn.sock.recv(kms_context.bytes_needed) - # else: - # data = await async_receive_kms(conn, kms_context.bytes_needed) data = await async_receive_kms(conn, kms_context.bytes_needed) if not data: raise OSError("KMS connection closed") diff --git a/pymongo/synchronous/encryption.py b/pymongo/synchronous/encryption.py index 2da3cd6669..d1709768de 100644 --- a/pymongo/synchronous/encryption.py +++ b/pymongo/synchronous/encryption.py @@ -201,11 +201,6 @@ def kms_request(self, kms_context: MongoCryptKmsContext) -> None: while kms_context.bytes_needed > 0: # CSOT: update timeout. conn.set_conn_timeout(max(_csot.clamp_remaining(_KMS_CONNECT_TIMEOUT), 0)) - # if _IS_SYNC: - # # TODO: why can't we use receive_kms? - # data = conn.conn.sock.recv(kms_context.bytes_needed) - # else: - # data = receive_kms(conn, kms_context.bytes_needed) data = receive_kms(conn, kms_context.bytes_needed) if not data: raise OSError("KMS connection closed") From 0cbdd58cc538db79f9c04d29eaeb26aa88570c73 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 7 Aug 2025 21:14:27 -0500 Subject: [PATCH 17/30] undo lock file changes --- uv.lock | 98 ++++++++++++++++++++++++--------------------------------- 1 file changed, 41 insertions(+), 57 deletions(-) diff --git a/uv.lock b/uv.lock index 3472ab3578..9c45c4cdb9 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.9" resolution-markers = [ "python_full_version == '3.14.*'", @@ -1047,53 +1047,46 @@ dependencies = [ [[package]] name = "mypy" -version = "1.17.1" +version = "1.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, - { name = "pathspec" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/a9/3d7aa83955617cdf02f94e50aab5c830d205cfa4320cf124ff64acce3a8e/mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972", size = 11003299, upload-time = "2025-07-31T07:54:06.425Z" }, - { url = "https://files.pythonhosted.org/packages/83/e8/72e62ff837dd5caaac2b4a5c07ce769c8e808a00a65e5d8f94ea9c6f20ab/mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7", size = 10125451, upload-time = "2025-07-31T07:53:52.974Z" }, - { url = "https://files.pythonhosted.org/packages/7d/10/f3f3543f6448db11881776f26a0ed079865926b0c841818ee22de2c6bbab/mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df", size = 11916211, upload-time = "2025-07-31T07:53:18.879Z" }, - { url = "https://files.pythonhosted.org/packages/06/bf/63e83ed551282d67bb3f7fea2cd5561b08d2bb6eb287c096539feb5ddbc5/mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390", size = 12652687, upload-time = "2025-07-31T07:53:30.544Z" }, - { url = "https://files.pythonhosted.org/packages/69/66/68f2eeef11facf597143e85b694a161868b3b006a5fbad50e09ea117ef24/mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94", size = 12896322, upload-time = "2025-07-31T07:53:50.74Z" }, - { url = "https://files.pythonhosted.org/packages/a3/87/8e3e9c2c8bd0d7e071a89c71be28ad088aaecbadf0454f46a540bda7bca6/mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b", size = 9507962, upload-time = "2025-07-31T07:53:08.431Z" }, - { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" }, - { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" }, - { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" }, - { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" }, - { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" }, - { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" }, - { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" }, - { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" }, - { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" }, - { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" }, - { url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" }, - { url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" }, - { url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" }, - { url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296, upload-time = "2025-07-31T07:53:03.896Z" }, - { url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657, upload-time = "2025-07-31T07:54:08.576Z" }, - { url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320, upload-time = "2025-07-31T07:53:01.341Z" }, - { url = "https://files.pythonhosted.org/packages/38/56/79c2fac86da57c7d8c48622a05873eaab40b905096c33597462713f5af90/mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733", size = 11040037, upload-time = "2025-07-31T07:54:10.942Z" }, - { url = "https://files.pythonhosted.org/packages/4d/c3/adabe6ff53638e3cad19e3547268482408323b1e68bf082c9119000cd049/mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd", size = 10131550, upload-time = "2025-07-31T07:53:41.307Z" }, - { url = "https://files.pythonhosted.org/packages/b8/c5/2e234c22c3bdeb23a7817af57a58865a39753bde52c74e2c661ee0cfc640/mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0", size = 11872963, upload-time = "2025-07-31T07:53:16.878Z" }, - { url = "https://files.pythonhosted.org/packages/ab/26/c13c130f35ca8caa5f2ceab68a247775648fdcd6c9a18f158825f2bc2410/mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a", size = 12710189, upload-time = "2025-07-31T07:54:01.962Z" }, - { url = "https://files.pythonhosted.org/packages/82/df/c7d79d09f6de8383fe800521d066d877e54d30b4fb94281c262be2df84ef/mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91", size = 12900322, upload-time = "2025-07-31T07:53:10.551Z" }, - { url = "https://files.pythonhosted.org/packages/b8/98/3d5a48978b4f708c55ae832619addc66d677f6dc59f3ebad71bae8285ca6/mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed", size = 9751879, upload-time = "2025-07-31T07:52:56.683Z" }, - { url = "https://files.pythonhosted.org/packages/29/cb/673e3d34e5d8de60b3a61f44f80150a738bff568cd6b7efb55742a605e98/mypy-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9", size = 10992466, upload-time = "2025-07-31T07:53:57.574Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d0/fe1895836eea3a33ab801561987a10569df92f2d3d4715abf2cfeaa29cb2/mypy-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99", size = 10117638, upload-time = "2025-07-31T07:53:34.256Z" }, - { url = "https://files.pythonhosted.org/packages/97/f3/514aa5532303aafb95b9ca400a31054a2bd9489de166558c2baaeea9c522/mypy-1.17.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8", size = 11915673, upload-time = "2025-07-31T07:52:59.361Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c3/c0805f0edec96fe8e2c048b03769a6291523d509be8ee7f56ae922fa3882/mypy-1.17.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8", size = 12649022, upload-time = "2025-07-31T07:53:45.92Z" }, - { url = "https://files.pythonhosted.org/packages/45/3e/d646b5a298ada21a8512fa7e5531f664535a495efa672601702398cea2b4/mypy-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259", size = 12895536, upload-time = "2025-07-31T07:53:06.17Z" }, - { url = "https://files.pythonhosted.org/packages/14/55/e13d0dcd276975927d1f4e9e2ec4fd409e199f01bdc671717e673cc63a22/mypy-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7598cf74c3e16539d4e2f0b8d8c318e00041553d83d4861f87c7a72e95ac24d", size = 9512564, upload-time = "2025-07-31T07:53:12.346Z" }, - { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051, upload-time = "2024-12-30T16:39:07.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/7a/87ae2adb31d68402da6da1e5f30c07ea6063e9f09b5e7cfc9dfa44075e74/mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb", size = 11211002, upload-time = "2024-12-30T16:37:22.435Z" }, + { url = "https://files.pythonhosted.org/packages/e1/23/eada4c38608b444618a132be0d199b280049ded278b24cbb9d3fc59658e4/mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0", size = 10358400, upload-time = "2024-12-30T16:37:53.526Z" }, + { url = "https://files.pythonhosted.org/packages/43/c9/d6785c6f66241c62fd2992b05057f404237deaad1566545e9f144ced07f5/mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d", size = 12095172, upload-time = "2024-12-30T16:37:50.332Z" }, + { url = "https://files.pythonhosted.org/packages/c3/62/daa7e787770c83c52ce2aaf1a111eae5893de9e004743f51bfcad9e487ec/mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b", size = 12828732, upload-time = "2024-12-30T16:37:29.96Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a2/5fb18318a3637f29f16f4e41340b795da14f4751ef4f51c99ff39ab62e52/mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427", size = 13012197, upload-time = "2024-12-30T16:38:05.037Z" }, + { url = "https://files.pythonhosted.org/packages/28/99/e153ce39105d164b5f02c06c35c7ba958aaff50a2babba7d080988b03fe7/mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f", size = 9780836, upload-time = "2024-12-30T16:37:19.726Z" }, + { url = "https://files.pythonhosted.org/packages/da/11/a9422850fd506edbcdc7f6090682ecceaf1f87b9dd847f9df79942da8506/mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", size = 11120432, upload-time = "2024-12-30T16:37:11.533Z" }, + { url = "https://files.pythonhosted.org/packages/b6/9e/47e450fd39078d9c02d620545b2cb37993a8a8bdf7db3652ace2f80521ca/mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", size = 10279515, upload-time = "2024-12-30T16:37:40.724Z" }, + { url = "https://files.pythonhosted.org/packages/01/b5/6c8d33bd0f851a7692a8bfe4ee75eb82b6983a3cf39e5e32a5d2a723f0c1/mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", size = 12025791, upload-time = "2024-12-30T16:36:58.73Z" }, + { url = "https://files.pythonhosted.org/packages/f0/4c/e10e2c46ea37cab5c471d0ddaaa9a434dc1d28650078ac1b56c2d7b9b2e4/mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", size = 12749203, upload-time = "2024-12-30T16:37:03.741Z" }, + { url = "https://files.pythonhosted.org/packages/88/55/beacb0c69beab2153a0f57671ec07861d27d735a0faff135a494cd4f5020/mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", size = 12885900, upload-time = "2024-12-30T16:37:57.948Z" }, + { url = "https://files.pythonhosted.org/packages/a2/75/8c93ff7f315c4d086a2dfcde02f713004357d70a163eddb6c56a6a5eff40/mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", size = 9777869, upload-time = "2024-12-30T16:37:33.428Z" }, + { url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668, upload-time = "2024-12-30T16:38:02.211Z" }, + { url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060, upload-time = "2024-12-30T16:37:46.131Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167, upload-time = "2024-12-30T16:37:43.534Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341, upload-time = "2024-12-30T16:37:36.249Z" }, + { url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991, upload-time = "2024-12-30T16:37:06.743Z" }, + { url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016, upload-time = "2024-12-30T16:37:15.02Z" }, + { url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097, upload-time = "2024-12-30T16:37:25.144Z" }, + { url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728, upload-time = "2024-12-30T16:38:08.634Z" }, + { url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965, upload-time = "2024-12-30T16:38:12.132Z" }, + { url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660, upload-time = "2024-12-30T16:38:17.342Z" }, + { url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198, upload-time = "2024-12-30T16:38:32.839Z" }, + { url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276, upload-time = "2024-12-30T16:38:20.828Z" }, + { url = "https://files.pythonhosted.org/packages/ca/1f/186d133ae2514633f8558e78cd658070ba686c0e9275c5a5c24a1e1f0d67/mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35", size = 11200493, upload-time = "2024-12-30T16:38:26.935Z" }, + { url = "https://files.pythonhosted.org/packages/af/fc/4842485d034e38a4646cccd1369f6b1ccd7bc86989c52770d75d719a9941/mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc", size = 10357702, upload-time = "2024-12-30T16:38:50.623Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e6/457b83f2d701e23869cfec013a48a12638f75b9d37612a9ddf99072c1051/mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9", size = 12091104, upload-time = "2024-12-30T16:38:53.735Z" }, + { url = "https://files.pythonhosted.org/packages/f1/bf/76a569158db678fee59f4fd30b8e7a0d75bcbaeef49edd882a0d63af6d66/mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb", size = 12830167, upload-time = "2024-12-30T16:38:56.437Z" }, + { url = "https://files.pythonhosted.org/packages/43/bc/0bc6b694b3103de9fed61867f1c8bd33336b913d16831431e7cb48ef1c92/mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60", size = 13013834, upload-time = "2024-12-30T16:38:59.204Z" }, + { url = "https://files.pythonhosted.org/packages/b0/79/5f5ec47849b6df1e6943d5fd8e6632fbfc04b4fd4acfa5a5a9535d11b4e2/mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c", size = 9781231, upload-time = "2024-12-30T16:39:05.124Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905, upload-time = "2024-12-30T16:38:42.021Z" }, ] [[package]] @@ -1123,15 +1116,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, -] - [[package]] name = "pip" version = "25.2" @@ -1331,7 +1315,7 @@ provides-extras = ["aws", "docs", "encryption", "gssapi", "ocsp", "snappy", "tes [package.metadata.requires-dev] coverage = [ - { name = "coverage", specifier = ">=5,<=7.10.2" }, + { name = "coverage", specifier = ">=5,<=7.5" }, { name = "pytest-cov" }, ] dev = [{ name = "pre-commit", specifier = ">=4.0" }] @@ -1345,9 +1329,9 @@ perf = [{ name = "simplejson" }] pip = [{ name = "pip" }] pymongocrypt-source = [{ name = "pymongocrypt", git = "https://github.com/mongodb/libmongocrypt?subdirectory=bindings%2Fpython&rev=master" }] typing = [ - { name = "mypy", specifier = "==1.17.1" }, + { name = "mypy", specifier = "==1.14.1" }, { name = "pip" }, - { name = "pyright", specifier = "==1.1.403" }, + { name = "pyright", specifier = "==1.1.392.post0" }, { name = "typing-extensions" }, ] @@ -1391,15 +1375,15 @@ wheels = [ [[package]] name = "pyright" -version = "1.1.403" +version = "1.1.392.post0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fe/f6/35f885264ff08c960b23d1542038d8da86971c5d8c955cfab195a4f672d7/pyright-1.1.403.tar.gz", hash = "sha256:3ab69b9f41c67fb5bbb4d7a36243256f0d549ed3608678d381d5f51863921104", size = 3913526, upload-time = "2025-07-09T07:15:52.882Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/df/3c6f6b08fba7ccf49b114dfc4bb33e25c299883fd763f93fad47ef8bc58d/pyright-1.1.392.post0.tar.gz", hash = "sha256:3b7f88de74a28dcfa90c7d90c782b6569a48c2be5f9d4add38472bdaac247ebd", size = 3789911, upload-time = "2025-01-15T15:01:20.913Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/b6/b04e5c2f41a5ccad74a1a4759da41adb20b4bc9d59a5e08d29ba60084d07/pyright-1.1.403-py3-none-any.whl", hash = "sha256:c0eeca5aa76cbef3fcc271259bbd785753c7ad7bcac99a9162b4c4c7daed23b3", size = 5684504, upload-time = "2025-07-09T07:15:50.958Z" }, + { url = "https://files.pythonhosted.org/packages/e7/b1/a18de17f40e4f61ca58856b9ef9b0febf74ff88978c3f7776f910071f567/pyright-1.1.392.post0-py3-none-any.whl", hash = "sha256:252f84458a46fa2f0fd4e2f91fc74f50b9ca52c757062e93f6c250c0d8329eb2", size = 5595487, upload-time = "2025-01-15T15:01:17.775Z" }, ] [[package]] From 6fe6ba34b326c1822203f87a1b4b8ee8153e7e4e Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 8 Aug 2025 13:12:06 -0500 Subject: [PATCH 18/30] use det branch --- .evergreen/scripts/configure-env.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index 8dc328aab3..c19c68cd56 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -76,6 +76,8 @@ EOT rm -rf $DRIVERS_TOOLS BRANCH=master ORG=mongodb-labs +BRANCH=fix-mock-kms-header +ORG=blink1073 git clone --branch $BRANCH https://github.com/$ORG/drivers-evergreen-tools.git $DRIVERS_TOOLS cat < ${DRIVERS_TOOLS}/.env From 6ed92bbf4e0039e5196e7d64908af9f06c459236 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 8 Aug 2025 13:15:30 -0500 Subject: [PATCH 19/30] fix branch name --- .evergreen/scripts/configure-env.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index c19c68cd56..e83b518268 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -76,7 +76,7 @@ EOT rm -rf $DRIVERS_TOOLS BRANCH=master ORG=mongodb-labs -BRANCH=fix-mock-kms-header +BRANCH=fix-mock-kms-headers ORG=blink1073 git clone --branch $BRANCH https://github.com/$ORG/drivers-evergreen-tools.git $DRIVERS_TOOLS From 971139cab4dba0e9ab2dfa9c5bf7f7b88c1b80c1 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 8 Aug 2025 15:59:16 -0500 Subject: [PATCH 20/30] fix buffer handling and close handling --- pymongo/network_layer.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 345ebc692a..b50233c5de 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -481,7 +481,7 @@ def _resolve_pending(self, exc: Optional[Exception] = None) -> None: class KMSBuffer: buffer: memoryview start_index: int - length: int + end_index: int class PyMongoKMSProtocol(PyMongoBaseProtocol): @@ -524,11 +524,21 @@ def get_buffer(self, sizehint: int) -> memoryview: If any data does not fit into the returned buffer, this method will be called again until either no data remains or an empty buffer is returned. """ - sizehint = max(sizehint, 1024) - buffer = KMSBuffer(memoryview(bytearray(sizehint)), 0, 0) + # Reuse the active buffer if it has space. + if len(self._buffers): + buffer = self._buffers[-1] + if len(buffer.buffer) - buffer.end_index > sizehint: + return buffer.buffer[buffer.end_index :] + # Allocate a bit more than the max response size for an AWS KMS response. + buffer = KMSBuffer(memoryview(bytearray(16384)), 0, 0) self._buffers.append(buffer) return buffer.buffer + def _resolve_pending(self, exc: Optional[Exception] = None) -> None: + while self._pending_listeners: + fut = self._pending_listeners.popleft() + fut.set_result(b"") + def buffer_updated(self, nbytes: int) -> None: """Called when the buffer was updated with the received data""" # Wrote 0 bytes into a non-empty buffer, signal connection closed @@ -540,9 +550,7 @@ def buffer_updated(self, nbytes: int) -> None: self._bytes_ready += nbytes # Update the length of the current buffer. - current_buffer = self._buffers.pop() - current_buffer.length += nbytes - self._buffers.append(current_buffer) + self._buffers[-1].end_index += nbytes if not len(self._pending_reads): return @@ -564,7 +572,7 @@ def _read(self, bytes_needed: int) -> memoryview: out_index = 0 while n_remaining > 0: buffer = self._buffers.popleft() - buffer_remaining = buffer.length - buffer.start_index + buffer_remaining = buffer.end_index - buffer.start_index # if we didn't exhaust the buffer, read the partial data and return the buffer. if buffer_remaining > n_remaining: output_buf[out_index : n_remaining + out_index] = buffer.buffer[ @@ -576,10 +584,14 @@ def _read(self, bytes_needed: int) -> memoryview: # otherwise exhaust the buffer. else: output_buf[out_index : out_index + buffer_remaining] = buffer.buffer[ - buffer.start_index : buffer.length + buffer.start_index : buffer.end_index ] out_index += buffer_remaining n_remaining -= buffer_remaining + # if this is the only buffer, add it back to the queue. + if not len(self._buffers): + buffer.start_index = buffer.end_index + self._buffers.appendleft(buffer) return memoryview(output_buf) From db4332d68c1485a0bd18dd181c3bdb3fce5d0015 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 8 Aug 2025 16:46:56 -0500 Subject: [PATCH 21/30] fix close conn behavior --- pymongo/asynchronous/pool.py | 4 +++- pymongo/synchronous/pool.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pymongo/asynchronous/pool.py b/pymongo/asynchronous/pool.py index 0aba99bed8..a57bd98451 100644 --- a/pymongo/asynchronous/pool.py +++ b/pymongo/asynchronous/pool.py @@ -594,7 +594,9 @@ def validate_session( async def close_conn(self, reason: Optional[str]) -> None: """Close this connection with a reason.""" - await super().close_conn(reason) + if self.closed: + return + await self._close_conn() if reason: if self.enabled_for_cmap: assert self.listeners is not None diff --git a/pymongo/synchronous/pool.py b/pymongo/synchronous/pool.py index b58d38209b..434c70d288 100644 --- a/pymongo/synchronous/pool.py +++ b/pymongo/synchronous/pool.py @@ -592,7 +592,9 @@ def validate_session( def close_conn(self, reason: Optional[str]) -> None: """Close this connection with a reason.""" - super().close_conn(reason) + if self.closed: + return + self._close_conn() if reason: if self.enabled_for_cmap: assert self.listeners is not None From c2f6ae8e7b1a75dc154402b8b8031d8b7c8a0309 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 8 Aug 2025 16:50:16 -0500 Subject: [PATCH 22/30] skip another test --- test/asynchronous/test_collection.py | 2 ++ test/test_collection.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/asynchronous/test_collection.py b/test/asynchronous/test_collection.py index cda8452d1c..6a85b63960 100644 --- a/test/asynchronous/test_collection.py +++ b/test/asynchronous/test_collection.py @@ -335,6 +335,8 @@ async def test_create_index(self): 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() diff --git a/test/test_collection.py b/test/test_collection.py index ccace72bec..0dce88423b 100644 --- a/test/test_collection.py +++ b/test/test_collection.py @@ -333,6 +333,8 @@ def test_create_index(self): 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() From d9e65b6b99931ff80162547ced2cac18edbca1e6 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 8 Aug 2025 16:35:35 -0500 Subject: [PATCH 23/30] skip tests --- test/asynchronous/unified_format.py | 2 ++ test/unified_format.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/asynchronous/unified_format.py b/test/asynchronous/unified_format.py index 964d2df96d..09bf7e83ea 100644 --- a/test/asynchronous/unified_format.py +++ b/test/asynchronous/unified_format.py @@ -564,6 +564,8 @@ def maybe_skip_test(self, spec): 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 diff --git a/test/unified_format.py b/test/unified_format.py index c21f29fe19..3496b2ad44 100644 --- a/test/unified_format.py +++ b/test/unified_format.py @@ -563,6 +563,8 @@ def maybe_skip_test(self, spec): 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 From 4546f23c3d73d645820a53799d11a75dce9cda44 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 11 Aug 2025 10:52:37 -0500 Subject: [PATCH 24/30] address review --- pymongo/network_layer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index b50233c5de..81dd3f6935 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -525,12 +525,13 @@ def get_buffer(self, sizehint: int) -> memoryview: either no data remains or an empty buffer is returned. """ # Reuse the active buffer if it has space. + # Allocate a bit more than the max response size for an AWS KMS response. + sizehint = max(sizehint, 16384) if len(self._buffers): buffer = self._buffers[-1] if len(buffer.buffer) - buffer.end_index > sizehint: return buffer.buffer[buffer.end_index :] - # Allocate a bit more than the max response size for an AWS KMS response. - buffer = KMSBuffer(memoryview(bytearray(16384)), 0, 0) + buffer = KMSBuffer(memoryview(bytearray(sizehint)), 0, 0) self._buffers.append(buffer) return buffer.buffer From 39b45263526663d3ee875edb04b35dd9b0582ae7 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 11 Aug 2025 10:53:02 -0500 Subject: [PATCH 25/30] use upstream d-e-t --- .evergreen/scripts/configure-env.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index e83b518268..8dc328aab3 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -76,8 +76,6 @@ EOT rm -rf $DRIVERS_TOOLS BRANCH=master ORG=mongodb-labs -BRANCH=fix-mock-kms-headers -ORG=blink1073 git clone --branch $BRANCH https://github.com/$ORG/drivers-evergreen-tools.git $DRIVERS_TOOLS cat < ${DRIVERS_TOOLS}/.env From da04fc8b4e31c6bfe02f72bd4cf1f8a97b602dc2 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 11 Aug 2025 12:37:30 -0500 Subject: [PATCH 26/30] fix waiting logic --- pymongo/network_layer.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 81dd3f6935..0fd274a61e 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -500,10 +500,11 @@ def connection_made(self, transport: BaseTransport) -> None: async def read(self, bytes_needed: int) -> bytes: """Read up to the requested bytes from this connection.""" + # Wait for other listeners first. + if len(self._pending_listeners): + await asyncio.gather(*self._pending_listeners) + # If there are bytes ready, then there is no need to wait further. if self._bytes_ready > 0: - # Wait for other listeners first. - if len(self._pending_listeners): - await asyncio.gather(*self._pending_listeners) return self._read(bytes_needed) if self.transport: try: From 28afc383ca31521d9c2e3193846122b2348ecf8f Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 11 Aug 2025 16:09:11 -0500 Subject: [PATCH 27/30] address review --- pymongo/network_layer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 0fd274a61e..6e4185adf7 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -500,6 +500,8 @@ def connection_made(self, transport: BaseTransport) -> None: async def read(self, bytes_needed: int) -> bytes: """Read up to the requested bytes from this connection.""" + # Note: all reads are "up-to" bytes_needed because we don't know if the kms_context + # has processed a Content-Length header and is requesting a response or not. # Wait for other listeners first. if len(self._pending_listeners): await asyncio.gather(*self._pending_listeners) From 8b357cd2df76087fe0c387e8e344b084870194d9 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 14 Aug 2025 20:04:07 -0500 Subject: [PATCH 28/30] use the base Protocol --- pymongo/network_layer.py | 97 +++++++++++++--------------------------- 1 file changed, 30 insertions(+), 67 deletions(-) diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 6e4185adf7..92535b93fc 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -22,8 +22,7 @@ import struct import sys import time -from asyncio import BaseTransport, BufferedProtocol, Future, Transport -from dataclasses import dataclass +from asyncio import BaseTransport, BufferedProtocol, Future, Protocol, Transport from typing import ( TYPE_CHECKING, Any, @@ -251,7 +250,7 @@ def recv_into(self, buffer: bytes) -> int: return self.conn.recv_into(buffer) -class PyMongoBaseProtocol(BufferedProtocol): +class PyMongoBaseProtocol(Protocol): def __init__(self, timeout: Optional[float] = None): self.transport: Transport = None # type: ignore[assignment] self._timeout = timeout @@ -293,7 +292,7 @@ async def read(self, *args: Any) -> Any: raise NotImplementedError -class PyMongoProtocol(PyMongoBaseProtocol): +class PyMongoProtocol(PyMongoBaseProtocol, BufferedProtocol): def __init__(self, timeout: Optional[float] = None): super().__init__(timeout) # Each message is reader in 2-3 parts: header, compression header, and message body @@ -477,17 +476,10 @@ def _resolve_pending(self, exc: Optional[Exception] = None) -> None: self._done_messages.append(msg) -@dataclass -class KMSBuffer: - buffer: memoryview - start_index: int - end_index: int - - class PyMongoKMSProtocol(PyMongoBaseProtocol): def __init__(self, timeout: Optional[float] = None): super().__init__(timeout) - self._buffers: collections.deque[KMSBuffer] = collections.deque() + self._buffers: collections.deque[memoryview[bytes]] = collections.deque() self._bytes_ready = 0 self._pending_reads: collections.deque[int] = collections.deque() self._pending_listeners: collections.deque[Future[Any]] = collections.deque() @@ -498,6 +490,24 @@ def connection_made(self, transport: BaseTransport) -> None: """ self.transport = transport # type: ignore[assignment] + def data_received(self, data: bytes) -> None: + if self._connection_lost: + return + + self._bytes_ready += len(data) + self._buffers.append(memoryview[data]) + + if not len(self._pending_reads): + return + + bytes_needed = self._pending_reads.popleft() + data = self._read(bytes_needed) + waiter = self._pending_listeners.popleft() + waiter.set_result(data) + + def eof_received(self): + self.close(OSError("connection closed")) + async def read(self, bytes_needed: int) -> bytes: """Read up to the requested bytes from this connection.""" # Note: all reads are "up-to" bytes_needed because we don't know if the kms_context @@ -521,51 +531,13 @@ async def read(self, bytes_needed: int) -> bytes: self._pending_listeners.append(read_waiter) return await read_waiter - def get_buffer(self, sizehint: int) -> memoryview: - """Called to allocate a new receive buffer. - The asyncio loop calls this method expecting to receive a non-empty buffer to fill with data. - If any data does not fit into the returned buffer, this method will be called again until - either no data remains or an empty buffer is returned. - """ - # Reuse the active buffer if it has space. - # Allocate a bit more than the max response size for an AWS KMS response. - sizehint = max(sizehint, 16384) - if len(self._buffers): - buffer = self._buffers[-1] - if len(buffer.buffer) - buffer.end_index > sizehint: - return buffer.buffer[buffer.end_index :] - buffer = KMSBuffer(memoryview(bytearray(sizehint)), 0, 0) - self._buffers.append(buffer) - return buffer.buffer - def _resolve_pending(self, exc: Optional[Exception] = None) -> None: while self._pending_listeners: fut = self._pending_listeners.popleft() fut.set_result(b"") - def buffer_updated(self, nbytes: int) -> None: - """Called when the buffer was updated with the received data""" - # Wrote 0 bytes into a non-empty buffer, signal connection closed - if nbytes == 0: - self.close(OSError("connection closed")) - return - if self._connection_lost: - return - self._bytes_ready += nbytes - - # Update the length of the current buffer. - self._buffers[-1].end_index += nbytes - - if not len(self._pending_reads): - return - - bytes_needed = self._pending_reads.popleft() - data = self._read(bytes_needed) - waiter = self._pending_listeners.popleft() - waiter.set_result(data) - def _read(self, bytes_needed: int) -> memoryview: - """Read bytes from the buffer.""" + """Read bytes.""" # Send the bytes to the listener. if self._bytes_ready < bytes_needed: bytes_needed = self._bytes_ready @@ -576,26 +548,17 @@ def _read(self, bytes_needed: int) -> memoryview: out_index = 0 while n_remaining > 0: buffer = self._buffers.popleft() - buffer_remaining = buffer.end_index - buffer.start_index + buf_size = len(buffer) # if we didn't exhaust the buffer, read the partial data and return the buffer. - if buffer_remaining > n_remaining: - output_buf[out_index : n_remaining + out_index] = buffer.buffer[ - buffer.start_index : buffer.start_index + n_remaining - ] - buffer.start_index += n_remaining + if buf_size > n_remaining: + output_buf[out_index : n_remaining + out_index] = buffer[:n_remaining] n_remaining = 0 - self._buffers.appendleft(buffer) + self._buffers.appendleft(buffer[n_remaining:]) # otherwise exhaust the buffer. else: - output_buf[out_index : out_index + buffer_remaining] = buffer.buffer[ - buffer.start_index : buffer.end_index - ] - out_index += buffer_remaining - n_remaining -= buffer_remaining - # if this is the only buffer, add it back to the queue. - if not len(self._buffers): - buffer.start_index = buffer.end_index - self._buffers.appendleft(buffer) + output_buf[out_index : out_index + buf_size] = buffer[:] + out_index += buf_size + n_remaining -= buf_size return memoryview(output_buf) From 070827852d5d07414c6967075080f524e3f3e0d7 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 15 Aug 2025 09:20:29 -0500 Subject: [PATCH 29/30] fixups --- pymongo/network_layer.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 92535b93fc..605b8dde9b 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -495,7 +495,7 @@ def data_received(self, data: bytes) -> None: return self._bytes_ready += len(data) - self._buffers.append(memoryview[data]) + self._buffers.append(memoryview(data)) if not len(self._pending_reads): return @@ -505,9 +505,6 @@ def data_received(self, data: bytes) -> None: waiter = self._pending_listeners.popleft() waiter.set_result(data) - def eof_received(self): - self.close(OSError("connection closed")) - async def read(self, bytes_needed: int) -> bytes: """Read up to the requested bytes from this connection.""" # Note: all reads are "up-to" bytes_needed because we don't know if the kms_context @@ -552,8 +549,9 @@ def _read(self, bytes_needed: int) -> memoryview: # if we didn't exhaust the buffer, read the partial data and return the buffer. if buf_size > n_remaining: output_buf[out_index : n_remaining + out_index] = buffer[:n_remaining] + buffer = buffer[n_remaining:] n_remaining = 0 - self._buffers.appendleft(buffer[n_remaining:]) + self._buffers.appendleft(buffer) # otherwise exhaust the buffer. else: output_buf[out_index : out_index + buf_size] = buffer[:] From b5e8c0e2be653fb8aad21c6356d2f442c8bb6180 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 18 Aug 2025 07:20:33 -0500 Subject: [PATCH 30/30] PYTHON-5500 Mark test_dns_failures_logging as flaky --- .evergreen/scripts/install-dependencies.sh | 2 ++ test/asynchronous/test_srv_polling.py | 1 + test/test_srv_polling.py | 1 + 3 files changed, 4 insertions(+) diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index 5425d10c8c..febaa77126 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -51,6 +51,8 @@ if ! command -v just &>/dev/null; then 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." diff --git a/test/asynchronous/test_srv_polling.py b/test/asynchronous/test_srv_polling.py index a89403b473..d6f0f6a18f 100644 --- a/test/asynchronous/test_srv_polling.py +++ b/test/asynchronous/test_srv_polling.py @@ -225,6 +225,7 @@ def response_callback(*args): await self.run_scenario(response_callback, False) + @flaky(reason="PYTHON-5500") async def test_dns_failures_logging(self): from dns import exception diff --git a/test/test_srv_polling.py b/test/test_srv_polling.py index 09579eda12..09c900cf09 100644 --- a/test/test_srv_polling.py +++ b/test/test_srv_polling.py @@ -225,6 +225,7 @@ def response_callback(*args): self.run_scenario(response_callback, False) + @flaky(reason="PYTHON-5500") def test_dns_failures_logging(self): from dns import exception