Skip to content

Commit 1d8ec78

Browse files
Drop underscore prefix on LEADER_ERROR_CODES re-import
The wire layer publishes LEADER_ERROR_CODES as a public constant. Both consumers (pool.py and connection.py) imported it under the private alias _LEADER_ERROR_CODES. The underscore was overprotective: the constant is intentionally re-imported from a public name, and neither module re-exports it via __all__. The alias gave a misleading "private to this module" cue to readers. Drop the alias in both modules; usages now read LEADER_ERROR_CODES directly. Pin the cross-layer identity contract via a new test that asserts LEADER_ERROR_CODES is the same object in pool.py, connection.py, and the wire layer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0052163 commit 1d8ec78

3 files changed

Lines changed: 28 additions & 6 deletions

File tree

src/dqliteclient/connection.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
)
2626
from dqlitewire import DEFAULT_MAX_CONTINUATION_FRAMES as _DEFAULT_MAX_CONTINUATION_FRAMES
2727
from dqlitewire import DEFAULT_MAX_TOTAL_ROWS as _DEFAULT_MAX_TOTAL_ROWS
28-
from dqlitewire import LEADER_ERROR_CODES as _LEADER_ERROR_CODES
28+
from dqlitewire import LEADER_ERROR_CODES
2929
from dqlitewire import SQLITE_BUSY as _SQLITE_BUSY
3030
from dqlitewire import TX_AUTO_ROLLBACK_PRIMARY_CODES as _TX_AUTO_ROLLBACK_PRIMARY_CODES
3131
from dqlitewire import primary_sqlite_code as _primary_sqlite_code
@@ -65,7 +65,7 @@
6565
# * SQLITE_ABORT (4) — operation aborted, e.g. via sqlite3_interrupt.
6666
# * SQLITE_INTERRUPT (9) — query interrupted via INTERRUPT.
6767
# * SQLITE_IOERR (10) — and most extended IOERR variants (the leader-
68-
# change variants are caught earlier as ``_LEADER_ERROR_CODES`` and
68+
# change variants are caught earlier as ``LEADER_ERROR_CODES`` and
6969
# trigger a full ``_invalidate`` instead).
7070
# * SQLITE_CORRUPT (11).
7171
# * SQLITE_FULL (13).
@@ -754,7 +754,7 @@ async def _connect_impl(self) -> None:
754754
self._invalidation_cause = None
755755
except OperationalError as e:
756756
await self._abort_protocol()
757-
if e.code in _LEADER_ERROR_CODES:
757+
if e.code in LEADER_ERROR_CODES:
758758
# Leader-change errors during OPEN are transport-level
759759
# problems — the caller needs to reconnect elsewhere, not
760760
# treat this as a SQL error.
@@ -1135,7 +1135,7 @@ async def _run_protocol[T](self, fn: Callable[[DqliteProtocol, int], Awaitable[T
11351135
self._invalidate(e)
11361136
raise
11371137
except OperationalError as e:
1138-
if e.code in _LEADER_ERROR_CODES:
1138+
if e.code in LEADER_ERROR_CODES:
11391139
self._invalidate(e)
11401140
elif _primary_sqlite_code(e.code) in _TX_AUTO_ROLLBACK_PRIMARY_CODES:
11411141
# Server-side SQLite engine auto-rolled-back the

src/dqliteclient/pool.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from dqlitewire import (
3030
DEFAULT_MAX_TOTAL_ROWS as _DEFAULT_MAX_TOTAL_ROWS,
3131
)
32-
from dqlitewire import LEADER_ERROR_CODES as _LEADER_ERROR_CODES
32+
from dqlitewire import LEADER_ERROR_CODES
3333

3434
__all__ = ["ConnectionPool"]
3535

@@ -1036,7 +1036,7 @@ async def _reset_connection(self, conn: DqliteConnection) -> bool:
10361036
# DEBUG) from genuine server failure (latent bug or
10371037
# server fault — WARNING with traceback).
10381038
code = getattr(exc, "code", None)
1039-
if code in _LEADER_ERROR_CODES:
1039+
if code in LEADER_ERROR_CODES:
10401040
logger.debug(
10411041
"pool: dropping connection %s after leader-class "
10421042
"ROLLBACK failure (code=%s)",
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""Pin: ``LEADER_ERROR_CODES`` is the single source of truth for the
2+
leader-change SQLite extended codes, owned by ``dqlitewire`` and
3+
imported as-is by ``dqliteclient``. Identity equality is the
4+
contract — a future SA-only override or accidental local copy would
5+
fail this test.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
11+
def test_dqliteclient_uses_wire_leader_error_codes_identity() -> None:
12+
# The constants live inside each module but are deliberately NOT
13+
# in ``__all__`` (they're re-imports of a wire-layer public
14+
# constant, not the module's own public surface). Direct
15+
# attribute access works at runtime; the mypy ignores reflect
16+
# the re-import-without-export pattern.
17+
import dqliteclient.connection as _conn_mod
18+
import dqliteclient.pool as _pool_mod
19+
from dqlitewire import LEADER_ERROR_CODES as wire_codes
20+
21+
assert _pool_mod.LEADER_ERROR_CODES is wire_codes # type: ignore[attr-defined]
22+
assert _conn_mod.LEADER_ERROR_CODES is wire_codes # type: ignore[attr-defined]

0 commit comments

Comments
 (0)