Michael Merickel
2011-08-12 c45737397842359010b3d517a1d5f9f34c6d1e53
Added support for specifying tuples in over/under.

This change means that errors are now raised if no tween is found from
either an over or an under specification.
3 files modified
152 ■■■■ changed files
pyramid/config.py 3 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_tweens.py 106 ●●●● patch | view | raw | blame | history
pyramid/tweens.py 43 ●●●●● patch | view | raw | blame | history
pyramid/config.py
@@ -995,6 +995,9 @@
        
        - One of the constants :attr:`pyramid.tweens.MAIN`,
          :attr:`pyramid.tweens.INGRESS`, or :attr:`pyramid.tweens.EXCVIEW`.
        - A tuple of any combination of the above. This allows the user
          to specify fallbacks if the desired tween is not included.
        
        ``under`` means 'closer to the main Pyramid application than',
        ``over`` means 'closer to the request ingress than'.
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):
@@ -83,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
@@ -91,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
@@ -103,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')
@@ -227,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(),
@@ -245,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'),
                             ])
@@ -279,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(),
                         [
@@ -288,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,6 +65,8 @@
    def __init__(self):
        self.explicit = []
        self.names = []
        self.req_over = set()
        self.req_under = set()
        self.factories = {}
        self.order = []
        self.alias_to_name = {INGRESS:INGRESS, MAIN:MAIN}
@@ -83,15 +85,20 @@
        if under is None and over is None:
            under = INGRESS
        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]
        for name in self.names:
@@ -114,26 +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))
            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 = []