Skip to content

Commit 944e8c5

Browse files
Annotate module-level constants in dqlitedbapi as Final
Per PEP 591, immutable module-level constants should be annotated as Final. Sweep the dbapi modules: - connection.py: _NO_TX_CODES, _NO_TX_SUBSTRINGS, _LOOP_THREAD_JOIN_TIMEOUT_SECONDS - cursor.py: _SQLITE_{CONSTRAINT,INTERNAL,TOOBIG,MISMATCH,RANGE,NOMEM, PROTOCOL}, _ROW_RETURNING_PREFIXES, _EXECUTEMANY_REJECT_VERBS, _INT64_OVERFLOW_THRESHOLD, _UINT64_RANGE - types.py: _MAX_UNIXTIME_SECONDS No runtime behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 45a67a1 commit 944e8c5

3 files changed

Lines changed: 20 additions & 18 deletions

File tree

src/dqlitedbapi/connection.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import weakref
1111
from collections.abc import Coroutine
1212
from types import TracebackType
13-
from typing import Any
13+
from typing import Any, Final
1414

1515
import dqliteclient.exceptions as _client_exc
1616
from dqliteclient import DqliteConnection
@@ -54,13 +54,13 @@
5454
# normal ``OperationalError`` rather than silently swallow it at the
5555
# commit/rollback boundary — masking it would hide a real diagnostic
5656
# from callers who issued an empty COMMIT/ROLLBACK by accident.
57-
_NO_TX_CODES = frozenset({1})
57+
_NO_TX_CODES: Final[frozenset[int]] = frozenset({1})
5858
# Substrings that mark a benign "no transaction was active" reply.
5959
# Mirror the client-layer ``_is_no_tx_rollback_error`` recogniser so a
6060
# wording drift in the server (or in the embedded SQLite version) that
6161
# drops one of these clauses does not produce a silent layer
6262
# divergence — the client suppressing while the dbapi raises.
63-
_NO_TX_SUBSTRINGS = ("no transaction is active", "cannot rollback")
63+
_NO_TX_SUBSTRINGS: Final[tuple[str, ...]] = ("no transaction is active", "cannot rollback")
6464

6565
# Bound (in seconds) for joining the background event-loop thread on
6666
# teardown. Worst-case scenario: a coroutine queued on the loop is
@@ -70,7 +70,7 @@
7070
# while preventing process hang on close. Both the finalizer
7171
# (``_cleanup_loop_thread``) and ``Connection.close()`` use this
7272
# bound — keep them in step via the constant.
73-
_LOOP_THREAD_JOIN_TIMEOUT_SECONDS = 5.0
73+
_LOOP_THREAD_JOIN_TIMEOUT_SECONDS: Final[float] = 5.0
7474

7575

7676
def _validate_timeout(timeout: float) -> None:

src/dqlitedbapi/cursor.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import re
44
from collections.abc import Callable, Coroutine, Iterable, Mapping, Sequence
55
from types import TracebackType
6-
from typing import TYPE_CHECKING, Any, Protocol
6+
from typing import TYPE_CHECKING, Any, Final, Protocol
77

88
import dqliteclient.exceptions as _client_exc
99
from dqlitedbapi.exceptions import (
@@ -35,18 +35,18 @@
3535
# FOREIGN_KEY = 787, etc.) all share ``code & 0xFF == 19``. PEP 249
3636
# mandates IntegrityError for these, so map them here rather than
3737
# leaving every caller to inspect the code themselves.
38-
_SQLITE_CONSTRAINT = 19
38+
_SQLITE_CONSTRAINT: Final[int] = 19
3939

4040
# SQLite primary error code 2 (SQLITE_INTERNAL). stdlib ``sqlite3``
4141
# routes this to ``sqlite3.InternalError`` (CPython's
4242
# ``_pysqlite_seterror``); PEP 249 defines ``InternalError`` for exactly
4343
# this purpose — "internal errors of the database, e.g. the cursor is
4444
# not valid anymore".
45-
_SQLITE_INTERNAL = 2
45+
_SQLITE_INTERNAL: Final[int] = 2
4646

4747
# SQLITE_TOOBIG (18) — "value exceeds size limit" — is the canonical
4848
# PEP 249 ``DataError`` case ("problems with the processed data").
49-
_SQLITE_TOOBIG = 18
49+
_SQLITE_TOOBIG: Final[int] = 18
5050

5151
# SQLITE_MISMATCH (20) — datatype mismatch on STRICT tables. CPython
5252
# stdlib ``sqlite3`` (``Modules/_sqlite/util.c::_pysqlite_seterror``)
@@ -55,19 +55,19 @@
5555
# are defensible PEP 249 readings for STRICT-table datatype mismatch,
5656
# but callers porting between stdlib and dqlite expect the stdlib
5757
# grouping — so align to it.
58-
_SQLITE_MISMATCH = 20
58+
_SQLITE_MISMATCH: Final[int] = 20
5959

6060
# SQLITE_RANGE (25) — "bind index out of range" — is a caller-side
6161
# parameter-binding error; PEP 249 ``ProgrammingError`` is the right
6262
# fit ("bad parameter ... wrong number of parameters") rather than
6363
# ``DataError``.
64-
_SQLITE_RANGE = 25
64+
_SQLITE_RANGE: Final[int] = 25
6565

6666
# SQLITE_NOMEM (7) — server-side allocation failure. CPython stdlib
6767
# ``sqlite3`` raises ``MemoryError`` (system-level, bypasses PEP 249);
6868
# we route through ``InternalError`` so callers stay inside the
6969
# PEP 249 hierarchy and ``except dbapi.Error:`` continues to catch.
70-
_SQLITE_NOMEM = 7
70+
_SQLITE_NOMEM: Final[int] = 7
7171

7272
# SQLITE_CORRUPT (11), SQLITE_FORMAT (24), SQLITE_NOTADB (26) — the
7373
# server-side database file is malformed / wrong format / not a
@@ -84,7 +84,7 @@
8484
# default unmapped codes to OperationalError so this entry is
8585
# documentary — it pins the contract so a future audit shows the
8686
# code was considered.
87-
_SQLITE_PROTOCOL = 15
87+
_SQLITE_PROTOCOL: Final[int] = 15
8888

8989
# Registry of primary-code → PEP 249 class. Keep the default
9090
# (OperationalError) outside the dict so adding a code is one line.
@@ -328,15 +328,15 @@ def _strip_leading_comments(sql: str) -> str:
328328
return s
329329

330330

331-
_ROW_RETURNING_PREFIXES = ("SELECT", "VALUES", "PRAGMA", "EXPLAIN", "WITH")
331+
_ROW_RETURNING_PREFIXES: Final[tuple[str, ...]] = ("SELECT", "VALUES", "PRAGMA", "EXPLAIN", "WITH")
332332

333333
# Verbs that take no parameters and cannot legitimately drive an
334334
# ``executemany`` call. Stdlib ``sqlite3.Cursor.executemany`` rejects
335335
# the same shapes via its statement-type check; admitting them here
336336
# silently re-runs the bare statement N times against ignored bind
337337
# params, producing duplicate server-side savepoint frames (and other
338338
# state divergence) that compound with the duplicate-name LIFO rule.
339-
_EXECUTEMANY_REJECT_VERBS = frozenset(
339+
_EXECUTEMANY_REJECT_VERBS: Final[frozenset[str]] = frozenset(
340340
{"SAVEPOINT", "RELEASE", "ROLLBACK", "BEGIN", "COMMIT", "END"}
341341
)
342342

@@ -608,8 +608,8 @@ def _is_dml_with_returning(sql: str) -> bool:
608608
return body.startswith(("INSERT", "UPDATE", "DELETE", "REPLACE"))
609609

610610

611-
_INT64_OVERFLOW_THRESHOLD = 1 << 63
612-
_UINT64_RANGE = 1 << 64
611+
_INT64_OVERFLOW_THRESHOLD: Final[int] = 1 << 63
612+
_UINT64_RANGE: Final[int] = 1 << 64
613613

614614

615615
def _to_signed_int64(value: int) -> int:

src/dqlitedbapi/types.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import datetime
44
import math
5-
from typing import Any
5+
from typing import Any, Final
66

77
from dqlitedbapi.exceptions import DataError
88
from dqlitewire.constants import ValueType
@@ -428,7 +428,9 @@ def _datetime_from_iso8601(text: str) -> datetime.datetime | datetime.time | Non
428428
# does not depend on the host's libc behavior at module-load time —
429429
# 32-bit Windows would otherwise OverflowError on the
430430
# ``datetime.MAX.timestamp()`` round-trip.
431-
_MAX_UNIXTIME_SECONDS = 253402300799 # = datetime(9999,12,31,23,59,59,tz=UTC).timestamp()
431+
_MAX_UNIXTIME_SECONDS: Final[int] = (
432+
253402300799 # = datetime(9999,12,31,23,59,59,tz=UTC).timestamp()
433+
)
432434

433435

434436
def _datetime_from_unixtime(value: int) -> datetime.datetime:

0 commit comments

Comments
 (0)