@@ -41,7 +41,7 @@ class Connection(metaclass=ConnectionMeta):
41
41
'_stmt_cache' , '_stmts_to_close' , '_listeners' ,
42
42
'_server_version' , '_server_caps' , '_intro_query' ,
43
43
'_reset_query' , '_proxy' , '_stmt_exclusive_section' ,
44
- '_config' , '_params' , '_addr' )
44
+ '_config' , '_params' , '_addr' , '_notice_callbacks' )
45
45
46
46
def __init__ (self , protocol , transport , loop ,
47
47
addr : (str , int ) or str ,
@@ -69,6 +69,7 @@ def __init__(self, protocol, transport, loop,
69
69
self ._stmts_to_close = set ()
70
70
71
71
self ._listeners = {}
72
+ self ._notice_callbacks = set ()
72
73
73
74
settings = self ._protocol .get_settings ()
74
75
ver_string = settings .server_version
@@ -126,6 +127,26 @@ async def remove_listener(self, channel, callback):
126
127
del self ._listeners [channel ]
127
128
await self .fetch ('UNLISTEN {}' .format (channel ))
128
129
130
+ def add_notice_callback (self , callback ):
131
+ """Add a callback for Postgres notices (NOTICE, DEBUG, LOG etc.).
132
+
133
+ It will be called when asyncronous NoticeResponse is received
134
+ from the connection. Possible message types are: WARNING, NOTICE, DEBUG,
135
+ INFO, or LOG.
136
+
137
+ :param callable callback:
138
+ A callable receiving the following arguments:
139
+ **connection**: a Connection the callback is registered with;
140
+ **message**: the `exceptions.PostgresNotice` message.
141
+ """
142
+ if self .is_closed ():
143
+ raise exceptions .InterfaceError ('connection is closed' )
144
+ self ._notice_callbacks .add (callback )
145
+
146
+ def remove_notice_callback (self , callback ):
147
+ """Remove a callback for notices."""
148
+ self ._notice_callbacks .discard (callback )
149
+
129
150
def get_server_pid (self ):
130
151
"""Return the PID of the Postgres server the connection is bound to."""
131
152
return self ._protocol .get_server_pid ()
@@ -821,13 +842,15 @@ async def close(self):
821
842
self ._listeners = {}
822
843
self ._aborted = True
823
844
await self ._protocol .close ()
845
+ self ._notice_callbacks = set ()
824
846
825
847
def terminate (self ):
826
848
"""Terminate the connection without waiting for pending data."""
827
849
self ._mark_stmts_as_closed ()
828
850
self ._listeners = {}
829
851
self ._aborted = True
830
852
self ._protocol .abort ()
853
+ self ._notice_callbacks = set ()
831
854
832
855
async def reset (self ):
833
856
self ._check_open ()
@@ -909,6 +932,26 @@ async def cancel():
909
932
910
933
self ._loop .create_task (cancel ())
911
934
935
+ def _notice (self , message ):
936
+ if self ._proxy is None :
937
+ con_ref = self
938
+ else :
939
+ # See the comment in the `_notify` below.
940
+ con_ref = self ._proxy
941
+
942
+ for cb in self ._notice_callbacks :
943
+ self ._loop .call_soon (self ._call_notice_cb , cb , con_ref , message )
944
+
945
+ def _call_notice_cb (self , cb , con_ref , message ):
946
+ try :
947
+ cb (con_ref , message )
948
+ except Exception as ex :
949
+ self ._loop .call_exception_handler ({
950
+ 'message' : 'Unhandled exception in asyncpg notice message '
951
+ 'callback {!r}' .format (cb ),
952
+ 'exception' : ex
953
+ })
954
+
912
955
def _notify (self , pid , channel , payload ):
913
956
if channel not in self ._listeners :
914
957
return
0 commit comments