docs/narr/hooks.rst | ●●●●● patch | view | raw | blame | history | |
pyramid/config.py | ●●●●● patch | view | raw | blame | history | |
pyramid/tests/test_config.py | ●●●●● patch | view | raw | blame | history | |
pyramid/tests/test_tweens.py | ●●●●● patch | view | raw | blame | history | |
pyramid/tweens.py | ●●●●● patch | view | raw | blame | history |
docs/narr/hooks.rst
@@ -949,6 +949,10 @@ - One of the constants :attr:`pyramid.tweens.MAIN`, :attr:`pyramid.tweens.INGRESS`, or :attr:`pyramid.tweens.EXCVIEW`. - An iterable of any combination of the above. This allows the user to specify fallbacks if the desired tween is not included, as well as compatibility with multiple other tweens. Effectively, ``under`` means "closer to the main Pyramid application than", ``over`` means "closer to the request ingress than". @@ -1000,10 +1004,14 @@ Specifying neither ``over`` nor ``under`` is equivalent to specifying ``under=INGRESS``. If an ``under`` or ``over`` value is provided that does not match a tween factory dotted name or alias in the current configuration, that value will be ignored. It is not an error to provide an ``under`` or ``over`` value that matches an unused tween factory. If all options for ``under`` (or ``over``) cannot be found in the current configuration, it is an error. If some options are specified purely for compatibilty with other tweens, just add a fallback of MAIN or INGRESS. For example, ``under=('someothertween', 'someothertween2', INGRESS)``. This constraint will require the tween to be located under both the 'someothertween' tween, the 'someothertween2' tween, and INGRESS. If any of these is not in the current configuration, this constraint will only organize itself based on the tweens that are present. :meth:`~pyramid.config.Configurator.add_tween` also accepts an ``alias`` argument. If ``alias`` is not ``None``, should be a string. The string will pyramid/config.py
@@ -978,8 +978,8 @@ The ``under`` and ``over`` arguments allow the caller of ``add_tween`` to provide a hint about where in the tween chain this tween factory should be placed when an implicit tween chain is used. These hints are only used used when an explicit tween chain is not used (when the ``pyramid.tweens`` configuration value is not set). These hints are only used when an explicit tween chain is not used (when the ``pyramid.tweens`` configuration value is not set). Allowable values for ``under`` or ``over`` (or both) are: - ``None`` (the default). @@ -994,6 +994,10 @@ - One of the constants :attr:`pyramid.tweens.MAIN`, :attr:`pyramid.tweens.INGRESS`, or :attr:`pyramid.tweens.EXCVIEW`. - An iterable of any combination of the above. This allows the user to specify fallbacks if the desired tween is not included, as well as compatibility with multiple other tweens. ``under`` means 'closer to the main Pyramid application than', ``over`` means 'closer to the request ingress than'. @@ -1007,10 +1011,15 @@ fictional) 'someothertween' tween factory (which was presumably added via ``add_tween(factory, alias='someothertween')``). If an ``under`` or ``over`` value is provided that does not match a tween factory dotted name or alias in the current configuration, that value will be ignored. It is not an error to provide an ``under`` or ``over`` value that matches an unused tween factory. If all options for ``under`` (or ``over``) cannot be found in the current configuration, it is an error. If some options are specified purely for compatibilty with other tweens, just add a fallback of MAIN or INGRESS. For example, ``under=('someothertween', 'someothertween2', INGRESS)``. This constraint will require the tween to be located under both the 'someothertween' tween, the 'someothertween2' tween, and INGRESS. If any of these is not in the current configuration, this constraint will only organize itself based on the tweens that are present. Specifying neither ``over`` nor ``under`` is equivalent to specifying ``under=INGRESS``. @@ -1040,10 +1049,10 @@ if alias in (MAIN, INGRESS): raise ConfigurationError('%s is a reserved tween name' % alias) if over is INGRESS: if over is INGRESS or hasattr(over, '__iter__') and INGRESS in over: raise ConfigurationError('%s cannot be over INGRESS' % name) if under is MAIN: if under is MAIN or hasattr(under, '__iter__') and MAIN in under: raise ConfigurationError('%s cannot be under MAIN' % name) registry = self.registry pyramid/tests/test_config.py
@@ -763,6 +763,14 @@ config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory', over=INGRESS) def test_add_tween_over_ingress_iterable(self): from pyramid.exceptions import ConfigurationError from pyramid.tweens import INGRESS config = self._makeOne() self.assertRaises(ConfigurationError, config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory', over=('a', INGRESS)) def test_add_tween_under_main(self): from pyramid.exceptions import ConfigurationError from pyramid.tweens import MAIN @@ -771,6 +779,14 @@ config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory', under=MAIN) def test_add_tween_under_main_iterable(self): from pyramid.exceptions import ConfigurationError from pyramid.tweens import MAIN config = self._makeOne() self.assertRaises(ConfigurationError, config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory', under=('a', MAIN)) def test_add_subscriber_defaults(self): from zope.interface import implements from zope.interface import Interface pyramid/tests/test_tweens.py
@@ -2,7 +2,7 @@ class TestTweens(unittest.TestCase): def _makeOne(self): from pyramid.config import Tweens from pyramid.tweens import Tweens return Tweens() def test_add_explicit(self): @@ -23,7 +23,6 @@ self.assertEqual(tweens.alias_to_name['name'], 'name') self.assertEqual(tweens.name_to_alias['name'], 'name') self.assertEqual(tweens.order, [(INGRESS, 'name')]) self.assertEqual(tweens.ingress_alias_names, ['name']) tweens.add_implicit('name2', 'factory2') self.assertEqual(tweens.names, ['name', 'name2']) self.assertEqual(tweens.factories, @@ -32,7 +31,6 @@ self.assertEqual(tweens.name_to_alias['name2'], 'name2') self.assertEqual(tweens.order, [(INGRESS, 'name'), (INGRESS, 'name2')]) self.assertEqual(tweens.ingress_alias_names, ['name', 'name2']) tweens.add_implicit('name3', 'factory3', over='name2') self.assertEqual(tweens.names, ['name', 'name2', 'name3']) @@ -44,7 +42,6 @@ self.assertEqual(tweens.order, [(INGRESS, 'name'), (INGRESS, 'name2'), ('name3', 'name2')]) self.assertEqual(tweens.ingress_alias_names, ['name', 'name2']) def test_add_implicit_withaliases(self): from pyramid.tweens import INGRESS @@ -56,7 +53,6 @@ self.assertEqual(tweens.alias_to_name['n1'], 'name1') self.assertEqual(tweens.name_to_alias['name1'], 'n1') self.assertEqual(tweens.order, [(INGRESS, 'n1')]) self.assertEqual(tweens.ingress_alias_names, ['n1']) tweens.add_implicit('name2', 'factory2', alias='n2') self.assertEqual(tweens.names, ['name1', 'name2']) self.assertEqual(tweens.factories, @@ -65,7 +61,6 @@ self.assertEqual(tweens.name_to_alias['name2'], 'n2') self.assertEqual(tweens.order, [(INGRESS, 'n1'), (INGRESS, 'n2')]) self.assertEqual(tweens.ingress_alias_names, ['n1', 'n2']) tweens.add_implicit('name3', 'factory3', alias='n3', over='name2') self.assertEqual(tweens.names, ['name1', 'name2', 'name3']) @@ -77,7 +72,6 @@ self.assertEqual(tweens.order, [(INGRESS, 'n1'), (INGRESS, 'n2'), ('n3', 'name2')]) self.assertEqual(tweens.ingress_alias_names, ['n1', 'n2']) def test___call___explicit(self): tweens = self._makeOne() @@ -89,6 +83,7 @@ self.assertEqual(tweens(None, None), '123') def test___call___implicit(self): from pyramid.tweens import INGRESS tweens = self._makeOne() def factory1(handler, registry): return handler @@ -97,10 +92,13 @@ tweens.names = ['name', 'name2'] tweens.alias_to_name = {'name':'name', 'name2':'name2'} tweens.name_to_alias = {'name':'name', 'name2':'name2'} tweens.req_under = set(['name', 'name2']) tweens.order = [(INGRESS, 'name'), (INGRESS, 'name2')] tweens.factories = {'name':factory1, 'name2':factory2} self.assertEqual(tweens(None, None), '123') def test___call___implicit_with_aliasnames_different_than_names(self): from pyramid.tweens import INGRESS tweens = self._makeOne() def factory1(handler, registry): return handler @@ -109,6 +107,8 @@ tweens.names = ['name', 'name2'] tweens.alias_to_name = {'foo1':'name', 'foo2':'name2'} tweens.name_to_alias = {'name':'foo1', 'name2':'foo2'} tweens.req_under = set(['foo1', 'foo2']) tweens.order = [(INGRESS, 'name'), (INGRESS, 'name2')] tweens.factories = {'name':factory1, 'name2':factory2} self.assertEqual(tweens(None, None), '123') @@ -233,13 +233,44 @@ ('txnmgr', 'txnmgr_factory'), ]) def test_implicit_ordering_missing_partial(self): def test_implicit_ordering_missing_over_partial(self): from pyramid.exceptions import ConfigurationError tweens = self._makeOne() add = tweens.add_implicit add('dbt', 'dbt_factory') add('auth', 'auth_factory', under='browserid') add('retry', 'retry_factory', over='txnmgr', under='exceptionview') add('browserid', 'browserid_factory') self.assertRaises(ConfigurationError, tweens.implicit) def test_implicit_ordering_missing_under_partial(self): from pyramid.exceptions import ConfigurationError tweens = self._makeOne() add = tweens.add_implicit add('dbt', 'dbt_factory') add('auth', 'auth_factory', under='txnmgr') add('retry', 'retry_factory', over='dbt', under='exceptionview') add('browserid', 'browserid_factory') self.assertRaises(ConfigurationError, tweens.implicit) def test_implicit_ordering_missing_over_and_under_partials(self): from pyramid.exceptions import ConfigurationError tweens = self._makeOne() add = tweens.add_implicit add('dbt', 'dbt_factory') add('auth', 'auth_factory', under='browserid') add('retry', 'retry_factory', over='foo', under='txnmgr') add('browserid', 'browserid_factory') self.assertRaises(ConfigurationError, tweens.implicit) def test_implicit_ordering_missing_over_partial_with_fallback(self): from pyramid.tweens import MAIN tweens = self._makeOne() add = tweens.add_implicit add('exceptionview', 'excview_factory', over=MAIN) add('auth', 'auth_factory', under='browserid') add('retry', 'retry_factory', over='txnmgr', under='exceptionview') add('retry', 'retry_factory', over=('txnmgr',MAIN), under='exceptionview') add('browserid', 'browserid_factory') add('dbt', 'dbt_factory') self.assertEqual(tweens.implicit(), @@ -251,31 +282,20 @@ ('retry', 'retry_factory'), ]) def test_implicit_ordering_missing_partial2(self): tweens = self._makeOne() add = tweens.add_implicit add('dbt', 'dbt_factory') add('auth', 'auth_factory', under='browserid') add('retry', 'retry_factory', over='txnmgr', under='exceptionview') add('browserid', 'browserid_factory') self.assertEqual(tweens.implicit(), [ ('retry', 'retry_factory'), ('browserid', 'browserid_factory'), ('auth', 'auth_factory'), ('dbt', 'dbt_factory'), ]) def test_implicit_ordering_missing_partial3(self): def test_implicit_ordering_missing_under_partial_with_fallback(self): from pyramid.tweens import MAIN tweens = self._makeOne() add = tweens.add_implicit add('exceptionview', 'excview_factory', over=MAIN) add('retry', 'retry_factory', over='txnmgr', under='exceptionview') add('auth', 'auth_factory', under=('txnmgr','browserid')) add('retry', 'retry_factory', under='exceptionview') add('browserid', 'browserid_factory') add('dbt', 'dbt_factory') self.assertEqual(tweens.implicit(), [ ('dbt', 'dbt_factory'), ('browserid', 'browserid_factory'), ('auth', 'auth_factory'), ('exceptionview', 'excview_factory'), ('retry', 'retry_factory'), ]) @@ -285,7 +305,7 @@ tweens = self._makeOne() add = tweens.add_implicit add('exceptionview', 'excview_factory', alias='e', over=MAIN) add('retry', 'retry_factory', over='txnmgr', under='e') add('retry', 'retry_factory', over=('txnmgr',MAIN), under='e') add('browserid', 'browserid_factory') self.assertEqual(tweens.implicit(), [ @@ -294,6 +314,44 @@ ('retry', 'retry_factory'), ]) def test_implicit_ordering_with_partial_fallbacks(self): from pyramid.tweens import MAIN tweens = self._makeOne() add = tweens.add_implicit add('exceptionview', 'excview_factory', alias='e', over=('b', MAIN)) add('retry', 'retry_factory', under='e') add('browserid', 'browserid_factory', over=('txnmgr', 'e')) self.assertEqual(tweens.implicit(), [ ('browserid', 'browserid_factory'), ('exceptionview', 'excview_factory'), ('retry', 'retry_factory'), ]) def test_implicit_ordering_with_multiple_matching_fallbacks(self): from pyramid.tweens import MAIN tweens = self._makeOne() add = tweens.add_implicit add('exceptionview', 'excview_factory', alias='e', over=MAIN) add('retry', 'retry_factory', under='e') add('browserid', 'browserid_factory', over=('retry', 'e')) self.assertEqual(tweens.implicit(), [ ('browserid', 'browserid_factory'), ('exceptionview', 'excview_factory'), ('retry', 'retry_factory'), ]) def test_implicit_ordering_with_missing_fallbacks(self): from pyramid.exceptions import ConfigurationError from pyramid.tweens import MAIN tweens = self._makeOne() add = tweens.add_implicit add('exceptionview', 'excview_factory', alias='e', over=MAIN) add('retry', 'retry_factory', under='e') add('browserid', 'browserid_factory', over=('txnmgr', 'auth')) self.assertRaises(ConfigurationError, tweens.implicit) def test_implicit_ordering_conflict_direct(self): from pyramid.tweens import CyclicDependencyError tweens = self._makeOne() pyramid/tweens.py
@@ -65,9 +65,10 @@ def __init__(self): self.explicit = [] self.names = [] self.req_over = set() self.req_under = set() self.factories = {} self.order = [] self.ingress_alias_names = [] self.alias_to_name = {INGRESS:INGRESS, MAIN:MAIN} self.name_to_alias = {INGRESS:INGRESS, MAIN:MAIN} @@ -83,19 +84,22 @@ self.factories[name] = factory if under is None and over is None: under = INGRESS self.ingress_alias_names.append(alias) if under is not None: self.order.append((under, alias)) if not hasattr(under, '__iter__'): under = (under,) self.order += [(u, alias) for u in under] self.req_under.add(alias) if over is not None: self.order.append((alias, over)) if not hasattr(over, '__iter__'): over = (over,) self.order += [(alias, o) for o in over] self.req_over.add(alias) def implicit(self): order = [(INGRESS, MAIN)] roots = [] graph = {} has_order = {} aliases = [INGRESS, MAIN] ingress_alias_names = self.ingress_alias_names[:] for name in self.names: aliases.append(self.name_to_alias[name]) @@ -117,27 +121,26 @@ if tonode in roots: roots.remove(tonode) # remove ordering information that mentions unknown names/aliases for pos, (first, second) in enumerate(order): has_first = first in aliases has_second = second in aliases if (not has_first) or (not has_second): order[pos] = None, None else: has_order[first] = has_order[second] = True for alias in aliases: # any alias that doesn't have an ordering after we detect all # nodes with orders should get an ordering relative to INGRESS, # as if it were added with no under or over in add_implicit if (not alias in has_order) and (alias not in (INGRESS, MAIN)): order.append((INGRESS, alias)) ingress_alias_names.append(alias) add_node(alias) has_over, has_under = set(), set() for a, b in order: if a is not None and b is not None: # deal with removed orders if a in aliases and b in aliases: # deal with missing dependencies add_arc(a, b) has_over.add(a) has_under.add(b) if not self.req_over.issubset(has_over): raise ConfigurationError( 'Detected tweens with no satisfied over dependencies: %s' % (', '.join(sorted(self.req_over - has_over))) ) if not self.req_under.issubset(has_under): raise ConfigurationError( 'Detected tweens with no satisfied under dependencies: %s' % (', '.join(sorted(self.req_under - has_under))) ) sorted_aliases = [] @@ -163,8 +166,8 @@ result = [] for alias in sorted_aliases: if alias not in (MAIN, INGRESS): name = self.alias_to_name.get(alias, alias) name = self.alias_to_name.get(alias, alias) if name in self.names: result.append((name, self.factories[name])) return result