Skip to content

Commit 92a6623

Browse files
committed
Add tests for connection closure listeners
1 parent 3a637f1 commit 92a6623

File tree

3 files changed

+66
-11
lines changed

3 files changed

+66
-11
lines changed

asyncpg/_testbase/fuzzer.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ def _close_connection(self, connection):
145145
if conn_task is not None:
146146
conn_task.cancel()
147147

148+
def close_all_connections(self):
149+
for conn in list(self.connections):
150+
self.loop.call_soon_threadsafe(self._close_connection, conn)
151+
148152

149153
class Connection:
150154
def __init__(self, client_sock, backend_sock, proxy):
@@ -215,10 +219,11 @@ async def _read(self, sock, n):
215219
else:
216220
return read_task.result()
217221
finally:
218-
if not read_task.done():
219-
read_task.cancel()
220-
if not conn_event_task.done():
221-
conn_event_task.cancel()
222+
if not self.loop.is_closed():
223+
if not read_task.done():
224+
read_task.cancel()
225+
if not conn_event_task.done():
226+
conn_event_task.cancel()
222227

223228
async def _write(self, sock, data):
224229
write_task = asyncio.ensure_future(
@@ -236,10 +241,11 @@ async def _write(self, sock, data):
236241
else:
237242
return write_task.result()
238243
finally:
239-
if not write_task.done():
240-
write_task.cancel()
241-
if not conn_event_task.done():
242-
conn_event_task.cancel()
244+
if not self.loop.is_closed():
245+
if not write_task.done():
246+
write_task.cancel()
247+
if not conn_event_task.done():
248+
conn_event_task.cancel()
243249

244250
async def proxy_to_backend(self):
245251
buf = None
@@ -264,7 +270,8 @@ async def proxy_to_backend(self):
264270
pass
265271

266272
finally:
267-
self.loop.call_soon(self.close)
273+
if not self.loop.is_closed():
274+
self.loop.call_soon(self.close)
268275

269276
async def proxy_from_backend(self):
270277
buf = None
@@ -289,4 +296,5 @@ async def proxy_from_backend(self):
289296
pass
290297

291298
finally:
292-
self.loop.call_soon(self.close)
299+
if not self.loop.is_closed():
300+
self.loop.call_soon(self.close)

asyncpg/connection.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,16 @@ def add_close_listener(self, callback):
185185
:param callable callback:
186186
A callable receiving one argument:
187187
**connection**: a Connection the callback is registered with.
188+
189+
.. versionadded:: 0.21.0
188190
"""
189191
self._close_listeners.add(callback)
190192

191193
def remove_close_listener(self, callback):
192-
"""Remove a listening callback for the connection closing."""
194+
"""Remove a listening callback for the connection closing.
195+
196+
.. versionadded:: 0.21.0
197+
"""
193198
self._close_listeners.discard(callback)
194199

195200
def get_server_pid(self):

tests/test_listeners.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77

88
import asyncio
9+
import os
10+
import platform
11+
import sys
12+
import unittest
913

1014
from asyncpg import _testbase as tb
1115
from asyncpg import exceptions
@@ -272,3 +276,41 @@ def listener1(*args):
272276
pass
273277

274278
con.add_log_listener(listener1)
279+
280+
281+
@unittest.skipIf(os.environ.get('PGHOST'), 'using remote cluster for testing')
282+
@unittest.skipIf(
283+
platform.system() == 'Windows' and
284+
sys.version_info >= (3, 8),
285+
'not compatible with ProactorEventLoop which is default in Python 3.8')
286+
class TestConnectionCloseListener(tb.ProxiedClusterTestCase):
287+
288+
async def test_connection_close_callback_called_on_remote(self):
289+
290+
called = False
291+
292+
def close_cb(con):
293+
nonlocal called
294+
called = True
295+
296+
con = await self.connect()
297+
con.add_close_listener(close_cb)
298+
self.proxy.close_all_connections()
299+
try:
300+
await con.fetchval('SELECT 1')
301+
except Exception:
302+
pass
303+
self.assertTrue(called)
304+
305+
async def test_connection_close_callback_called_on_local(self):
306+
307+
called = False
308+
309+
def close_cb(con):
310+
nonlocal called
311+
called = True
312+
313+
con = await self.connect()
314+
con.add_close_listener(close_cb)
315+
await con.close()
316+
self.assertTrue(called)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy