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

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

29 statements  

1# event/api.py 

2# Copyright (C) 2005-2024 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 def unique_constraint_name(const, table): 

55 const.name = "uq_%s_%s" % ( 

56 table.name, 

57 list(const.columns)[0].name 

58 ) 

59 event.listen( 

60 UniqueConstraint, 

61 "after_parent_attach", 

62 unique_constraint_name) 

63 

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

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

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

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

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

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

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

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

72 

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

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

75 dictionary. 

76 See :ref:`event_named_argument_styles`. 

77 

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

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

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

81 listener function; associating an arbitrarily high number of listeners 

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

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

84 

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

86 with ORM instrumentation and mapping events. 

87 See :class:`_ormevent.MapperEvents` and 

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

89 

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

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

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

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

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

95 necessary for a return value to be processed. 

96 

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

98 include :class:`_events.ConnectionEvents` and 

99 :class:`_ormevent.AttributeEvents`. 

100 

101 .. note:: 

102 

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

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

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

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

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

108 that can't be changed during iteration. 

109 

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

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

112 systems that need to quickly associate and deassociate with 

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

114 from inside of a single listener. 

115 

116 .. seealso:: 

117 

118 :func:`.listens_for` 

119 

120 :func:`.remove` 

121 

122 """ 

123 

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

125 

126 

127def listens_for( 

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

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

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

131 

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

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

134 

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

136 

137 e.g.:: 

138 

139 from sqlalchemy import event 

140 from sqlalchemy.schema import UniqueConstraint 

141 

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

143 def unique_constraint_name(const, table): 

144 const.name = "uq_%s_%s" % ( 

145 table.name, 

146 list(const.columns)[0].name 

147 ) 

148 

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

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

151 

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

153 def on_config(): 

154 do_config() 

155 

156 

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

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

159 listener entry will remain associated with the target object. 

160 Associating an arbitrarily high number of listeners without explicitly 

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

162 is specified. 

163 

164 .. seealso:: 

165 

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

167 

168 """ 

169 

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

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

172 return fn 

173 

174 return decorate 

175 

176 

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

178 """Remove an event listener. 

179 

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

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

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

183 arguments. 

184 

185 e.g.:: 

186 

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

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

189 def my_listener_function(*arg): 

190 pass 

191 

192 # ... it's removed like this 

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

194 

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

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

197 function will revert all of these operations. 

198 

199 .. note:: 

200 

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

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

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

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

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

206 that can't be changed during iteration. 

207 

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

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

210 systems that need to quickly associate and deassociate with 

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

212 from inside of a single listener. 

213 

214 .. seealso:: 

215 

216 :func:`.listen` 

217 

218 """ 

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

220 

221 

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

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

224 

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