Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/event/api.py: 90%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

30 statements  

1# event/api.py 

2# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors 

3# <see AUTHORS file> 

4# 

5# This module is part of SQLAlchemy and is released under 

6# the MIT License: https://www.opensource.org/licenses/mit-license.php 

7 

8"""Public API functions for the event system. 

9 

10""" 

11from __future__ import annotations 

12 

13from typing import Any 

14from typing import Callable 

15 

16from .base import _registrars 

17from .registry import _ET 

18from .registry import _EventKey 

19from .registry import _ListenerFnType 

20from .. import exc 

21from .. import util 

22 

23 

24CANCEL = util.symbol("CANCEL") 

25NO_RETVAL = util.symbol("NO_RETVAL") 

26 

27 

28def _event_key( 

29 target: _ET, identifier: str, fn: _ListenerFnType 

30) -> _EventKey[_ET]: 

31 for evt_cls in _registrars[identifier]: 

32 tgt = evt_cls._accept_with(target, identifier) 

33 if tgt is not None: 

34 return _EventKey(target, identifier, fn, tgt) 

35 else: 

36 raise exc.InvalidRequestError( 

37 "No such event '%s' for target '%s'" % (identifier, target) 

38 ) 

39 

40 

41def listen( 

42 target: Any, identifier: str, fn: Callable[..., Any], *args: Any, **kw: Any 

43) -> None: 

44 """Register a listener function for the given target. 

45 

46 The :func:`.listen` function is part of the primary interface for the 

47 SQLAlchemy event system, documented at :ref:`event_toplevel`. 

48 

49 e.g.:: 

50 

51 from sqlalchemy import event 

52 from sqlalchemy.schema import UniqueConstraint 

53 

54 

55 def unique_constraint_name(const, table): 

56 const.name = "uq_%s_%s" % (table.name, list(const.columns)[0].name) 

57 

58 

59 event.listen( 

60 UniqueConstraint, "after_parent_attach", unique_constraint_name 

61 ) 

62 

63 :param bool insert: The default behavior for event handlers is to append 

64 the decorated user defined function to an internal list of registered 

65 event listeners upon discovery. If a user registers a function with 

66 ``insert=True``, SQLAlchemy will insert (prepend) the function to the 

67 internal list upon discovery. This feature is not typically used or 

68 recommended by the SQLAlchemy maintainers, but is provided to ensure 

69 certain user defined functions can run before others, such as when 

70 :ref:`Changing the sql_mode in MySQL <mysql_sql_mode>`. 

71 

72 :param bool named: When using named argument passing, the names listed in 

73 the function argument specification will be used as keys in the 

74 dictionary. 

75 See :ref:`event_named_argument_styles`. 

76 

77 :param bool once: Private/Internal API usage. Deprecated. This parameter 

78 would provide that an event function would run only once per given 

79 target. It does not however imply automatic de-registration of the 

80 listener function; associating an arbitrarily high number of listeners 

81 without explicitly removing them will cause memory to grow unbounded even 

82 if ``once=True`` is specified. 

83 

84 :param bool propagate: The ``propagate`` kwarg is available when working 

85 with ORM instrumentation and mapping events. 

86 See :class:`_ormevent.MapperEvents` and 

87 :meth:`_ormevent.MapperEvents.before_mapper_configured` for examples. 

88 

89 :param bool retval: This flag applies only to specific event listeners, 

90 each of which includes documentation explaining when it should be used. 

91 By default, no listener ever requires a return value. 

92 However, some listeners do support special behaviors for return values, 

93 and include in their documentation that the ``retval=True`` flag is 

94 necessary for a return value to be processed. 

95 

96 Event listener suites that make use of :paramref:`_event.listen.retval` 

97 include :class:`_events.ConnectionEvents` and 

98 :class:`_ormevent.AttributeEvents`. 

99 

100 .. note:: 

101 

102 The :func:`.listen` function cannot be called at the same time 

103 that the target event is being run. This has implications 

104 for thread safety, and also means an event cannot be added 

105 from inside the listener function for itself. The list of 

106 events to be run are present inside of a mutable collection 

107 that can't be changed during iteration. 

108 

109 Event registration and removal is not intended to be a "high 

110 velocity" operation; it is a configurational operation. For 

111 systems that need to quickly associate and deassociate with 

112 events at high scale, use a mutable structure that is handled 

113 from inside of a single listener. 

114 

115 .. seealso:: 

116 

117 :func:`.listens_for` 

118 

119 :func:`.remove` 

120 

121 """ 

122 

123 _event_key(target, identifier, fn).listen(*args, **kw) 

124 

125 

126def listens_for( 

127 target: Any, identifier: str, *args: Any, **kw: Any 

128) -> Callable[[Callable[..., Any]], Callable[..., Any]]: 

129 """Decorate a function as a listener for the given target + identifier. 

130 

131 The :func:`.listens_for` decorator is part of the primary interface for the 

132 SQLAlchemy event system, documented at :ref:`event_toplevel`. 

133 

134 This function generally shares the same kwargs as :func:`.listen`. 

135 

136 e.g.:: 

137 

138 from sqlalchemy import event 

139 from sqlalchemy.schema import UniqueConstraint 

140 

141 

142 @event.listens_for(UniqueConstraint, "after_parent_attach") 

143 def unique_constraint_name(const, table): 

144 const.name = "uq_%s_%s" % (table.name, list(const.columns)[0].name) 

145 

146 A given function can also be invoked for only the first invocation 

147 of the event using the ``once`` argument:: 

148 

149 @event.listens_for(Mapper, "before_configure", once=True) 

150 def on_config(): 

151 do_config() 

152 

153 .. warning:: The ``once`` argument does not imply automatic de-registration 

154 of the listener function after it has been invoked a first time; a 

155 listener entry will remain associated with the target object. 

156 Associating an arbitrarily high number of listeners without explicitly 

157 removing them will cause memory to grow unbounded even if ``once=True`` 

158 is specified. 

159 

160 .. seealso:: 

161 

162 :func:`.listen` - general description of event listening 

163 

164 """ 

165 

166 def decorate(fn: Callable[..., Any]) -> Callable[..., Any]: 

167 listen(target, identifier, fn, *args, **kw) 

168 return fn 

169 

170 return decorate 

171 

172 

173def remove(target: Any, identifier: str, fn: Callable[..., Any]) -> None: 

174 """Remove an event listener. 

175 

176 The arguments here should match exactly those which were sent to 

177 :func:`.listen`; all the event registration which proceeded as a result 

178 of this call will be reverted by calling :func:`.remove` with the same 

179 arguments. 

180 

181 e.g.:: 

182 

183 # if a function was registered like this... 

184 @event.listens_for(SomeMappedClass, "before_insert", propagate=True) 

185 def my_listener_function(*arg): 

186 pass 

187 

188 

189 # ... it's removed like this 

190 event.remove(SomeMappedClass, "before_insert", my_listener_function) 

191 

192 Above, the listener function associated with ``SomeMappedClass`` was also 

193 propagated to subclasses of ``SomeMappedClass``; the :func:`.remove` 

194 function will revert all of these operations. 

195 

196 .. note:: 

197 

198 The :func:`.remove` function cannot be called at the same time 

199 that the target event is being run. This has implications 

200 for thread safety, and also means an event cannot be removed 

201 from inside the listener function for itself. The list of 

202 events to be run are present inside of a mutable collection 

203 that can't be changed during iteration. 

204 

205 Event registration and removal is not intended to be a "high 

206 velocity" operation; it is a configurational operation. For 

207 systems that need to quickly associate and deassociate with 

208 events at high scale, use a mutable structure that is handled 

209 from inside of a single listener. 

210 

211 .. seealso:: 

212 

213 :func:`.listen` 

214 

215 """ 

216 _event_key(target, identifier, fn).remove() 

217 

218 

219def contains(target: Any, identifier: str, fn: Callable[..., Any]) -> bool: 

220 """Return True if the given target/ident/fn is set up to listen.""" 

221 

222 return _event_key(target, identifier, fn).contains()