@@ -124,6 +124,11 @@ def build_options(kwargs: dict[str, Any]) -> dict[str, Any]:
124124 options [Constants .Kwargs .READ_TIMEOUT ] = kwargs [Constants .Kwargs .READ_TIMEOUT ]
125125 if Constants .Kwargs .TIMEOUT in kwargs :
126126 options [Constants .Kwargs .TIMEOUT ] = kwargs [Constants .Kwargs .TIMEOUT ]
127+ # Copy (not pop) so connection_timeout stays in kwargs for the page fetch
128+ # and is also placed in options, where the container read, partition-key
129+ # ranges, and query plan calls read it.
130+ if Constants .Kwargs .CONNECTION_TIMEOUT in kwargs :
131+ options [Constants .Kwargs .CONNECTION_TIMEOUT ] = kwargs [Constants .Kwargs .CONNECTION_TIMEOUT ]
127132
128133
129134 options [Constants .OperationStartTime ] = time .time ()
@@ -1082,6 +1087,70 @@ def _build_properties_cache(properties: dict[str, Any], container_link: str) ->
10821087 "partitionKey" : properties .get ("partitionKey" , None ), "container_link" : container_link
10831088 }
10841089
1090+ # The per-call timeout keys a caller can set on a single request. Listed once
1091+ # here so format_pk_range_options and the hybrid-search fetch forward the same
1092+ # set.
1093+ _PER_CALL_TIMEOUT_OPTION_KEYS : Tuple [str , ...] = (
1094+ Constants .Kwargs .READ_TIMEOUT ,
1095+ Constants .Kwargs .CONNECTION_TIMEOUT ,
1096+ Constants .Kwargs .TIMEOUT ,
1097+ )
1098+
1099+ # The operation deadline is checked as elapsed = now - OperationStartTime, and
1100+ # OperationStartTime defaults to the current time when it is missing. So timeout
1101+ # and OperationStartTime must be carried together onto the metadata setup calls;
1102+ # otherwise a setup call measures the deadline from its own start instead of the
1103+ # operation's start. This adds OperationStartTime to the three timeout keys above.
1104+ _PER_CALL_DEADLINE_OPTION_KEYS : Tuple [str , ...] = _PER_CALL_TIMEOUT_OPTION_KEYS + (
1105+ Constants .OperationStartTime ,
1106+ )
1107+
1108+
1109+ def _carry_per_call_timeout_options (source : Mapping [str , Any ], destination : dict [str , Any ]) -> None :
1110+ """Copy the per-call timeouts and the operation start time from source into destination.
1111+
1112+ Copies read_timeout, connection_timeout, timeout, and OperationStartTime. Only
1113+ keys present in source are copied, so a timeout the caller did not set stays
1114+ absent and the request uses the client default instead of None.
1115+
1116+ :param source: The request options to read the timeouts from.
1117+ :type source: ~collections.abc.Mapping[str, typing.Any]
1118+ :param destination: The options dict to copy the timeouts into.
1119+ :type destination: dict[str, typing.Any]
1120+ :return: None
1121+ :rtype: None
1122+ """
1123+ for key in _PER_CALL_DEADLINE_OPTION_KEYS :
1124+ if key in source :
1125+ destination [key ] = source [key ]
1126+
1127+
1128+ def _copy_per_call_timeouts_to_kwargs (
1129+ options : Optional [Mapping [str , Any ]],
1130+ kwargs : dict [str , Any ]
1131+ ) -> None :
1132+ """Copy the per-call timeouts and the operation start time from options into kwargs.
1133+
1134+ Moves read_timeout, connection_timeout, timeout, and OperationStartTime from
1135+ the request options into the kwargs the request layer reads. A value is copied
1136+ only when it is set (not None), so an unset timeout falls back to the client
1137+ default instead of None; setdefault keeps any value already in kwargs.
1138+
1139+ :param options: The request options to read the timeouts from (may be None or empty).
1140+ :type options: ~collections.abc.Mapping[str, typing.Any] or None
1141+ :param kwargs: The kwargs dict to copy the timeouts into; mutated in place.
1142+ :type kwargs: dict[str, typing.Any]
1143+ :return: None
1144+ :rtype: None
1145+ """
1146+ if not options :
1147+ return
1148+ for key in _PER_CALL_DEADLINE_OPTION_KEYS :
1149+ value = options .get (key )
1150+ if value is not None :
1151+ kwargs .setdefault (key , value )
1152+
1153+
10851154def format_pk_range_options (query_options : Mapping [str , Any ]) -> dict [str , Any ]:
10861155 """Formats the partition key range options to be used internally from the query ones.
10871156 :param dict query_options: The query options being used.
@@ -1094,4 +1163,7 @@ def format_pk_range_options(query_options: Mapping[str, Any]) -> dict[str, Any]:
10941163 pk_range_options [Constants .ContainerRID ] = query_options [Constants .ContainerRID ]
10951164 if "excludedLocations" in query_options :
10961165 pk_range_options ["excludedLocations" ] = query_options ["excludedLocations" ]
1166+ # Keep the per-call timeouts so the partition-key ranges fetch uses them
1167+ # instead of the client default.
1168+ _carry_per_call_timeout_options (query_options , pk_range_options )
10971169 return pk_range_options
0 commit comments