From ba19c549e4c2a5b608aedb8e186afaab3cf1fd25 Mon Sep 17 00:00:00 2001 From: Oleg Ovcharuk Date: Wed, 18 Mar 2026 15:53:48 +0300 Subject: [PATCH] Ability to pass additional info to sdk header --- ydb/_utilities.py | 6 ++++-- ydb/_utilities_test.py | 14 ++++++++++++++ ydb/aio/connection.py | 2 +- ydb/connection.py | 2 +- ydb/driver.py | 5 +++++ 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/ydb/_utilities.py b/ydb/_utilities.py index 46d8e382..2e04ba12 100644 --- a/ydb/_utilities.py +++ b/ydb/_utilities.py @@ -38,8 +38,10 @@ def future(): return futures.Future() -def x_ydb_sdk_build_info_header(): - return ("x-ydb-sdk-build-info", "ydb-python-sdk/" + ydb_version.VERSION) +def x_ydb_sdk_build_info_header(additional_sdk_headers): + sdk_header_list = ["ydb-python-sdk/" + ydb_version.VERSION] + sdk_header_list.extend(additional_sdk_headers) + return ("x-ydb-sdk-build-info", ";".join(sdk_header_list)) def is_secure_protocol(endpoint): diff --git a/ydb/_utilities_test.py b/ydb/_utilities_test.py index 653922e3..8199a53b 100644 --- a/ydb/_utilities_test.py +++ b/ydb/_utilities_test.py @@ -1,6 +1,8 @@ import pytest from ydb._utilities import check_module_exists +from ydb._utilities import x_ydb_sdk_build_info_header +from .ydb_version import VERSION @pytest.mark.parametrize( @@ -9,3 +11,15 @@ ) def test_check_module_exists(path, result): assert check_module_exists(path) == result + + +def test_x_ydb_sdk_build_info_header(): + assert x_ydb_sdk_build_info_header(()) == ("x-ydb-sdk-build-info", "ydb-python-sdk/" + VERSION) + assert x_ydb_sdk_build_info_header(("lib1/0.1.0",)) == ( + "x-ydb-sdk-build-info", + "ydb-python-sdk/" + VERSION + ";lib1/0.1.0", + ) + assert x_ydb_sdk_build_info_header(("lib1/0.1.0", "lib2/0.2.0")) == ( + "x-ydb-sdk-build-info", + "ydb-python-sdk/" + VERSION + ";lib1/0.1.0;lib2/0.2.0", + ) diff --git a/ydb/aio/connection.py b/ydb/aio/connection.py index 9a031ff1..9e03450d 100644 --- a/ydb/aio/connection.py +++ b/ydb/aio/connection.py @@ -70,7 +70,7 @@ async def _construct_metadata( if settings.request_type is not None: metadata.append((YDB_REQUEST_TYPE_HEADER, settings.request_type)) - metadata.append(_utilities.x_ydb_sdk_build_info_header()) + metadata.append(_utilities.x_ydb_sdk_build_info_header(getattr(driver_config, "_additional_sdk_headers", ()))) return metadata diff --git a/ydb/connection.py b/ydb/connection.py index fc05544b..85187f65 100644 --- a/ydb/connection.py +++ b/ydb/connection.py @@ -175,7 +175,7 @@ def _construct_metadata(driver_config, settings): metadata.append((YDB_REQUEST_TYPE_HEADER, settings.request_type)) metadata.extend(getattr(settings, "headers", [])) - metadata.append(_utilities.x_ydb_sdk_build_info_header()) + metadata.append(_utilities.x_ydb_sdk_build_info_header(getattr(driver_config, "_additional_sdk_headers", ()))) return metadata diff --git a/ydb/driver.py b/ydb/driver.py index e0603be1..555ca485 100644 --- a/ydb/driver.py +++ b/ydb/driver.py @@ -110,6 +110,7 @@ class DriverConfig(object): "discovery_request_timeout", "compression", "disable_discovery", + "_additional_sdk_headers", ) def __init__( @@ -135,6 +136,8 @@ def __init__( discovery_request_timeout: int = 10, compression: Optional[grpc.Compression] = None, disable_discovery: bool = False, + *, + _additional_sdk_headers: Tuple[str, ...] = (), ) -> None: """ A driver config to initialize a driver instance @@ -156,6 +159,7 @@ def __init__( :param grpc_lb_policy_name: A load balancing policy to be used for discovery channel construction. Default value is `round_round` :param discovery_request_timeout: A default timeout to complete the discovery. The default value is 10 seconds. :param disable_discovery: If True, endpoint discovery is disabled and only the start endpoint is used for all requests. + :param _additional_sdk_headers: Reserved for SDK integrations (e.g. dbapi, sqlalchemy). Do not use in application code. """ self.endpoint = endpoint @@ -184,6 +188,7 @@ def __init__( self.discovery_request_timeout = discovery_request_timeout self.compression = compression self.disable_discovery = disable_discovery + self._additional_sdk_headers = _additional_sdk_headers def set_database(self, database: str) -> "DriverConfig": self.database = database