From 7d89bdd229d4d151cfa5b6f1b0da9e74f7d77a34 Mon Sep 17 00:00:00 2001 From: Krasnov Dmitry Date: Tue, 25 Aug 2026 11:07:03 +0300 Subject: [PATCH] Add return_not_null_data_as_optional param to read_table The gRPC ReadTableRequest already has a return_not_null_data_as_optional field and the C++ SDK exposes it as ReturnNotNullAsOptional, but the Python SDK never set it, so read_table always wrapped NOT NULL columns in Optional with no way to opt out. Plumb the flag through read_table (sync + async) and read_table_request_factory. Default behavior is unchanged (still Optional). --- CHANGELOG.md | 1 + ydb/_session_impl.py | 9 +++++++++ ydb/aio/table.py | 2 ++ ydb/table.py | 5 +++++ 4 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 061c2fe70..ce888f88b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +* Add `return_not_null_data_as_optional` parameter to `read_table` (sync and async) — allows reading `NOT NULL` columns as non-optional types, matching the existing gRPC field and the C++ SDK's `ReturnNotNullAsOptional`. By default `read_table` still wraps `NOT NULL` columns in `Optional` for backward compatibility * Introduce `ydb.observability` — vendor-neutral tracing entrypoint with a `TracingProvider` interface; `enable_tracing(provider)` accepts any implementation (custom or OpenTelemetry) and replaces the previously installed one. The SDK core no longer imports `opentelemetry`, so tracing can be enabled without the OpenTelemetry packages by supplying a custom provider * When tracing is enabled the SDK appends a `ydb-sdk-tracing/0.1.0` token to the `x-ydb-sdk-build-info` header, so the server can distinguish requests from tracing-enabled clients diff --git a/ydb/_session_impl.py b/ydb/_session_impl.py index 4fcd23314..1cb3b6b05 100644 --- a/ydb/_session_impl.py +++ b/ydb/_session_impl.py @@ -332,6 +332,7 @@ def read_table_request_factory( ordered=False, row_limit=None, use_snapshot=None, + return_not_null_data_as_optional=None, ): request = _apis.ydb_table.ReadTableRequest() request.path = path @@ -362,6 +363,14 @@ def read_table_request_factory( request.use_snapshot = _apis.FeatureFlag.DISABLED else: request.use_snapshot = use_snapshot + if return_not_null_data_as_optional is not None: + if isinstance(return_not_null_data_as_optional, bool): + if return_not_null_data_as_optional: + request.return_not_null_data_as_optional = _apis.FeatureFlag.ENABLED + else: + request.return_not_null_data_as_optional = _apis.FeatureFlag.DISABLED + else: + request.return_not_null_data_as_optional = return_not_null_data_as_optional return session_state.attach_request(request) diff --git a/ydb/aio/table.py b/ydb/aio/table.py index 8d5e02c18..3a066c5ef 100644 --- a/ydb/aio/table.py +++ b/ydb/aio/table.py @@ -44,6 +44,7 @@ async def read_table( row_limit=None, settings=None, use_snapshot=None, + return_not_null_data_as_optional=None, ): # pylint: disable=W0236 request = _session_impl.read_table_request_factory( self._state, @@ -53,6 +54,7 @@ async def read_table( ordered, row_limit, use_snapshot=use_snapshot, + return_not_null_data_as_optional=return_not_null_data_as_optional, ) stream_it = await self._driver( request, diff --git a/ydb/table.py b/ydb/table.py index fb7fadc42..123f3a9cc 100644 --- a/ydb/table.py +++ b/ydb/table.py @@ -1053,6 +1053,7 @@ def read_table( row_limit=None, settings=None, use_snapshot=None, + return_not_null_data_as_optional=None, ): """ Perform an read table request. @@ -1715,6 +1716,7 @@ def read_table( row_limit=None, settings=None, use_snapshot=None, + return_not_null_data_as_optional=None, ): """ Perform an read table request. @@ -1737,6 +1739,7 @@ def read_table( ordered, row_limit, use_snapshot=use_snapshot, + return_not_null_data_as_optional=return_not_null_data_as_optional, ) stream_it = self._driver( request, @@ -1975,6 +1978,7 @@ def async_read_table( row_limit=None, settings=None, use_snapshot=None, + return_not_null_data_as_optional=None, ): """ Perform an read table request. @@ -1999,6 +2003,7 @@ def async_read_table( ordered, row_limit, use_snapshot=use_snapshot, + return_not_null_data_as_optional=return_not_null_data_as_optional, ) stream_it = self._driver( request,