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.""" 

9from __future__ import annotations 

10 

11from typing import Any 

12from typing import Callable 

13 

14from .base import _registrars 

15from .registry import _ET 

16from .registry import _EventKey 

17from .registry import _ListenerFnType 

18from .. import exc 

19from .. import util 

20 

21 

22CANCEL = util.symbol("CANCEL") 

23NO_RETVAL = util.symbol("NO_RETVAL") 

24 

25 

26def _event_key( 

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

28) -> _EventKey[_ET]: 

29 for evt_cls in _registrars[identifier]: 

30 tgt = evt_cls._accept_with(target, identifier) 

31 if tgt is not None: 

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

33 else: 

34 raise exc.InvalidRequestError( 

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

36 ) 

37 

38 

39def listen( 

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

41) -> None: 

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

43 

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

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

46 

47 e.g.:: 

48 

49 from sqlalchemy import event 

50 from sqlalchemy.schema import UniqueConstraint 

51 

52 

53 def unique_constraint_name(const, table): 

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

55 

56 

57 event.listen( 

58 UniqueConstraint, "after_parent_attach", unique_constraint_name 

59 ) 

60 

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

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

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

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

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

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

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

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

69 

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

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

72 dictionary. 

73 See :ref:`event_named_argument_styles`. 

74 

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

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

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

78 listener function; associating an arbitrarily high number of listeners 

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

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

81 

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

83 with ORM instrumentation and mapping events. 

84 See :class:`_ormevent.MapperEvents` and 

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

86 

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

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

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

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

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

92 necessary for a return value to be processed. 

93 

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

95 include :class:`_events.ConnectionEvents` and 

96 :class:`_ormevent.AttributeEvents`. 

97 

98 .. note:: 

99 

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

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

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

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

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

105 that can't be changed during iteration. 

106 

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

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

109 systems that need to quickly associate and deassociate with 

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

111 from inside of a single listener. 

112 

113 .. seealso:: 

114 

115 :func:`.listens_for` 

116 

117 :func:`.remove` 

118 

119 """ 

120 

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

122 

123 

124def listens_for( 

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

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

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

128 

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

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

131 

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

133 

134 e.g.:: 

135 

136 from sqlalchemy import event 

137 from sqlalchemy.schema import UniqueConstraint 

138 

139 

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

141 def unique_constraint_name(const, table): 

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

143 

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

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

146 

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

148 def on_config(): 

149 do_config() 

150 

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

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

153 listener entry will remain associated with the target object. 

154 Associating an arbitrarily high number of listeners without explicitly 

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

156 is specified. 

157 

158 .. seealso:: 

159 

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

161 

162 """ 

163 

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

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

166 return fn 

167 

168 return decorate 

169 

170 

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

172 """Remove an event listener. 

173 

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

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

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

177 arguments. 

178 

179 e.g.:: 

180 

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

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

183 def my_listener_function(*arg): 

184 pass 

185 

186 

187 # ... it's removed like this 

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

189 

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

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

192 function will revert all of these operations. 

193 

194 .. note:: 

195 

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

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

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

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

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

201 that can't be changed during iteration. 

202 

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

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

205 systems that need to quickly associate and deassociate with 

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

207 from inside of a single listener. 

208 

209 .. seealso:: 

210 

211 :func:`.listen` 

212 

213 """ 

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

215 

216 

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

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

219 

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