Skip to content

Commit 7b23a68

Browse files
Add context manager and close() to ClientBase (#121)
* Add context manager and close() to ClientBase Allows using CustomerIO and APIClient as context managers to properly close the underlying requests Session and avoid ResourceWarning for unclosed SSL sockets. Closes #87
1 parent 02c0e14 commit 7b23a68

2 files changed

Lines changed: 53 additions & 0 deletions

File tree

customerio/client_base.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ def __init__(self, retries=3, timeout=10, backoff_factor=0.02, use_connection_po
2424
self.use_connection_pooling = use_connection_pooling
2525
self._current_session = None
2626

27+
def __enter__(self):
28+
return self
29+
30+
def __exit__(self, *args):
31+
self.close()
32+
33+
def close(self):
34+
if self._current_session is not None:
35+
try:
36+
self._current_session.close()
37+
finally:
38+
self._current_session = None
39+
2740
@property
2841
def http(self):
2942
if self._current_session is None:

tests/test_client_base.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,46 @@ def build_session(resp=response):
134134
result = client.send_request("POST", "https://example.com", {})
135135
self.assertEqual(result.status_code, status)
136136

137+
def test_context_manager_closes_session(self):
138+
client = ClientBase(use_connection_pooling=True)
139+
session = FakeSession()
140+
client._build_session = lambda: session
141+
142+
with client:
143+
client.send_request("POST", "https://example.com", {})
144+
self.assertFalse(session.closed)
145+
146+
self.assertTrue(session.closed)
147+
self.assertIsNone(client._current_session)
148+
149+
def test_close_without_session(self):
150+
client = ClientBase()
151+
client.close()
152+
self.assertIsNone(client._current_session)
153+
154+
def test_close_resets_session(self):
155+
client = ClientBase(use_connection_pooling=True)
156+
session = FakeSession()
157+
client._build_session = lambda: session
158+
159+
client.send_request("POST", "https://example.com", {})
160+
self.assertIsNotNone(client._current_session)
161+
162+
client.close()
163+
self.assertTrue(session.closed)
164+
self.assertIsNone(client._current_session)
165+
166+
def test_close_resets_session_even_on_error(self):
167+
client = ClientBase(use_connection_pooling=True)
168+
session = FakeSession()
169+
session.close = lambda: (_ for _ in ()).throw(RuntimeError("close failed"))
170+
client._current_session = session
171+
172+
with self.assertRaises(RuntimeError):
173+
client.close()
174+
175+
self.assertIsNone(client._current_session)
176+
137177

138178
if __name__ == "__main__":
139179
unittest.main()

0 commit comments

Comments
 (0)