This commit is contained in:
Dominic Fallows 2026-09-12 16:48:35 +05:30 committed by GitHub
commit 4d8c26da80
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 35 additions and 7 deletions

View file

@ -54,10 +54,7 @@ class _PrismaEngine(Protocol):
class _PrismaClient(Protocol):
_Prisma__engine: _PrismaEngine
@property
def _engine(self) -> _PrismaEngine: ...
_engine: _PrismaEngine
class _PrismaDrainTracker:
@ -241,7 +238,9 @@ class PrismaWrapper:
@staticmethod
def _write_engine(prisma_client: _PrismaClient, engine: _PrismaEngine) -> None:
prisma_client._Prisma__engine = engine
# Public setter: prisma <0.13 stored the engine on the mangled `Prisma.__engine`,
# 0.13+ on `BasePrisma._internal_engine`. Both are __slots__ classes.
prisma_client._engine = engine # rebind-ok: third-party Prisma client instance, not a local param we own
def _instrument_prisma_client(self, prisma_client: _PrismaClient) -> _PrismaDrainTracker | None:
from prisma.errors import ClientNotConnectedError

View file

@ -171,6 +171,35 @@ def test_get_engine_pid_returns_zero_for_disconnected_client(disconnected_prisma
assert wrapper._get_engine_pid() == 0
def test_write_engine_uses_the_public_setter_not_the_mangled_name():
"""Real Prisma clients (>=0.13) are __slots__ classes exposing only a public
`_engine` property/setter, with no `_Prisma__engine` slot. Writing to the
mangled name raises AttributeError instead of storing the engine."""
class _FakeSlotsBasedPrismaClient:
__slots__ = ("_internal_engine",)
def __init__(self):
self._internal_engine = None
@property
def _engine(self):
if self._internal_engine is None:
raise AttributeError("not connected")
return self._internal_engine
@_engine.setter
def _engine(self, engine):
self._internal_engine = engine
client = _FakeSlotsBasedPrismaClient()
sentinel = object()
PrismaWrapper._write_engine(client, sentinel)
assert PrismaWrapper._read_engine(client) is sentinel
@pytest.mark.asyncio
async def test_recreate_prisma_client_recovers_from_disconnected_client(
mock_prisma_binary, disconnected_prisma

View file

@ -60,7 +60,7 @@ def _make_wrapper(engine_pid: int = 111, iam: bool = False) -> PrismaWrapper:
prisma = GeneratedPrisma(use_dotenv=False)
engine = MagicMock()
engine.process.pid = engine_pid
setattr(prisma, "_Prisma__engine", engine)
prisma._engine = engine
return PrismaWrapper(original_prisma=prisma, iam_token_db_auth=iam)
@ -158,7 +158,7 @@ def _token_db_url(created: datetime, expires_in: int = 900) -> str:
def test_wrapper_instruments_generated_prisma_engine() -> None:
prisma = GeneratedPrisma(use_dotenv=False)
engine = MagicMock()
setattr(prisma, "_Prisma__engine", engine)
prisma._engine = engine
wrapper = PrismaWrapper(original_prisma=prisma, iam_token_db_auth=False)