From d80d6ae01cf2e2fe10621f9ed954a6703ae8a082 Mon Sep 17 00:00:00 2001 From: Subrata Paitandi Date: Wed, 23 Sep 2026 12:13:31 +0000 Subject: [PATCH 1/2] REFACTOR: Mark preview async entry-point classes as internal --- mssql_python/async_query/__init__.py | 9 ++++--- mssql_python/async_query/async_connection.py | 14 +++++----- mssql_python/async_query/async_cursor.py | 4 +-- mssql_python/async_query/async_execute.py | 12 ++++----- mssql_python/async_query/async_fetch.py | 14 +++++----- tests/AsyncTest/conftest.py | 4 +-- .../AsyncTest/test_001_async_query_native.py | 13 ++++++++++ tests/AsyncTest/test_002_async_connection.py | 23 ++++++++-------- tests/AsyncTest/test_003_async_exceptions.py | 10 +++---- tests/AsyncTest/test_004_async_logging.py | 8 +++--- tests/AsyncTest/test_006_async_execute.py | 18 +++++++------ tests/AsyncTest/test_007_async_fetch.py | 26 +++++++++---------- 12 files changed, 86 insertions(+), 69 deletions(-) diff --git a/mssql_python/async_query/__init__.py b/mssql_python/async_query/__init__.py index f3937a136..95f8f0ecf 100644 --- a/mssql_python/async_query/__init__.py +++ b/mssql_python/async_query/__init__.py @@ -1,5 +1,8 @@ """Asynchronous query support backed directly by mssql-py-core. +The internal entry points are ``_AsyncConnection`` and ``_AsyncCursor``. +Their methods retain DB-API names, but the classes are not stable public API. + Warning: Async query execution APIs are under active development and are not intended for production use. Their signatures, behavior, error handling, and compatibility @@ -7,8 +10,8 @@ """ from ._native import load_py_core -from .async_connection import AsyncConnection -from .async_cursor import AsyncCursor +from .async_connection import _AsyncConnection # pyright: ignore[reportPrivateUsage] +from .async_cursor import _AsyncCursor # pyright: ignore[reportPrivateUsage] from .exception_translator import ( DataError, DatabaseError, @@ -23,8 +26,6 @@ ) __all__ = [ - "AsyncConnection", - "AsyncCursor", "DataError", "DatabaseError", "Error", diff --git a/mssql_python/async_query/async_connection.py b/mssql_python/async_query/async_connection.py index 8f589285a..865d172b8 100644 --- a/mssql_python/async_query/async_connection.py +++ b/mssql_python/async_query/async_connection.py @@ -11,7 +11,7 @@ from ..logging import logger from ._native import load_py_core from ._connection_context import build_async_connection_context -from .async_cursor import AsyncCursor +from .async_cursor import _AsyncCursor # pyright: ignore[reportPrivateUsage] from .exception_translator import ( DataError, DatabaseError, @@ -27,7 +27,7 @@ ) -class AsyncConnection: +class _AsyncConnection: """Thin Python wrapper over ``mssql_py_core.PyAsyncConnection``. Warning: @@ -56,7 +56,7 @@ async def connect( autocommit: bool = False, timeout: int = 0, python_logger: Optional[Any] = None, - ) -> "AsyncConnection": + ) -> "_AsyncConnection": """Establish an asynchronous connection from an ODBC connection string.""" logger_bridge = python_logger if logger_bridge is None and logger.is_debug_enabled: @@ -81,12 +81,12 @@ async def connect( logger.debug("AsyncConnection.connect: connected") return cls(py_core_async_connection) - def cursor(self) -> AsyncCursor: + def cursor(self) -> _AsyncCursor: """Create a public asynchronous cursor sharing this connection.""" with translate_py_core_exceptions(): py_core_async_cursor = self._py_core_async_connection.cursor() logger.debug("AsyncConnection.cursor: cursor created") - return AsyncCursor(py_core_async_cursor, self) + return _AsyncCursor(py_core_async_cursor, self) async def commit(self) -> None: """Commit the active transaction, if any.""" @@ -109,7 +109,7 @@ async def close(self) -> None: await self._py_core_async_connection.close() logger.debug("AsyncConnection.close: completed") - async def __aenter__(self) -> "AsyncConnection": + async def __aenter__(self) -> "_AsyncConnection": logger.debug("AsyncConnection.__aenter__: entering context") with translate_py_core_exceptions(): await self._py_core_async_connection.__aenter__() @@ -157,4 +157,4 @@ def is_connected(self) -> bool: def __repr__(self) -> str: state = "closed" if self.closed else "connected" - return f"AsyncConnection({state})" + return f"_AsyncConnection({state})" diff --git a/mssql_python/async_query/async_cursor.py b/mssql_python/async_query/async_cursor.py index e574e7d94..dd340af8c 100644 --- a/mssql_python/async_query/async_cursor.py +++ b/mssql_python/async_query/async_cursor.py @@ -20,7 +20,7 @@ from .exception_translator import translate_py_core_exceptions -class AsyncCursor: +class _AsyncCursor: """Thin Python wrapper over ``mssql_py_core.PyAsyncCursor``. Warning: @@ -129,7 +129,7 @@ async def execute( *parameters: Any, use_prepare: bool = True, reset_cursor: bool = True, - ) -> "AsyncCursor": + ) -> "_AsyncCursor": async with self._result_transition(): return await async_execute.execute( self, diff --git a/mssql_python/async_query/async_execute.py b/mssql_python/async_query/async_execute.py index 6d8015e63..6f9bb7b05 100644 --- a/mssql_python/async_query/async_execute.py +++ b/mssql_python/async_query/async_execute.py @@ -10,10 +10,10 @@ from .exception_translator import translate_py_core_exceptions if TYPE_CHECKING: - from .async_cursor import AsyncCursor + from .async_cursor import _AsyncCursor # pyright: ignore[reportPrivateUsage] -def _get_py_core_async_cursor(cursor: "AsyncCursor") -> Any: +def _get_py_core_async_cursor(cursor: "_AsyncCursor") -> Any: return cursor._py_core_async_cursor # pyright: ignore[reportPrivateUsage] @@ -28,7 +28,7 @@ def _is_non_mutating_rejection(error: BaseException) -> bool: ) -def _reconcile_failed_execution(cursor: "AsyncCursor", error: BaseException) -> None: +def _reconcile_failed_execution(cursor: "_AsyncCursor", error: BaseException) -> None: if _is_non_mutating_rejection(error): return cursor._reset_fetch_tracking() # pyright: ignore[reportPrivateUsage] @@ -40,12 +40,12 @@ def _reconcile_failed_execution(cursor: "AsyncCursor", error: BaseException) -> async def execute( - cursor: "AsyncCursor", + cursor: "_AsyncCursor", operation: str, *parameters: Any, use_prepare: bool = True, reset_cursor: bool = True, -) -> "AsyncCursor": +) -> "_AsyncCursor": """Execute a statement using the py-core async cursor.""" if len(parameters) == 1 and isinstance(parameters[0], (tuple, list, Row)): parameters = tuple(parameters[0]) @@ -83,7 +83,7 @@ async def execute( async def executemany( - cursor: "AsyncCursor", + cursor: "_AsyncCursor", operation: str, seq_of_parameters: Sequence[Sequence[Any]] | Sequence[Mapping[str, Any]], *, diff --git a/mssql_python/async_query/async_fetch.py b/mssql_python/async_query/async_fetch.py index 8cac01c23..404baa5e6 100644 --- a/mssql_python/async_query/async_fetch.py +++ b/mssql_python/async_query/async_fetch.py @@ -10,7 +10,7 @@ from .exception_translator import translate_py_core_exceptions if TYPE_CHECKING: - from .async_cursor import AsyncCursor + from .async_cursor import _AsyncCursor # pyright: ignore[reportPrivateUsage] _ResultSnapshot = tuple[ int, @@ -21,11 +21,11 @@ ] -def _get_py_core_async_cursor(cursor: "AsyncCursor") -> Any: +def _get_py_core_async_cursor(cursor: "_AsyncCursor") -> Any: return cursor._py_core_async_cursor # pyright: ignore[reportPrivateUsage] -def _snapshot_result(cursor: "AsyncCursor") -> _ResultSnapshot: +def _snapshot_result(cursor: "_AsyncCursor") -> _ResultSnapshot: return ( cursor._result_generation, # pyright: ignore[reportPrivateUsage] cursor._column_map, # pyright: ignore[reportPrivateUsage] @@ -36,7 +36,7 @@ def _snapshot_result(cursor: "AsyncCursor") -> _ResultSnapshot: def _reconcile_failed_fetch( - cursor: "AsyncCursor", generation: int, operation: str, error: BaseException + cursor: "_AsyncCursor", generation: int, operation: str, error: BaseException ) -> None: if generation != cursor._result_generation: # pyright: ignore[reportPrivateUsage] return @@ -60,7 +60,7 @@ def _wrap_row(snapshot: _ResultSnapshot, values: tuple[Any, ...]) -> Row: ) -async def fetchone(cursor: "AsyncCursor") -> Row | None: +async def fetchone(cursor: "_AsyncCursor") -> Row | None: """Fetch the next row through the py-core async cursor.""" logger.debug("AsyncCursor.fetchone: starting") await cursor._wait_for_result_publication() # pyright: ignore[reportPrivateUsage] @@ -82,7 +82,7 @@ async def fetchone(cursor: "AsyncCursor") -> Row | None: return None if row is None else _wrap_row(snapshot, row) -async def fetchmany(cursor: "AsyncCursor", size: int | None = None) -> list[Row]: +async def fetchmany(cursor: "_AsyncCursor", size: int | None = None) -> list[Row]: """Fetch up to size rows, using cursor arraysize when size is omitted.""" await cursor._wait_for_result_publication() # pyright: ignore[reportPrivateUsage] cursor._check_closed() # pyright: ignore[reportPrivateUsage] @@ -114,7 +114,7 @@ async def fetchmany(cursor: "AsyncCursor", size: int | None = None) -> list[Row] return [_wrap_row(snapshot, row) for row in rows] -async def fetchall(cursor: "AsyncCursor") -> list[Row]: +async def fetchall(cursor: "_AsyncCursor") -> list[Row]: """Fetch all remaining rows through the py-core async cursor.""" logger.debug("AsyncCursor.fetchall: starting") await cursor._wait_for_result_publication() # pyright: ignore[reportPrivateUsage] diff --git a/tests/AsyncTest/conftest.py b/tests/AsyncTest/conftest.py index b1131de5e..6cb93b042 100644 --- a/tests/AsyncTest/conftest.py +++ b/tests/AsyncTest/conftest.py @@ -1,7 +1,7 @@ import pytest import pytest_asyncio -from mssql_python.async_query import AsyncConnection +from mssql_python.async_query import _AsyncConnection # pyright: ignore[reportPrivateUsage] @pytest.fixture @@ -13,7 +13,7 @@ def async_connection_string(conn_str): @pytest_asyncio.fixture async def async_connection(async_connection_string): - connection = await AsyncConnection.connect(async_connection_string) + connection = await _AsyncConnection.connect(async_connection_string) try: yield connection finally: diff --git a/tests/AsyncTest/test_001_async_query_native.py b/tests/AsyncTest/test_001_async_query_native.py index 44c76aac2..9685bbecc 100644 --- a/tests/AsyncTest/test_001_async_query_native.py +++ b/tests/AsyncTest/test_001_async_query_native.py @@ -2,9 +2,22 @@ import pytest +import mssql_python.async_query as async_query import mssql_python.async_query._native as native +def test_async_entry_points_are_internal(): + from mssql_python.async_query import _AsyncConnection # pyright: ignore[reportPrivateUsage] + from mssql_python.async_query import _AsyncCursor # pyright: ignore[reportPrivateUsage] + + assert _AsyncConnection.__name__ == "_AsyncConnection" + assert _AsyncCursor.__name__ == "_AsyncCursor" + for name in ("AsyncConnection", "AsyncCursor"): + assert not hasattr(async_query, name) + assert name not in async_query.__all__ + assert f"_{name}" not in async_query.__all__ + + def test_load_py_core_uses_direct_native_dependency(): pytest.importorskip("mssql_py_core", exc_type=ImportError) diff --git a/tests/AsyncTest/test_002_async_connection.py b/tests/AsyncTest/test_002_async_connection.py index ef0b10022..4257c4644 100644 --- a/tests/AsyncTest/test_002_async_connection.py +++ b/tests/AsyncTest/test_002_async_connection.py @@ -7,14 +7,15 @@ InterfaceError, NotSupportedError, ) -from mssql_python.async_query import AsyncConnection, AsyncCursor +from mssql_python.async_query import _AsyncConnection # pyright: ignore[reportPrivateUsage] +from mssql_python.async_query import _AsyncCursor # pyright: ignore[reportPrivateUsage] from mssql_python.async_query._connection_context import build_async_connection_context from mssql_python.helpers import connstr_to_pycore_params @pytest.mark.asyncio async def test_connect_accepts_login_options_with_real_py_core(async_connection_string): - connection = await AsyncConnection.connect( + connection = await _AsyncConnection.connect( async_connection_string, autocommit=True, timeout=12, @@ -24,14 +25,14 @@ async def test_connect_accepts_login_options_with_real_py_core(async_connection_ assert connection.autocommit is True assert connection.closed is False assert connection.is_connected() is True - assert repr(connection) == "AsyncConnection(connected)" + assert repr(connection) == "_AsyncConnection(connected)" finally: await connection.close() @pytest.mark.asyncio async def test_connect_defaults_autocommit_to_false(async_connection_string): - connection = await AsyncConnection.connect(async_connection_string) + connection = await _AsyncConnection.connect(async_connection_string) try: assert connection.autocommit is False finally: @@ -268,7 +269,7 @@ def test_async_connection_rejects_missing_server(): @pytest.mark.asyncio async def test_connection_exposes_complete_native_surface(async_connection): cursor = async_connection.cursor() - assert isinstance(cursor, AsyncCursor) + assert isinstance(cursor, _AsyncCursor) assert async_connection.timeout == 0 async_connection.timeout = 12 assert async_connection.timeout == 12 @@ -279,7 +280,7 @@ async def test_connection_exposes_complete_native_surface(async_connection): with pytest.raises(AttributeError): setattr(async_connection, "closed", True) assert async_connection.is_connected() is True - assert repr(async_connection) == "AsyncConnection(connected)" + assert repr(async_connection) == "_AsyncConnection(connected)" await cursor.close() assert await async_connection.commit() is None @@ -290,7 +291,7 @@ async def test_connection_exposes_complete_native_surface(async_connection): async def test_async_context_manager_returns_wrapper_and_closes_connection( async_connection_string, ): - connection = await AsyncConnection.connect(async_connection_string) + connection = await _AsyncConnection.connect(async_connection_string) async with connection as entered: assert entered is connection @@ -302,19 +303,19 @@ async def test_async_context_manager_returns_wrapper_and_closes_connection( @pytest.mark.asyncio async def test_close_can_be_called_repeatedly(async_connection_string): - connection = await AsyncConnection.connect(async_connection_string) + connection = await _AsyncConnection.connect(async_connection_string) await connection.close() await connection.close() assert connection.closed is True assert connection.is_connected() is False - assert repr(connection) == "AsyncConnection(closed)" + assert repr(connection) == "_AsyncConnection(closed)" @pytest.mark.asyncio async def test_operations_after_close_translate_native_errors(async_connection_string): - connection = await AsyncConnection.connect(async_connection_string) + connection = await _AsyncConnection.connect(async_connection_string) await connection.close() with pytest.raises(InterfaceError, match="Connection is closed") as cursor_error: @@ -338,7 +339,7 @@ async def test_operations_after_close_translate_native_errors(async_connection_s async def test_context_manager_preserves_block_exception_and_closes_connection( async_connection_string, ): - connection = await AsyncConnection.connect(async_connection_string) + connection = await _AsyncConnection.connect(async_connection_string) with pytest.raises(ValueError, match="user_error_42"): async with connection: diff --git a/tests/AsyncTest/test_003_async_exceptions.py b/tests/AsyncTest/test_003_async_exceptions.py index 13e26c296..0b36a593c 100644 --- a/tests/AsyncTest/test_003_async_exceptions.py +++ b/tests/AsyncTest/test_003_async_exceptions.py @@ -6,7 +6,7 @@ mssql_py_core = pytest.importorskip("mssql_py_core", exc_type=ImportError) from mssql_python import exceptions as public_exceptions -from mssql_python.async_query import AsyncConnection +from mssql_python.async_query import _AsyncConnection # pyright: ignore[reportPrivateUsage] from mssql_python.async_query.exception_translator import ( translate_py_core_exception, translate_py_core_exceptions, @@ -188,7 +188,7 @@ async def test_closed_cursor_executemany_checks_state_before_parameters(async_co @pytest.mark.asyncio async def test_connection_close_invalidates_cursor_fetchmany_fast_path(async_connection_string): - connection = await AsyncConnection.connect(async_connection_string) + connection = await _AsyncConnection.connect(async_connection_string) cursor = connection.cursor() await connection.close() @@ -280,7 +280,7 @@ async def test_execute_programming_error_preserves_diagnostics_and_cursor_is_reu async def test_executemany_integrity_error_reports_row_and_preserves_partial_progress( async_connection_string, ): - connection = await AsyncConnection.connect(async_connection_string, autocommit=True) + connection = await _AsyncConnection.connect(async_connection_string, autocommit=True) cursor = connection.cursor() table_name = f"async_exception_{uuid4().hex}" try: @@ -306,7 +306,7 @@ async def test_executemany_integrity_error_reports_row_and_preserves_partial_pro @pytest.mark.asyncio async def test_timeout_is_operational_error_and_cursor_is_reusable(async_connection_string): - connection = await AsyncConnection.connect(async_connection_string) + connection = await _AsyncConnection.connect(async_connection_string) connection.timeout = 1 cursor = connection.cursor() try: @@ -340,4 +340,4 @@ async def test_busy_connection_is_operational_error(async_connection): def test_async_connection_exposes_public_exception_classes(): for name in EXCEPTION_NAMES: - assert getattr(AsyncConnection, name) is getattr(public_exceptions, name) + assert getattr(_AsyncConnection, name) is getattr(public_exceptions, name) diff --git a/tests/AsyncTest/test_004_async_logging.py b/tests/AsyncTest/test_004_async_logging.py index 2e16f0eb6..7d8e48ce4 100644 --- a/tests/AsyncTest/test_004_async_logging.py +++ b/tests/AsyncTest/test_004_async_logging.py @@ -5,7 +5,7 @@ mssql_py_core = pytest.importorskip("mssql_py_core", exc_type=ImportError) from mssql_python import OperationalError, setup_logging -from mssql_python.async_query import AsyncConnection +from mssql_python.async_query import _AsyncConnection # pyright: ignore[reportPrivateUsage] from mssql_python.async_query import exception_translator from mssql_python.connection_string_parser import ( _ConnectionStringParser, # pyright: ignore[reportPrivateUsage] @@ -54,7 +54,7 @@ async def test_connect_logging_does_not_include_client_context( ): log_path = enable_file_logging(tmp_path, "async-connect.log") - connection = await AsyncConnection.connect(async_connection_string, autocommit=True) + connection = await _AsyncConnection.connect(async_connection_string, autocommit=True) await connection.close() messages = read_log(log_path) @@ -73,7 +73,7 @@ async def test_default_logger_combines_python_and_py_core_operation_logs( tmp_path, ): log_path = enable_file_logging(tmp_path, "async-operations.log") - connection = await AsyncConnection.connect(async_connection_string, autocommit=True) + connection = await _AsyncConnection.connect(async_connection_string, autocommit=True) cursor = connection.cursor() try: await cursor.execute("SELECT CAST(? AS INT) AS value UNION ALL SELECT 2", 1) @@ -139,7 +139,7 @@ async def test_context_logging_records_error_presence_without_error_details( async_connection_string, tmp_path, ): - connection = await AsyncConnection.connect(async_connection_string) + connection = await _AsyncConnection.connect(async_connection_string) log_path = enable_file_logging(tmp_path, "async-context.log") secret_message = "sensitive user exception" diff --git a/tests/AsyncTest/test_006_async_execute.py b/tests/AsyncTest/test_006_async_execute.py index 82a53ce8d..f028a9120 100644 --- a/tests/AsyncTest/test_006_async_execute.py +++ b/tests/AsyncTest/test_006_async_execute.py @@ -9,7 +9,9 @@ mssql_py_core = pytest.importorskip("mssql_py_core", exc_type=ImportError) import mssql_python -from mssql_python.async_query import AsyncConnection, AsyncCursor, async_execute +from mssql_python.async_query import _AsyncConnection # pyright: ignore[reportPrivateUsage] +from mssql_python.async_query import _AsyncCursor # pyright: ignore[reportPrivateUsage] +from mssql_python.async_query import async_execute from mssql_python import DatabaseError, OperationalError, ProgrammingError from mssql_python.row import Row @@ -51,7 +53,7 @@ async def fetchone(self): self.rowcount = 2 return (2,) - class StatefulAsyncCursor(AsyncCursor): + class StatefulAsyncCursor(_AsyncCursor): def seed_result_state(self): self._description = [("value", int, None, None, None, None, True)] self._column_map = {"value": 0} @@ -126,7 +128,7 @@ async def executemany(self, *_args, **_kwargs): self.rowcount = -1 raise RuntimeError("execution failed") - class StatefulAsyncCursor(AsyncCursor): + class StatefulAsyncCursor(_AsyncCursor): def seed_result_state(self): self._description = [("value", int, None, None, None, None, True)] self._column_map = {"value": 0} @@ -172,7 +174,7 @@ async def executemany(self, *_args, **_kwargs): async def fetchone(self): return (self.value,) - class StatefulAsyncCursor(AsyncCursor): + class StatefulAsyncCursor(_AsyncCursor): def seed_result_state(self): self._description = [("id", int, None, None, None, None, True)] self._column_map = {"id": 0} @@ -224,7 +226,7 @@ async def executemany(self, *_args, **_kwargs): async def fetchone(self): return (self.value,) - class StatefulAsyncCursor(AsyncCursor): + class StatefulAsyncCursor(_AsyncCursor): def seed_result_state(self): self._description = [("id", int, None, None, None, None, True)] self._column_map = {"id": 0} @@ -274,7 +276,7 @@ async def executemany(self, *_args, **_kwargs): self.broken = True raise native_error - cursor = AsyncCursor(BrokenNativeCursor()) + cursor = _AsyncCursor(BrokenNativeCursor()) with pytest.raises(DatabaseError) as caught: if method == "execute": @@ -293,11 +295,11 @@ async def test_execute_returns_public_cursor_and_binds_parameters( async_connection, use_prepare, ): - assert isinstance(async_connection, AsyncConnection) + assert isinstance(async_connection, _AsyncConnection) cursor = async_connection.cursor() try: - assert isinstance(cursor, AsyncCursor) + assert isinstance(cursor, _AsyncCursor) result = await cursor.execute( "IF CAST(? AS INT) <> 7 THROW 50000, 'Unexpected parameter value', 1", diff --git a/tests/AsyncTest/test_007_async_fetch.py b/tests/AsyncTest/test_007_async_fetch.py index da37b1f98..4d7d62966 100644 --- a/tests/AsyncTest/test_007_async_fetch.py +++ b/tests/AsyncTest/test_007_async_fetch.py @@ -8,7 +8,7 @@ import mssql_python from mssql_python import DataError, OperationalError, ProgrammingError, Row -from mssql_python.async_query import AsyncCursor +from mssql_python.async_query import _AsyncCursor # pyright: ignore[reportPrivateUsage] @pytest.mark.asyncio @@ -17,7 +17,7 @@ class NativeCursor: async def fetchone(self): return (7,) - cursor = AsyncCursor(NativeCursor()) + cursor = _AsyncCursor(NativeCursor()) row = await cursor.fetchone() @@ -78,7 +78,7 @@ async def nextset(self): self.description = None raise RuntimeError("nextset failed") - class StatefulAsyncCursor(AsyncCursor): + class StatefulAsyncCursor(_AsyncCursor): def seed_result_state(self): self._description = [("value", int, None, None, None, None, True)] self._column_map = {"value": 0} @@ -134,7 +134,7 @@ async def fetchall(self): async def nextset(self): raise RuntimeError("Connection is busy with another cursor operation") - cursor = AsyncCursor(BusyNativeCursor()) + cursor = _AsyncCursor(BusyNativeCursor()) previous_native_uuid = mssql_python.native_uuid fetch_task = None try: @@ -183,7 +183,7 @@ async def fetchall(self): async def close(self): self.description = None - cursor = AsyncCursor(ClosingNativeCursor()) + cursor = _AsyncCursor(ClosingNativeCursor()) fetch_task = None try: await cursor.execute("SELECT value") @@ -218,7 +218,7 @@ async def fetchone(self): async def close(self): raise RuntimeError("Connection is busy with another cursor operation") - cursor = AsyncCursor(RejectingNativeCursor()) + cursor = _AsyncCursor(RejectingNativeCursor()) await cursor.execute("SELECT value") assert await cursor.fetchone() == [1] assert cursor.rowcount == 1 @@ -265,7 +265,7 @@ async def nextset(self): ] return True - cursor = AsyncCursor(NavigatingNativeCursor()) + cursor = _AsyncCursor(NavigatingNativeCursor()) previous_native_uuid = mssql_python.native_uuid fetch_task = None try: @@ -339,7 +339,7 @@ async def fetchmany(self, _size): async def fetchall(self): return [await self._fetch()] - cursor = AsyncCursor(NavigatingNativeCursor()) + cursor = _AsyncCursor(NavigatingNativeCursor()) async def advance_results(): for _ in range(transition_count): @@ -407,7 +407,7 @@ async def nextset(self): async def fetchone(self): return (self.value,) - cursor = AsyncCursor(CancelledNativeCursor()) + cursor = _AsyncCursor(CancelledNativeCursor()) await cursor.execute("SELECT id") first = await cursor.fetchone() assert first is not None @@ -459,7 +459,7 @@ async def nextset(self): self.description = [("new_value", int, None, None, None, None, True)] return True - cursor = AsyncCursor(NavigatingNativeCursor()) + cursor = _AsyncCursor(NavigatingNativeCursor()) fetch_task = None await cursor.execute("SELECT old_value") try: @@ -506,7 +506,7 @@ async def fetchmany(self, _size): async def fetchall(self): return await self._fail() - class StatefulAsyncCursor(AsyncCursor): + class StatefulAsyncCursor(_AsyncCursor): def seed_result_state(self): self._description = [("value", int, None, None, None, None, True)] self._column_map = {"value": 0} @@ -553,7 +553,7 @@ async def fetchmany(self, _size): async def fetchall(self): return await self._fetch() - class StatefulAsyncCursor(AsyncCursor): + class StatefulAsyncCursor(_AsyncCursor): def seed_result_state(self): self._description = [("value", int, None, None, None, None, True)] self._column_map = {"value": 0} @@ -598,7 +598,7 @@ async def fetchmany(self, _size): async def fetchall(self): return await self._reject() - class StatefulAsyncCursor(AsyncCursor): + class StatefulAsyncCursor(_AsyncCursor): def seed_result_state(self): self._description = [("value", int, None, None, None, None, True)] self._column_map = {"value": 0} From 99c6ad62e0e7b991acdd31f412a40d8035ce4d43 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 24 Sep 2026 04:43:30 +0000 Subject: [PATCH 2/2] DOC: Clarify internal async cursor documentation Co-authored-by: subrata-ms <141804867+subrata-ms@users.noreply.github.com> --- mssql_python/async_query/async_cursor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mssql_python/async_query/async_cursor.py b/mssql_python/async_query/async_cursor.py index dd340af8c..2547e2fae 100644 --- a/mssql_python/async_query/async_cursor.py +++ b/mssql_python/async_query/async_cursor.py @@ -1,4 +1,4 @@ -"""Public asynchronous cursor backed directly by mssql-py-core. +"""Internal asynchronous cursor backed directly by mssql-py-core. Warning: Async query execution APIs are under active development and are not intended @@ -21,7 +21,7 @@ class _AsyncCursor: - """Thin Python wrapper over ``mssql_py_core.PyAsyncCursor``. + """Internal Python wrapper over ``mssql_py_core.PyAsyncCursor``. Warning: This preview API is under active development and is not intended for production use.