Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1from zope.interface import implementer 

2 

3from pyramid.interfaces import ITweens 

4 

5from pyramid.compat import string_types, is_nonstr_iter 

6 

7from pyramid.exceptions import ConfigurationError 

8 

9from pyramid.tweens import MAIN, INGRESS, EXCVIEW 

10 

11from pyramid.util import is_string_or_iterable, TopologicalSorter 

12 

13from pyramid.config.actions import action_method 

14 

15 

16class TweensConfiguratorMixin(object): 

17 def add_tween(self, tween_factory, under=None, over=None): 

18 """ 

19 .. versionadded:: 1.2 

20 

21 Add a 'tween factory'. A :term:`tween` (a contraction of 'between') 

22 is a bit of code that sits between the Pyramid router's main request 

23 handling function and the upstream WSGI component that uses 

24 :app:`Pyramid` as its 'app'. Tweens are a feature that may be used 

25 by Pyramid framework extensions, to provide, for example, 

26 Pyramid-specific view timing support, bookkeeping code that examines 

27 exceptions before they are returned to the upstream WSGI application, 

28 or a variety of other features. Tweens behave a bit like 

29 :term:`WSGI` 'middleware' but they have the benefit of running in a 

30 context in which they have access to the Pyramid :term:`application 

31 registry` as well as the Pyramid rendering machinery. 

32 

33 .. note:: You can view the tween ordering configured into a given 

34 Pyramid application by using the ``ptweens`` 

35 command. See :ref:`displaying_tweens`. 

36 

37 The ``tween_factory`` argument must be a :term:`dotted Python name` 

38 to a global object representing the tween factory. 

39 

40 The ``under`` and ``over`` arguments allow the caller of 

41 ``add_tween`` to provide a hint about where in the tween chain this 

42 tween factory should be placed when an implicit tween chain is used. 

43 These hints are only used when an explicit tween chain is not used 

44 (when the ``pyramid.tweens`` configuration value is not set). 

45 Allowable values for ``under`` or ``over`` (or both) are: 

46 

47 - ``None`` (the default). 

48 

49 - A :term:`dotted Python name` to a tween factory: a string 

50 representing the dotted name of a tween factory added in a call to 

51 ``add_tween`` in the same configuration session. 

52 

53 - One of the constants :attr:`pyramid.tweens.MAIN`, 

54 :attr:`pyramid.tweens.INGRESS`, or :attr:`pyramid.tweens.EXCVIEW`. 

55 

56 - An iterable of any combination of the above. This allows the user 

57 to specify fallbacks if the desired tween is not included, as well 

58 as compatibility with multiple other tweens. 

59 

60 ``under`` means 'closer to the main Pyramid application than', 

61 ``over`` means 'closer to the request ingress than'. 

62 

63 For example, calling ``add_tween('myapp.tfactory', 

64 over=pyramid.tweens.MAIN)`` will attempt to place the tween factory 

65 represented by the dotted name ``myapp.tfactory`` directly 'above' 

66 (in ``ptweens`` order) the main Pyramid request handler. 

67 Likewise, calling ``add_tween('myapp.tfactory', 

68 over=pyramid.tweens.MAIN, under='mypkg.someothertween')`` will 

69 attempt to place this tween factory 'above' the main handler but 

70 'below' (a fictional) 'mypkg.someothertween' tween factory. 

71 

72 If all options for ``under`` (or ``over``) cannot be found in the 

73 current configuration, it is an error. If some options are specified 

74 purely for compatibilty with other tweens, just add a fallback of 

75 MAIN or INGRESS. For example, ``under=('mypkg.someothertween', 

76 'mypkg.someothertween2', INGRESS)``. This constraint will require 

77 the tween to be located under both the 'mypkg.someothertween' tween, 

78 the 'mypkg.someothertween2' tween, and INGRESS. If any of these is 

79 not in the current configuration, this constraint will only organize 

80 itself based on the tweens that are present. 

81 

82 Specifying neither ``over`` nor ``under`` is equivalent to specifying 

83 ``under=INGRESS``. 

84 

85 Implicit tween ordering is obviously only best-effort. Pyramid will 

86 attempt to present an implicit order of tweens as best it can, but 

87 the only surefire way to get any particular ordering is to use an 

88 explicit tween order. A user may always override the implicit tween 

89 ordering by using an explicit ``pyramid.tweens`` configuration value 

90 setting. 

91 

92 ``under``, and ``over`` arguments are ignored when an explicit tween 

93 chain is specified using the ``pyramid.tweens`` configuration value. 

94 

95 For more information, see :ref:`registering_tweens`. 

96 

97 """ 

98 return self._add_tween( 

99 tween_factory, under=under, over=over, explicit=False 

100 ) 

101 

102 def add_default_tweens(self): 

103 self.add_tween(EXCVIEW) 

104 

105 @action_method 

106 def _add_tween(self, tween_factory, under=None, over=None, explicit=False): 

107 

108 if not isinstance(tween_factory, string_types): 

109 raise ConfigurationError( 

110 'The "tween_factory" argument to add_tween must be a ' 

111 'dotted name to a globally importable object, not %r' 

112 % tween_factory 

113 ) 

114 

115 name = tween_factory 

116 

117 if name in (MAIN, INGRESS): 

118 raise ConfigurationError('%s is a reserved tween name' % name) 

119 

120 tween_factory = self.maybe_dotted(tween_factory) 

121 

122 for t, p in [('over', over), ('under', under)]: 

123 if p is not None: 

124 if not is_string_or_iterable(p): 

125 raise ConfigurationError( 

126 '"%s" must be a string or iterable, not %s' % (t, p) 

127 ) 

128 

129 if over is INGRESS or is_nonstr_iter(over) and INGRESS in over: 

130 raise ConfigurationError('%s cannot be over INGRESS' % name) 

131 

132 if under is MAIN or is_nonstr_iter(under) and MAIN in under: 

133 raise ConfigurationError('%s cannot be under MAIN' % name) 

134 

135 registry = self.registry 

136 introspectables = [] 

137 

138 tweens = registry.queryUtility(ITweens) 

139 if tweens is None: 

140 tweens = Tweens() 

141 registry.registerUtility(tweens, ITweens) 

142 

143 def register(): 

144 if explicit: 

145 tweens.add_explicit(name, tween_factory) 

146 else: 

147 tweens.add_implicit( 

148 name, tween_factory, under=under, over=over 

149 ) 

150 

151 discriminator = ('tween', name, explicit) 

152 tween_type = explicit and 'explicit' or 'implicit' 

153 

154 intr = self.introspectable( 

155 'tweens', discriminator, name, '%s tween' % tween_type 

156 ) 

157 intr['name'] = name 

158 intr['factory'] = tween_factory 

159 intr['type'] = tween_type 

160 intr['under'] = under 

161 intr['over'] = over 

162 introspectables.append(intr) 

163 self.action(discriminator, register, introspectables=introspectables) 

164 

165 

166@implementer(ITweens) 

167class Tweens(object): 

168 def __init__(self): 

169 self.sorter = TopologicalSorter( 

170 default_before=None, 

171 default_after=INGRESS, 

172 first=INGRESS, 

173 last=MAIN, 

174 ) 

175 self.explicit = [] 

176 

177 def add_explicit(self, name, factory): 

178 self.explicit.append((name, factory)) 

179 

180 def add_implicit(self, name, factory, under=None, over=None): 

181 self.sorter.add(name, factory, after=under, before=over) 

182 

183 def implicit(self): 

184 return self.sorter.sorted() 

185 

186 def __call__(self, handler, registry): 

187 if self.explicit: 

188 use = self.explicit 

189 else: 

190 use = self.implicit() 

191 for name, factory in use[::-1]: 

192 handler = factory(handler, registry) 

193 return handler