Skip to content

Commit d80d6ae

Browse files
committed
REFACTOR: Mark preview async entry-point classes as internal
1 parent 111e0cf commit d80d6ae

12 files changed

Lines changed: 86 additions & 69 deletions

‎mssql_python/async_query/__init__.py‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
"""Asynchronous query support backed directly by mssql-py-core.
22
3+
The internal entry points are ``_AsyncConnection`` and ``_AsyncCursor``.
4+
Their methods retain DB-API names, but the classes are not stable public API.
5+
36
Warning:
47
Async query execution APIs are under active development and are not intended
58
for production use. Their signatures, behavior, error handling, and compatibility
69
may change without notice.
710
"""
811

912
from ._native import load_py_core
10-
from .async_connection import AsyncConnection
11-
from .async_cursor import AsyncCursor
13+
from .async_connection import _AsyncConnection # pyright: ignore[reportPrivateUsage]
14+
from .async_cursor import _AsyncCursor # pyright: ignore[reportPrivateUsage]
1215
from .exception_translator import (
1316
DataError,
1417
DatabaseError,
@@ -23,8 +26,6 @@
2326
)
2427

2528
__all__ = [
26-
"AsyncConnection",
27-
"AsyncCursor",
2829
"DataError",
2930
"DatabaseError",
3031
"Error",

‎mssql_python/async_query/async_connection.py‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from ..logging import logger
1212
from ._native import load_py_core
1313
from ._connection_context import build_async_connection_context
14-
from .async_cursor import AsyncCursor
14+
from .async_cursor import _AsyncCursor # pyright: ignore[reportPrivateUsage]
1515
from .exception_translator import (
1616
DataError,
1717
DatabaseError,
@@ -27,7 +27,7 @@
2727
)
2828

2929

30-
class AsyncConnection:
30+
class _AsyncConnection:
3131
"""Thin Python wrapper over ``mssql_py_core.PyAsyncConnection``.
3232
3333
Warning:
@@ -56,7 +56,7 @@ async def connect(
5656
autocommit: bool = False,
5757
timeout: int = 0,
5858
python_logger: Optional[Any] = None,
59-
) -> "AsyncConnection":
59+
) -> "_AsyncConnection":
6060
"""Establish an asynchronous connection from an ODBC connection string."""
6161
logger_bridge = python_logger
6262
if logger_bridge is None and logger.is_debug_enabled:
@@ -81,12 +81,12 @@ async def connect(
8181
logger.debug("AsyncConnection.connect: connected")
8282
return cls(py_core_async_connection)
8383

84-
def cursor(self) -> AsyncCursor:
84+
def cursor(self) -> _AsyncCursor:
8585
"""Create a public asynchronous cursor sharing this connection."""
8686
with translate_py_core_exceptions():
8787
py_core_async_cursor = self._py_core_async_connection.cursor()
8888
logger.debug("AsyncConnection.cursor: cursor created")
89-
return AsyncCursor(py_core_async_cursor, self)
89+
return _AsyncCursor(py_core_async_cursor, self)
9090

9191
async def commit(self) -> None:
9292
"""Commit the active transaction, if any."""
@@ -109,7 +109,7 @@ async def close(self) -> None:
109109
await self._py_core_async_connection.close()
110110
logger.debug("AsyncConnection.close: completed")
111111

112-
async def __aenter__(self) -> "AsyncConnection":
112+
async def __aenter__(self) -> "_AsyncConnection":
113113
logger.debug("AsyncConnection.__aenter__: entering context")
114114
with translate_py_core_exceptions():
115115
await self._py_core_async_connection.__aenter__()
@@ -157,4 +157,4 @@ def is_connected(self) -> bool:
157157

158158
def __repr__(self) -> str:
159159
state = "closed" if self.closed else "connected"
160-
return f"AsyncConnection({state})"
160+
return f"_AsyncConnection({state})"

‎mssql_python/async_query/async_cursor.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from .exception_translator import translate_py_core_exceptions
2121

2222

23-
class AsyncCursor:
23+
class _AsyncCursor:
2424
"""Thin Python wrapper over ``mssql_py_core.PyAsyncCursor``.
2525
2626
Warning:
@@ -129,7 +129,7 @@ async def execute(
129129
*parameters: Any,
130130
use_prepare: bool = True,
131131
reset_cursor: bool = True,
132-
) -> "AsyncCursor":
132+
) -> "_AsyncCursor":
133133
async with self._result_transition():
134134
return await async_execute.execute(
135135
self,

‎mssql_python/async_query/async_execute.py‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
from .exception_translator import translate_py_core_exceptions
1111

1212
if TYPE_CHECKING:
13-
from .async_cursor import AsyncCursor
13+
from .async_cursor import _AsyncCursor # pyright: ignore[reportPrivateUsage]
1414

1515

16-
def _get_py_core_async_cursor(cursor: "AsyncCursor") -> Any:
16+
def _get_py_core_async_cursor(cursor: "_AsyncCursor") -> Any:
1717
return cursor._py_core_async_cursor # pyright: ignore[reportPrivateUsage]
1818

1919

@@ -28,7 +28,7 @@ def _is_non_mutating_rejection(error: BaseException) -> bool:
2828
)
2929

3030

31-
def _reconcile_failed_execution(cursor: "AsyncCursor", error: BaseException) -> None:
31+
def _reconcile_failed_execution(cursor: "_AsyncCursor", error: BaseException) -> None:
3232
if _is_non_mutating_rejection(error):
3333
return
3434
cursor._reset_fetch_tracking() # pyright: ignore[reportPrivateUsage]
@@ -40,12 +40,12 @@ def _reconcile_failed_execution(cursor: "AsyncCursor", error: BaseException) ->
4040

4141

4242
async def execute(
43-
cursor: "AsyncCursor",
43+
cursor: "_AsyncCursor",
4444
operation: str,
4545
*parameters: Any,
4646
use_prepare: bool = True,
4747
reset_cursor: bool = True,
48-
) -> "AsyncCursor":
48+
) -> "_AsyncCursor":
4949
"""Execute a statement using the py-core async cursor."""
5050
if len(parameters) == 1 and isinstance(parameters[0], (tuple, list, Row)):
5151
parameters = tuple(parameters[0])
@@ -83,7 +83,7 @@ async def execute(
8383

8484

8585
async def executemany(
86-
cursor: "AsyncCursor",
86+
cursor: "_AsyncCursor",
8787
operation: str,
8888
seq_of_parameters: Sequence[Sequence[Any]] | Sequence[Mapping[str, Any]],
8989
*,

‎mssql_python/async_query/async_fetch.py‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from .exception_translator import translate_py_core_exceptions
1111

1212
if TYPE_CHECKING:
13-
from .async_cursor import AsyncCursor
13+
from .async_cursor import _AsyncCursor # pyright: ignore[reportPrivateUsage]
1414

1515
_ResultSnapshot = tuple[
1616
int,
@@ -21,11 +21,11 @@
2121
]
2222

2323

24-
def _get_py_core_async_cursor(cursor: "AsyncCursor") -> Any:
24+
def _get_py_core_async_cursor(cursor: "_AsyncCursor") -> Any:
2525
return cursor._py_core_async_cursor # pyright: ignore[reportPrivateUsage]
2626

2727

28-
def _snapshot_result(cursor: "AsyncCursor") -> _ResultSnapshot:
28+
def _snapshot_result(cursor: "_AsyncCursor") -> _ResultSnapshot:
2929
return (
3030
cursor._result_generation, # pyright: ignore[reportPrivateUsage]
3131
cursor._column_map, # pyright: ignore[reportPrivateUsage]
@@ -36,7 +36,7 @@ def _snapshot_result(cursor: "AsyncCursor") -> _ResultSnapshot:
3636

3737

3838
def _reconcile_failed_fetch(
39-
cursor: "AsyncCursor", generation: int, operation: str, error: BaseException
39+
cursor: "_AsyncCursor", generation: int, operation: str, error: BaseException
4040
) -> None:
4141
if generation != cursor._result_generation: # pyright: ignore[reportPrivateUsage]
4242
return
@@ -60,7 +60,7 @@ def _wrap_row(snapshot: _ResultSnapshot, values: tuple[Any, ...]) -> Row:
6060
)
6161

6262

63-
async def fetchone(cursor: "AsyncCursor") -> Row | None:
63+
async def fetchone(cursor: "_AsyncCursor") -> Row | None:
6464
"""Fetch the next row through the py-core async cursor."""
6565
logger.debug("AsyncCursor.fetchone: starting")
6666
await cursor._wait_for_result_publication() # pyright: ignore[reportPrivateUsage]
@@ -82,7 +82,7 @@ async def fetchone(cursor: "AsyncCursor") -> Row | None:
8282
return None if row is None else _wrap_row(snapshot, row)
8383

8484

85-
async def fetchmany(cursor: "AsyncCursor", size: int | None = None) -> list[Row]:
85+
async def fetchmany(cursor: "_AsyncCursor", size: int | None = None) -> list[Row]:
8686
"""Fetch up to size rows, using cursor arraysize when size is omitted."""
8787
await cursor._wait_for_result_publication() # pyright: ignore[reportPrivateUsage]
8888
cursor._check_closed() # pyright: ignore[reportPrivateUsage]
@@ -114,7 +114,7 @@ async def fetchmany(cursor: "AsyncCursor", size: int | None = None) -> list[Row]
114114
return [_wrap_row(snapshot, row) for row in rows]
115115

116116

117-
async def fetchall(cursor: "AsyncCursor") -> list[Row]:
117+
async def fetchall(cursor: "_AsyncCursor") -> list[Row]:
118118
"""Fetch all remaining rows through the py-core async cursor."""
119119
logger.debug("AsyncCursor.fetchall: starting")
120120
await cursor._wait_for_result_publication() # pyright: ignore[reportPrivateUsage]

‎tests/AsyncTest/conftest.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22
import pytest_asyncio
33

4-
from mssql_python.async_query import AsyncConnection
4+
from mssql_python.async_query import _AsyncConnection # pyright: ignore[reportPrivateUsage]
55

66

77
@pytest.fixture
@@ -13,7 +13,7 @@ def async_connection_string(conn_str):
1313

1414
@pytest_asyncio.fixture
1515
async def async_connection(async_connection_string):
16-
connection = await AsyncConnection.connect(async_connection_string)
16+
connection = await _AsyncConnection.connect(async_connection_string)
1717
try:
1818
yield connection
1919
finally:

‎tests/AsyncTest/test_001_async_query_native.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,22 @@
22

33
import pytest
44

5+
import mssql_python.async_query as async_query
56
import mssql_python.async_query._native as native
67

78

9+
def test_async_entry_points_are_internal():
10+
from mssql_python.async_query import _AsyncConnection # pyright: ignore[reportPrivateUsage]
11+
from mssql_python.async_query import _AsyncCursor # pyright: ignore[reportPrivateUsage]
12+
13+
assert _AsyncConnection.__name__ == "_AsyncConnection"
14+
assert _AsyncCursor.__name__ == "_AsyncCursor"
15+
for name in ("AsyncConnection", "AsyncCursor"):
16+
assert not hasattr(async_query, name)
17+
assert name not in async_query.__all__
18+
assert f"_{name}" not in async_query.__all__
19+
20+
821
def test_load_py_core_uses_direct_native_dependency():
922
pytest.importorskip("mssql_py_core", exc_type=ImportError)
1023

‎tests/AsyncTest/test_002_async_connection.py‎

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@
77
InterfaceError,
88
NotSupportedError,
99
)
10-
from mssql_python.async_query import AsyncConnection, AsyncCursor
10+
from mssql_python.async_query import _AsyncConnection # pyright: ignore[reportPrivateUsage]
11+
from mssql_python.async_query import _AsyncCursor # pyright: ignore[reportPrivateUsage]
1112
from mssql_python.async_query._connection_context import build_async_connection_context
1213
from mssql_python.helpers import connstr_to_pycore_params
1314

1415

1516
@pytest.mark.asyncio
1617
async def test_connect_accepts_login_options_with_real_py_core(async_connection_string):
17-
connection = await AsyncConnection.connect(
18+
connection = await _AsyncConnection.connect(
1819
async_connection_string,
1920
autocommit=True,
2021
timeout=12,
@@ -24,14 +25,14 @@ async def test_connect_accepts_login_options_with_real_py_core(async_connection_
2425
assert connection.autocommit is True
2526
assert connection.closed is False
2627
assert connection.is_connected() is True
27-
assert repr(connection) == "AsyncConnection(connected)"
28+
assert repr(connection) == "_AsyncConnection(connected)"
2829
finally:
2930
await connection.close()
3031

3132

3233
@pytest.mark.asyncio
3334
async def test_connect_defaults_autocommit_to_false(async_connection_string):
34-
connection = await AsyncConnection.connect(async_connection_string)
35+
connection = await _AsyncConnection.connect(async_connection_string)
3536
try:
3637
assert connection.autocommit is False
3738
finally:
@@ -268,7 +269,7 @@ def test_async_connection_rejects_missing_server():
268269
@pytest.mark.asyncio
269270
async def test_connection_exposes_complete_native_surface(async_connection):
270271
cursor = async_connection.cursor()
271-
assert isinstance(cursor, AsyncCursor)
272+
assert isinstance(cursor, _AsyncCursor)
272273
assert async_connection.timeout == 0
273274
async_connection.timeout = 12
274275
assert async_connection.timeout == 12
@@ -279,7 +280,7 @@ async def test_connection_exposes_complete_native_surface(async_connection):
279280
with pytest.raises(AttributeError):
280281
setattr(async_connection, "closed", True)
281282
assert async_connection.is_connected() is True
282-
assert repr(async_connection) == "AsyncConnection(connected)"
283+
assert repr(async_connection) == "_AsyncConnection(connected)"
283284

284285
await cursor.close()
285286
assert await async_connection.commit() is None
@@ -290,7 +291,7 @@ async def test_connection_exposes_complete_native_surface(async_connection):
290291
async def test_async_context_manager_returns_wrapper_and_closes_connection(
291292
async_connection_string,
292293
):
293-
connection = await AsyncConnection.connect(async_connection_string)
294+
connection = await _AsyncConnection.connect(async_connection_string)
294295

295296
async with connection as entered:
296297
assert entered is connection
@@ -302,19 +303,19 @@ async def test_async_context_manager_returns_wrapper_and_closes_connection(
302303

303304
@pytest.mark.asyncio
304305
async def test_close_can_be_called_repeatedly(async_connection_string):
305-
connection = await AsyncConnection.connect(async_connection_string)
306+
connection = await _AsyncConnection.connect(async_connection_string)
306307

307308
await connection.close()
308309
await connection.close()
309310

310311
assert connection.closed is True
311312
assert connection.is_connected() is False
312-
assert repr(connection) == "AsyncConnection(closed)"
313+
assert repr(connection) == "_AsyncConnection(closed)"
313314

314315

315316
@pytest.mark.asyncio
316317
async def test_operations_after_close_translate_native_errors(async_connection_string):
317-
connection = await AsyncConnection.connect(async_connection_string)
318+
connection = await _AsyncConnection.connect(async_connection_string)
318319
await connection.close()
319320

320321
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
338339
async def test_context_manager_preserves_block_exception_and_closes_connection(
339340
async_connection_string,
340341
):
341-
connection = await AsyncConnection.connect(async_connection_string)
342+
connection = await _AsyncConnection.connect(async_connection_string)
342343

343344
with pytest.raises(ValueError, match="user_error_42"):
344345
async with connection:

‎tests/AsyncTest/test_003_async_exceptions.py‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
mssql_py_core = pytest.importorskip("mssql_py_core", exc_type=ImportError)
77

88
from mssql_python import exceptions as public_exceptions
9-
from mssql_python.async_query import AsyncConnection
9+
from mssql_python.async_query import _AsyncConnection # pyright: ignore[reportPrivateUsage]
1010
from mssql_python.async_query.exception_translator import (
1111
translate_py_core_exception,
1212
translate_py_core_exceptions,
@@ -188,7 +188,7 @@ async def test_closed_cursor_executemany_checks_state_before_parameters(async_co
188188

189189
@pytest.mark.asyncio
190190
async def test_connection_close_invalidates_cursor_fetchmany_fast_path(async_connection_string):
191-
connection = await AsyncConnection.connect(async_connection_string)
191+
connection = await _AsyncConnection.connect(async_connection_string)
192192
cursor = connection.cursor()
193193
await connection.close()
194194

@@ -280,7 +280,7 @@ async def test_execute_programming_error_preserves_diagnostics_and_cursor_is_reu
280280
async def test_executemany_integrity_error_reports_row_and_preserves_partial_progress(
281281
async_connection_string,
282282
):
283-
connection = await AsyncConnection.connect(async_connection_string, autocommit=True)
283+
connection = await _AsyncConnection.connect(async_connection_string, autocommit=True)
284284
cursor = connection.cursor()
285285
table_name = f"async_exception_{uuid4().hex}"
286286
try:
@@ -306,7 +306,7 @@ async def test_executemany_integrity_error_reports_row_and_preserves_partial_pro
306306

307307
@pytest.mark.asyncio
308308
async def test_timeout_is_operational_error_and_cursor_is_reusable(async_connection_string):
309-
connection = await AsyncConnection.connect(async_connection_string)
309+
connection = await _AsyncConnection.connect(async_connection_string)
310310
connection.timeout = 1
311311
cursor = connection.cursor()
312312
try:
@@ -340,4 +340,4 @@ async def test_busy_connection_is_operational_error(async_connection):
340340

341341
def test_async_connection_exposes_public_exception_classes():
342342
for name in EXCEPTION_NAMES:
343-
assert getattr(AsyncConnection, name) is getattr(public_exceptions, name)
343+
assert getattr(_AsyncConnection, name) is getattr(public_exceptions, name)

0 commit comments

Comments
 (0)