From 228f4ba56e7847839765ca1fce7e3376040f0b3d Mon Sep 17 00:00:00 2001 From: Harsh23Kashyap <55448981+Harsh23Kashyap@users.noreply.github.com> Date: Sat, 22 Aug 2026 02:15:22 +0530 Subject: [PATCH 1/2] fix(http): ApiClient.request_sync must not wrap sync result in event loop The sync ApiClient.request_sync shared the same body as the async AsyncApiClient.request_sync: it called get_event_loop().run_until_complete on self.request(...). For the async client, self.request returns a coroutine, so the event-loop bridge is correct. For the sync client, self.request returns an ordinary value, so run_until_complete raised TypeError: An asyncio.Future, a coroutine or an awaitable is required on every successful response. The two methods live in the same file and were clearly meant to differ in exactly this respect. The async side stays unchanged; the sync side now just returns self.request(...) directly. Adds a unit regression that mocks request with a plain dict and asserts the dict is returned, plus a type_=None path check. No live server required. Fixes #1336 --- qdrant_client/http/api_client.py | 2 +- tests/test_qdrant_client.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/qdrant_client/http/api_client.py b/qdrant_client/http/api_client.py index 4c907c3cf..6cfafb2a3 100644 --- a/qdrant_client/http/api_client.py +++ b/qdrant_client/http/api_client.py @@ -109,7 +109,7 @@ def request_sync(self, *, type_: Any, **kwargs: Any) -> Any: # noqa F811 """ This method is not used by the generated apis, but is included for convenience """ - return get_event_loop().run_until_complete(self.request(type_=type_, **kwargs)) + return self.request(type_=type_, **kwargs) def send(self, request: Request, type_: Type[T]) -> T: response = self.middleware(request, self.send_inner) diff --git a/tests/test_qdrant_client.py b/tests/test_qdrant_client.py index db7651203..c215b3697 100644 --- a/tests/test_qdrant_client.py +++ b/tests/test_qdrant_client.py @@ -8,6 +8,7 @@ from pprint import pprint from tempfile import mkdtemp from time import sleep +from unittest.mock import patch import numpy as np import pytest @@ -1757,6 +1758,35 @@ def test_timeout_propagation(): ) +def test_api_client_request_sync_returns_value(): + # Regression for #1336: ApiClient.request_sync wrapped a synchronous + # return value in run_until_complete, so any non-awaitable result + # raised TypeError instead of being returned. + from qdrant_client.http.api_client import ApiClient + + sync_client = ApiClient("http://localhost:6333") + with patch.object(sync_client, "request", return_value={"ok": True}) as mocked: + result = sync_client.request_sync( + type_=dict, + method="GET", + url="/collections", + ) + + assert result == {"ok": True} + mocked.assert_called_once_with(type_=dict, method="GET", url="/collections") + + # type_=None path also returns the value as-is. + with patch.object(sync_client, "request", return_value=None) as mocked_none: + result_none = sync_client.request_sync( + type_=None, + method="GET", + url="/collections", + ) + + assert result_none is None + mocked_none.assert_called_once_with(type_=None, method="GET", url="/collections") + + def test_grpc_options(): client_version = importlib.metadata.version("qdrant-client") user_agent = f"python-client/{client_version}" From b94e9231b1a0d582a2fe59204c5b5b42214a0f84 Mon Sep 17 00:00:00 2001 From: Harsh23Kashyap <55448981+Harsh23Kashyap@users.noreply.github.com> Date: Sat, 22 Aug 2026 02:19:05 +0530 Subject: [PATCH 2/2] test(http): trim test comment to the bug, not the diagnosis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 3-line regression comment for #1336 repeated the bug description that the linked issue already has. Replaced with a 2-line version that points to the bug number and the operation that was wrong. Also dropped the inline comment on the type_=None test block — the block's patch and assertion are self-evident. --- tests/test_qdrant_client.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_qdrant_client.py b/tests/test_qdrant_client.py index c215b3697..b882cc0e4 100644 --- a/tests/test_qdrant_client.py +++ b/tests/test_qdrant_client.py @@ -1759,9 +1759,8 @@ def test_timeout_propagation(): def test_api_client_request_sync_returns_value(): - # Regression for #1336: ApiClient.request_sync wrapped a synchronous - # return value in run_until_complete, so any non-awaitable result - # raised TypeError instead of being returned. + # Regression for #1336: sync client wrapped the return value in + # run_until_complete, so any non-awaitable result raised TypeError. from qdrant_client.http.api_client import ApiClient sync_client = ApiClient("http://localhost:6333") @@ -1775,7 +1774,6 @@ def test_api_client_request_sync_returns_value(): assert result == {"ok": True} mocked.assert_called_once_with(type_=dict, method="GET", url="/collections") - # type_=None path also returns the value as-is. with patch.object(sync_client, "request", return_value=None) as mocked_none: result_none = sync_client.request_sync( type_=None,