From ebd808f326f8f792ccf88f902925c986f936ba29 Mon Sep 17 00:00:00 2001 From: disqualifier Date: Tue, 30 Jun 2026 21:04:51 -0400 Subject: [PATCH] fix: async with closes the motor client when connect()/ping fails (mongo-1) __aenter__ delegated to connect(); when the ping raised on a bad server, __aexit__ was not called so the eagerly-constructed motor client (with its background topology monitor + pool) leaked. a retry loop against a flapping DB accumulated live clients. now close-on-failure. verified: 5 failed async-with blocks leave 0 open clients (was 5). bump v0.1.4 -> v0.1.5 Signed-off-by: disqualifier --- README.md | 6 +++--- pyproject.toml | 2 +- src/mongo/mongo.py | 10 +++++++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fb08d01..23b61b8 100644 --- a/README.md +++ b/README.md @@ -8,18 +8,18 @@ helpers for the common paths, with a raw escape hatch for everything else. `requirements.txt`: ``` -mongo @ git+ssh://git@git.rethinkstudios.io/rethink-public/mongo.git@v0.1.4 +mongo @ git+ssh://git@git.rethinkstudios.io/rethink-public/mongo.git@v0.1.5 ``` Direct: ```bash -pip install "mongo @ git+ssh://git@git.rethinkstudios.io/rethink-public/mongo.git@v0.1.4" +pip install "mongo @ git+ssh://git@git.rethinkstudios.io/rethink-public/mongo.git@v0.1.5" ``` Requires `motor` and `pymongo` (pulled transitively). -Drop the `@v0.1.4` suffix from the line above to install the latest unpinned. +Drop the `@v0.1.5` suffix from the line above to install the latest unpinned. ## Usage diff --git a/pyproject.toml b/pyproject.toml index 14280b2..d34189e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "mongo" -version = "0.1.4" +version = "0.1.5" description = "async mongodb wrapper over motor with a raw escape hatch" requires-python = ">=3.10" dependencies = [ diff --git a/src/mongo/mongo.py b/src/mongo/mongo.py index 2cca812..bc62efd 100644 --- a/src/mongo/mongo.py +++ b/src/mongo/mongo.py @@ -67,7 +67,15 @@ class MongoDB: return self async def __aenter__(self) -> "MongoDB": - return await self.connect() + try: + return await self.connect() + except BaseException: + # connect()/ping failing here would otherwise leak the motor client (built + # eagerly in __init__ with a background topology monitor + pool) — __aexit__ + # is not called when __aenter__ raises. close it before propagating so a + # retry loop against a flapping server doesn't accumulate live clients. + self.close() + raise async def __aexit__(self, exc_type, exc, tb) -> None: self.close()