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

25 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

1# event/api.py 

2# Copyright (C) 2005-2022 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 absolute_import 

12 

13from .base import _registrars 

14from .registry import _EventKey 

15from .. import exc 

16from .. import util 

17 

18 

19CANCEL = util.symbol("CANCEL") 

20NO_RETVAL = util.symbol("NO_RETVAL") 

21 

22 

23def _event_key(target, identifier, fn): 

24 for evt_cls in _registrars[identifier]: 

25 tgt = evt_cls._accept_with(target) 

26 if tgt is not None: 

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

28 else: 

29 raise exc.InvalidRequestError( 

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

31 ) 

32 

33 

34def listen(target, identifier, fn, *args, **kw): 

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

36 

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

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

39 

40 e.g.:: 

41 

42 from sqlalchemy import event 

43 from sqlalchemy.schema import UniqueConstraint 

44 

45 def unique_constraint_name(const, table): 

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

47 table.name, 

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

49 ) 

50 event.listen( 

51 UniqueConstraint, 

52 "after_parent_attach", 

53 unique_constraint_name) 

54 

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

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

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

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

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

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

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

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

63 

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

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

66 dictionary. 

67 See :ref:`event_named_argument_styles`. 

68 

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

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

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

72 listener function; associating an arbitrarily high number of listeners 

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

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

75 

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

77 with ORM instrumentation and mapping events. 

78 See :class:`_ormevent.MapperEvents` and 

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

80 

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

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

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

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

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

86 necessary for a return value to be processed. 

87 

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

89 include :class:`_events.ConnectionEvents` and 

90 :class:`_ormevent.AttributeEvents`. 

91 

92 .. note:: 

93 

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

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

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

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

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

99 that can't be changed during iteration. 

100 

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

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

103 systems that need to quickly associate and deassociate with 

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

105 from inside of a single listener. 

106 

107 .. seealso:: 

108 

109 :func:`.listens_for` 

110 

111 :func:`.remove` 

112 

113 """ 

114 

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

116 

117 

118def listens_for(target, identifier, *args, **kw): 

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

120 

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

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

123 

124 This function generally shares the same kwargs as :func:`.listens`. 

125 

126 e.g.:: 

127 

128 from sqlalchemy import event 

129 from sqlalchemy.schema import UniqueConstraint 

130 

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

132 def unique_constraint_name(const, table): 

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

134 table.name, 

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

136 ) 

137 

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

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

140 

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

142 def on_config(): 

143 do_config() 

144 

145 

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

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

148 listener entry will remain associated with the target object. 

149 Associating an arbitrarily high number of listeners without explicitly 

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

151 is specified. 

152 

153 .. seealso:: 

154 

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

156 

157 """ 

158 

159 def decorate(fn): 

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

161 return fn 

162 

163 return decorate 

164 

165 

166def remove(target, identifier, fn): 

167 """Remove an event listener. 

168 

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

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

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

172 arguments. 

173 

174 e.g.:: 

175 

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

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

178 def my_listener_function(*arg): 

179 pass 

180 

181 # ... it's removed like this 

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

183 

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

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

186 function will revert all of these operations. 

187 

188 .. note:: 

189 

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

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

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

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

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

195 that can't be changed during iteration. 

196 

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

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

199 systems that need to quickly associate and deassociate with 

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

201 from inside of a single listener. 

202 

203 .. versionchanged:: 1.0.0 - a ``collections.deque()`` object is now 

204 used as the container for the list of events, which explicitly 

205 disallows collection mutation while the collection is being 

206 iterated. 

207 

208 .. seealso:: 

209 

210 :func:`.listen` 

211 

212 """ 

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

214 

215 

216def contains(target, identifier, fn): 

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

218 

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