Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/basic_memory/cli/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def _send():
except Exception:
pass # Never break the CLI for analytics

# Non-daemon so the process waits for the request to complete.
# The 3s urllib timeout caps the worst-case exit delay.
t = threading.Thread(target=_send)
# Analytics must never keep a one-shot CLI process alive during interpreter
# shutdown. The request is best-effort and already has a bounded timeout.
t = threading.Thread(target=_send, daemon=True)
t.start()
7 changes: 4 additions & 3 deletions tests/cli/test_analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def test_sends_when_using_defaults(self, monkeypatch):
mock_thread.return_value = MagicMock()
track("test-event")
mock_thread.assert_called_once()
assert mock_thread.call_args.kwargs["daemon"] is True

def test_sends_event_when_configured(self, monkeypatch):
monkeypatch.delenv("BASIC_MEMORY_NO_PROMOS", raising=False)
Expand All @@ -76,7 +77,7 @@ def test_sends_event_when_configured(self, monkeypatch):

captured_target = None

def fake_thread(target):
def fake_thread(target, **kwargs):
nonlocal captured_target
captured_target = target
mock = MagicMock()
Expand All @@ -103,7 +104,7 @@ def fake_urlopen(req, timeout=None):
with patch("basic_memory.cli.analytics.urllib.request.urlopen", fake_urlopen):
with patch("basic_memory.cli.analytics.threading.Thread") as mock_thread:
# Capture the target function and call it directly
def run_target(target):
def run_target(target, **kwargs):
target() # Execute synchronously
return MagicMock()

Expand All @@ -130,7 +131,7 @@ def fake_urlopen(req, timeout=None):
with patch("basic_memory.cli.analytics.urllib.request.urlopen", fake_urlopen):
with patch("basic_memory.cli.analytics.threading.Thread") as mock_thread:

def run_target(target):
def run_target(target, **kwargs):
target() # Should not raise
return MagicMock()

Expand Down
Loading