From 0dd52e4c8d133f789dd8bd17086f32dc7e0b9fb1 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Fri, 3 Oct 2025 11:15:32 +0300 Subject: [PATCH 1/5] add close and aclose] --- falkordb/asyncio/falkordb.py | 23 +++++++++++++++++++++++ falkordb/falkordb.py | 25 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/falkordb/asyncio/falkordb.py b/falkordb/asyncio/falkordb.py index f2dd997..3e129ec 100644 --- a/falkordb/asyncio/falkordb.py +++ b/falkordb/asyncio/falkordb.py @@ -1,4 +1,5 @@ import redis.asyncio as redis +from redis.exceptions import RedisError from .cluster import * from .graph import AsyncGraph from typing import List, Union @@ -197,3 +198,25 @@ async def config_set(self, name: str, value=None) -> None: """ return await self.connection.execute_command(CONFIG_CMD, "SET", name, value) + + async def aclose(self) -> None: + """ + Close the underlying connection(s). + """ + + try: + await self.connection.aclose() + except RedisError: + # best-effort close — don't raise on Redis errors + pass + + + async def __aenter__(self) -> "FalkorDB": + """Return self to support async with-statement usage.""" + + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: + """Close the connection when exiting an async with-statement.""" + + await self.aclose() diff --git a/falkordb/falkordb.py b/falkordb/falkordb.py index 5bc5f58..39bf4b0 100644 --- a/falkordb/falkordb.py +++ b/falkordb/falkordb.py @@ -1,4 +1,5 @@ import redis +from redis.exceptions import RedisError from .cluster import * from .sentinel import * from .graph import Graph @@ -237,3 +238,27 @@ def config_set(self, name: str, value=None) -> None: """ return self.connection.execute_command(CONFIG_CMD, "SET", name, value) + + def close(self) -> None: + """ + Close the underlying connection(s). + """ + + try: + self.connection.close() + except RedisError: + # best-effort close — don't raise on Redis errors + pass + + def __enter__(self) -> "FalkorDB": + """Return self to support usage in a with-statement.""" + + return self + + def __exit__(self, exc_type, exc_val, exc_tb) -> None: + """Close the connection when exiting a with-statement.""" + self.close() + + def __del__(self): + self.close() + From 98bfffe751abd482ce2cce59532692c346379146 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Fri, 3 Oct 2025 11:16:45 +0300 Subject: [PATCH 2/5] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- falkordb/falkordb.py | 1 - 1 file changed, 1 deletion(-) diff --git a/falkordb/falkordb.py b/falkordb/falkordb.py index 39bf4b0..be4e3ae 100644 --- a/falkordb/falkordb.py +++ b/falkordb/falkordb.py @@ -258,7 +258,6 @@ def __enter__(self) -> "FalkorDB": def __exit__(self, exc_type, exc_val, exc_tb) -> None: """Close the connection when exiting a with-statement.""" self.close() - def __del__(self): self.close() From 94a6ac4eb997d5026f7868ef3fad3629fd419808 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Fri, 3 Oct 2025 11:17:20 +0300 Subject: [PATCH 3/5] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- falkordb/falkordb.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/falkordb/falkordb.py b/falkordb/falkordb.py index be4e3ae..58f0d54 100644 --- a/falkordb/falkordb.py +++ b/falkordb/falkordb.py @@ -258,6 +258,3 @@ def __enter__(self) -> "FalkorDB": def __exit__(self, exc_type, exc_val, exc_tb) -> None: """Close the connection when exiting a with-statement.""" self.close() - def __del__(self): - self.close() - From 1faec4416c43007dbf4d47f9e8561e4ed375fc6b Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Fri, 3 Oct 2025 11:29:10 +0300 Subject: [PATCH 4/5] add tests --- tests/test_close.py | 53 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/test_close.py diff --git a/tests/test_close.py b/tests/test_close.py new file mode 100644 index 0000000..7382723 --- /dev/null +++ b/tests/test_close.py @@ -0,0 +1,53 @@ +from types import SimpleNamespace +from unittest.mock import Mock, AsyncMock + +import pytest + +from falkordb.falkordb import FalkorDB as SyncFalkorDB +from falkordb.asyncio.falkordb import FalkorDB as AsyncFalkorDB + + +def test_sync_context_manager_calls_close(): + db = object.__new__(SyncFalkorDB) + + mock_conn = SimpleNamespace(close=Mock()) + mock_conn.connection_pool = SimpleNamespace(disconnect=Mock()) + db.connection = mock_conn + + # using context manager should call close on the underlying connection + with db as d: + assert d is db + + mock_conn.close.assert_called_once() + + +def test_sync_close_fallback_disconnect(): + db = object.__new__(SyncFalkorDB) + + # connection has no close(), only a connection_pool.disconnect() + mock_conn = SimpleNamespace() + mock_conn.connection_pool = SimpleNamespace(disconnect=Mock()) + db.connection = mock_conn + + db.close() + + mock_conn.connection_pool.disconnect.assert_called_once() + + +@pytest.mark.asyncio +async def test_async_aclose_and_context_manager(): + db = object.__new__(AsyncFalkorDB) + + mock_conn = SimpleNamespace(aclose=AsyncMock()) + db.connection = mock_conn + + # explicit aclose + await db.aclose() + mock_conn.aclose.assert_awaited_once() + + # async context manager should also await aclose + mock_conn.aclose.reset_mock() + async with db as d: + assert d is db + + mock_conn.aclose.assert_awaited_once() From db56499cc6ea9159a8dd2ab0d87a6a63bcee8a01 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Fri, 3 Oct 2025 11:36:20 +0300 Subject: [PATCH 5/5] fix test --- tests/test_close.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_close.py b/tests/test_close.py index 7382723..99af2a0 100644 --- a/tests/test_close.py +++ b/tests/test_close.py @@ -24,14 +24,16 @@ def test_sync_context_manager_calls_close(): def test_sync_close_fallback_disconnect(): db = object.__new__(SyncFalkorDB) - # connection has no close(), only a connection_pool.disconnect() - mock_conn = SimpleNamespace() + # connection provides close(); close() should be preferred over disconnect + mock_conn = SimpleNamespace(close=Mock()) mock_conn.connection_pool = SimpleNamespace(disconnect=Mock()) db.connection = mock_conn db.close() - mock_conn.connection_pool.disconnect.assert_called_once() + mock_conn.close.assert_called_once() + # ensure fallback wasn't used + mock_conn.connection_pool.disconnect.assert_not_called() @pytest.mark.asyncio