From b397ac9cfb2f28ccda9c5779ffdce50a22f99d3c Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 20 Aug 2011 19:02:45 -0400 Subject: move configuration bits of tween stuff into config/tweens --- pyramid/config/tweens.py | 139 ++++++++++- pyramid/tests/test_config/test_tweens.py | 382 +++++++++++++++++++++++++++++++ pyramid/tests/test_tweens.py | 382 ------------------------------- pyramid/tweens.py | 139 ----------- 4 files changed, 520 insertions(+), 522 deletions(-) create mode 100644 pyramid/tests/test_config/test_tweens.py delete mode 100644 pyramid/tests/test_tweens.py diff --git a/pyramid/config/tweens.py b/pyramid/config/tweens.py index 83067f0fe..46238b136 100644 --- a/pyramid/config/tweens.py +++ b/pyramid/config/tweens.py @@ -1,8 +1,9 @@ +from zope.interface import implements + from pyramid.interfaces import ITweens from pyramid.exceptions import ConfigurationError from pyramid.tweens import excview_tween_factory -from pyramid.tweens import Tweens from pyramid.tweens import MAIN, INGRESS, EXCVIEW from pyramid.config.util import action_method @@ -154,3 +155,139 @@ class TweensConfiguratorMixin(object): if not explicit and alias is not None: self.action(('tween', alias, explicit)) + +class CyclicDependencyError(Exception): + def __init__(self, cycles): + self.cycles = cycles + + def __str__(self): + L = [] + cycles = self.cycles + for cycle in cycles: + dependent = cycle + dependees = cycles[cycle] + L.append('%r sorts over %r' % (dependent, dependees)) + msg = 'Implicit tween ordering cycle:' + '; '.join(L) + return msg + +class Tweens(object): + implements(ITweens) + 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} + self.name_to_alias = {INGRESS:INGRESS, MAIN:MAIN} + + def add_explicit(self, name, factory): + self.explicit.append((name, factory)) + + def add_implicit(self, name, factory, alias=None, under=None, over=None): + if alias is None: + alias = name + self.alias_to_name[alias] = name + self.name_to_alias[name] = alias + self.names.append(name) + self.factories[name] = factory + if under is None and over is None: + under = INGRESS + if under is not None: + 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: + 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 = {} + aliases = [INGRESS, MAIN] + + for name in self.names: + aliases.append(self.name_to_alias[name]) + + for a, b in self.order: + # try to convert both a and b to an alias + a = self.name_to_alias.get(a, a) + b = self.name_to_alias.get(b, b) + order.append((a, b)) + + def add_node(node): + if not graph.has_key(node): + roots.append(node) + graph[node] = [0] # 0 = number of arcs coming into this node + + def add_arc(fromnode, tonode): + graph[fromnode].append(tonode) + graph[tonode][0] += 1 + if tonode in roots: + roots.remove(tonode) + + for alias in aliases: + add_node(alias) + + has_over, has_under = set(), set() + for a, b in order: + 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 = [] + + while roots: + root = roots.pop(0) + sorted_aliases.append(root) + children = graph[root][1:] + for child in children: + arcs = graph[child][0] + arcs -= 1 + graph[child][0] = arcs + if arcs == 0: + roots.insert(0, child) + del graph[root] + + if graph: + # loop in input + cycledeps = {} + for k, v in graph.items(): + cycledeps[k] = v[1:] + raise CyclicDependencyError(cycledeps) + + result = [] + + for alias in sorted_aliases: + name = self.alias_to_name.get(alias, alias) + if name in self.names: + result.append((name, self.factories[name])) + + return result + + def __call__(self, handler, registry): + if self.explicit: + use = self.explicit + else: + use = self.implicit() + for name, factory in use[::-1]: + handler = factory(handler, registry) + return handler + diff --git a/pyramid/tests/test_config/test_tweens.py b/pyramid/tests/test_config/test_tweens.py new file mode 100644 index 000000000..c7098875e --- /dev/null +++ b/pyramid/tests/test_config/test_tweens.py @@ -0,0 +1,382 @@ +import unittest + +class TestTweens(unittest.TestCase): + def _makeOne(self): + from pyramid.config.tweens import Tweens + return Tweens() + + def test_add_explicit(self): + tweens = self._makeOne() + tweens.add_explicit('name', 'factory') + self.assertEqual(tweens.explicit, [('name', 'factory')]) + tweens.add_explicit('name2', 'factory2') + self.assertEqual(tweens.explicit, [('name', 'factory'), + ('name2', 'factory2')]) + + def test_add_implicit_noaliases(self): + from pyramid.tweens import INGRESS + tweens = self._makeOne() + tweens.add_implicit('name', 'factory') + self.assertEqual(tweens.names, ['name']) + self.assertEqual(tweens.factories, + {'name':'factory'}) + self.assertEqual(tweens.alias_to_name['name'], 'name') + self.assertEqual(tweens.name_to_alias['name'], 'name') + self.assertEqual(tweens.order, [(INGRESS, 'name')]) + tweens.add_implicit('name2', 'factory2') + self.assertEqual(tweens.names, ['name', 'name2']) + self.assertEqual(tweens.factories, + {'name':'factory', 'name2':'factory2'}) + self.assertEqual(tweens.alias_to_name['name2'], 'name2') + self.assertEqual(tweens.name_to_alias['name2'], 'name2') + self.assertEqual(tweens.order, + [(INGRESS, 'name'), (INGRESS, 'name2')]) + tweens.add_implicit('name3', 'factory3', over='name2') + self.assertEqual(tweens.names, + ['name', 'name2', 'name3']) + self.assertEqual(tweens.factories, + {'name':'factory', 'name2':'factory2', + 'name3':'factory3'}) + self.assertEqual(tweens.alias_to_name['name3'], 'name3') + self.assertEqual(tweens.name_to_alias['name3'], 'name3') + self.assertEqual(tweens.order, + [(INGRESS, 'name'), (INGRESS, 'name2'), + ('name3', 'name2')]) + + def test_add_implicit_withaliases(self): + from pyramid.tweens import INGRESS + tweens = self._makeOne() + tweens.add_implicit('name1', 'factory', alias='n1') + self.assertEqual(tweens.names, ['name1']) + self.assertEqual(tweens.factories, + {'name1':'factory'}) + self.assertEqual(tweens.alias_to_name['n1'], 'name1') + self.assertEqual(tweens.name_to_alias['name1'], 'n1') + self.assertEqual(tweens.order, [(INGRESS, 'n1')]) + tweens.add_implicit('name2', 'factory2', alias='n2') + self.assertEqual(tweens.names, ['name1', 'name2']) + self.assertEqual(tweens.factories, + {'name1':'factory', 'name2':'factory2'}) + self.assertEqual(tweens.alias_to_name['n2'], 'name2') + self.assertEqual(tweens.name_to_alias['name2'], 'n2') + self.assertEqual(tweens.order, + [(INGRESS, 'n1'), (INGRESS, 'n2')]) + tweens.add_implicit('name3', 'factory3', alias='n3', over='name2') + self.assertEqual(tweens.names, + ['name1', 'name2', 'name3']) + self.assertEqual(tweens.factories, + {'name1':'factory', 'name2':'factory2', + 'name3':'factory3'}) + self.assertEqual(tweens.alias_to_name['n3'], 'name3') + self.assertEqual(tweens.name_to_alias['name3'], 'n3') + self.assertEqual(tweens.order, + [(INGRESS, 'n1'), (INGRESS, 'n2'), + ('n3', 'name2')]) + + def test___call___explicit(self): + tweens = self._makeOne() + def factory1(handler, registry): + return handler + def factory2(handler, registry): + return '123' + tweens.explicit = [('name', factory1), ('name', factory2)] + 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 + def factory2(handler, registry): + return '123' + 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 + def factory2(handler, registry): + return '123' + 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') + + def test_implicit_ordering_1(self): + tweens = self._makeOne() + tweens.add_implicit('name1', 'factory1') + tweens.add_implicit('name2', 'factory2') + self.assertEqual(tweens.implicit(), + [ + ('name2', 'factory2'), + ('name1', 'factory1'), + ]) + + def test_implicit_ordering_2(self): + from pyramid.tweens import MAIN + tweens = self._makeOne() + tweens.add_implicit('name1', 'factory1') + tweens.add_implicit('name2', 'factory2', over=MAIN) + self.assertEqual(tweens.implicit(), + [ + ('name1', 'factory1'), + ('name2', 'factory2'), + ]) + + def test_implicit_ordering_3(self): + from pyramid.tweens import MAIN + tweens = self._makeOne() + add = tweens.add_implicit + add('auth', 'auth_factory', under='browserid') + add('dbt', 'dbt_factory') + add('retry', 'retry_factory', over='txnmgr', under='exceptionview') + add('browserid', 'browserid_factory') + add('txnmgr', 'txnmgr_factory', under='exceptionview') + add('exceptionview', 'excview_factory', over=MAIN) + self.assertEqual(tweens.implicit(), + [ + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('dbt', 'dbt_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ('txnmgr', 'txnmgr_factory'), + ]) + + def test_implicit_ordering_4(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('browserid', 'browserid_factory') + add('txnmgr', 'txnmgr_factory', under='exceptionview') + add('dbt', 'dbt_factory') + self.assertEqual(tweens.implicit(), + [ + ('dbt', 'dbt_factory'), + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ('txnmgr', 'txnmgr_factory'), + ]) + + def test_implicit_ordering_5(self): + from pyramid.tweens import MAIN, INGRESS + tweens = self._makeOne() + add = tweens.add_implicit + add('exceptionview', 'excview_factory', over=MAIN) + add('auth', 'auth_factory', under=INGRESS) + add('retry', 'retry_factory', over='txnmgr', under='exceptionview') + add('browserid', 'browserid_factory', under=INGRESS) + add('txnmgr', 'txnmgr_factory', under='exceptionview', over=MAIN) + add('dbt', 'dbt_factory') + self.assertEqual(tweens.implicit(), + [ + ('dbt', 'dbt_factory'), + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ('txnmgr', 'txnmgr_factory'), + ]) + + def test_implicit_ordering_withaliases(self): + from pyramid.tweens import MAIN + tweens = self._makeOne() + add = tweens.add_implicit + add('exceptionview', 'excview_factory', alias='e', over=MAIN) + add('auth', 'auth_factory', under='b') + add('retry', 'retry_factory', over='t', under='exceptionview') + add('browserid', 'browserid_factory', alias='b') + add('txnmgr', 'txnmgr_factory', alias='t', under='exceptionview') + add('dbt', 'dbt_factory') + self.assertEqual(tweens.implicit(), + [ + ('dbt', 'dbt_factory'), + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ('txnmgr', 'txnmgr_factory'), + ]) + + def test_implicit_ordering_withaliases2(self): + from pyramid.tweens import MAIN + tweens = self._makeOne() + add = tweens.add_implicit + add('exceptionview', 'excview_factory', alias='e', over=MAIN) + add('auth', 'auth_factory', alias='a', under='b') + add('retry', 'retry_factory', alias='r', over='t', under='e') + add('browserid', 'browserid_factory', alias='b') + add('txnmgr', 'txnmgr_factory', alias='t', under='e') + add('dbt', 'dbt_factory', alias='d') + self.assertEqual(tweens.implicit(), + [ + ('dbt', 'dbt_factory'), + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ('txnmgr', 'txnmgr_factory'), + ]) + + 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',MAIN), + 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'), + ]) + + 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('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'), + ]) + + def test_implicit_ordering_missing_partial_with_aliases(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', over=('txnmgr',MAIN), under='e') + add('browserid', 'browserid_factory') + self.assertEqual(tweens.implicit(), + [ + ('browserid', 'browserid_factory'), + ('exceptionview', 'excview_factory'), + ('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.config.tweens import CyclicDependencyError + tweens = self._makeOne() + add = tweens.add_implicit + add('browserid', 'browserid_factory') + add('auth', 'auth_factory', over='browserid', under='browserid') + self.assertRaises(CyclicDependencyError, tweens.implicit) + + def test_implicit_ordering_conflict_indirect(self): + from pyramid.config.tweens import CyclicDependencyError + tweens = self._makeOne() + add = tweens.add_implicit + add('browserid', 'browserid_factory') + add('auth', 'auth_factory', over='browserid') + add('dbt', 'dbt_factory', under='browserid', over='auth') + self.assertRaises(CyclicDependencyError, tweens.implicit) + +class TestCyclicDependencyError(unittest.TestCase): + def _makeOne(self, cycles): + from pyramid.config.tweens import CyclicDependencyError + return CyclicDependencyError(cycles) + + def test___str__(self): + exc = self._makeOne({'a':['c', 'd'], 'c':['a']}) + result = str(exc) + self.assertTrue("'a' sorts over ['c', 'd']" in result) + self.assertTrue("'c' sorts over ['a']" in result) + diff --git a/pyramid/tests/test_tweens.py b/pyramid/tests/test_tweens.py deleted file mode 100644 index 67cfee8a9..000000000 --- a/pyramid/tests/test_tweens.py +++ /dev/null @@ -1,382 +0,0 @@ -import unittest - -class TestTweens(unittest.TestCase): - def _makeOne(self): - from pyramid.tweens import Tweens - return Tweens() - - def test_add_explicit(self): - tweens = self._makeOne() - tweens.add_explicit('name', 'factory') - self.assertEqual(tweens.explicit, [('name', 'factory')]) - tweens.add_explicit('name2', 'factory2') - self.assertEqual(tweens.explicit, [('name', 'factory'), - ('name2', 'factory2')]) - - def test_add_implicit_noaliases(self): - from pyramid.tweens import INGRESS - tweens = self._makeOne() - tweens.add_implicit('name', 'factory') - self.assertEqual(tweens.names, ['name']) - self.assertEqual(tweens.factories, - {'name':'factory'}) - self.assertEqual(tweens.alias_to_name['name'], 'name') - self.assertEqual(tweens.name_to_alias['name'], 'name') - self.assertEqual(tweens.order, [(INGRESS, 'name')]) - tweens.add_implicit('name2', 'factory2') - self.assertEqual(tweens.names, ['name', 'name2']) - self.assertEqual(tweens.factories, - {'name':'factory', 'name2':'factory2'}) - self.assertEqual(tweens.alias_to_name['name2'], 'name2') - self.assertEqual(tweens.name_to_alias['name2'], 'name2') - self.assertEqual(tweens.order, - [(INGRESS, 'name'), (INGRESS, 'name2')]) - tweens.add_implicit('name3', 'factory3', over='name2') - self.assertEqual(tweens.names, - ['name', 'name2', 'name3']) - self.assertEqual(tweens.factories, - {'name':'factory', 'name2':'factory2', - 'name3':'factory3'}) - self.assertEqual(tweens.alias_to_name['name3'], 'name3') - self.assertEqual(tweens.name_to_alias['name3'], 'name3') - self.assertEqual(tweens.order, - [(INGRESS, 'name'), (INGRESS, 'name2'), - ('name3', 'name2')]) - - def test_add_implicit_withaliases(self): - from pyramid.tweens import INGRESS - tweens = self._makeOne() - tweens.add_implicit('name1', 'factory', alias='n1') - self.assertEqual(tweens.names, ['name1']) - self.assertEqual(tweens.factories, - {'name1':'factory'}) - self.assertEqual(tweens.alias_to_name['n1'], 'name1') - self.assertEqual(tweens.name_to_alias['name1'], 'n1') - self.assertEqual(tweens.order, [(INGRESS, 'n1')]) - tweens.add_implicit('name2', 'factory2', alias='n2') - self.assertEqual(tweens.names, ['name1', 'name2']) - self.assertEqual(tweens.factories, - {'name1':'factory', 'name2':'factory2'}) - self.assertEqual(tweens.alias_to_name['n2'], 'name2') - self.assertEqual(tweens.name_to_alias['name2'], 'n2') - self.assertEqual(tweens.order, - [(INGRESS, 'n1'), (INGRESS, 'n2')]) - tweens.add_implicit('name3', 'factory3', alias='n3', over='name2') - self.assertEqual(tweens.names, - ['name1', 'name2', 'name3']) - self.assertEqual(tweens.factories, - {'name1':'factory', 'name2':'factory2', - 'name3':'factory3'}) - self.assertEqual(tweens.alias_to_name['n3'], 'name3') - self.assertEqual(tweens.name_to_alias['name3'], 'n3') - self.assertEqual(tweens.order, - [(INGRESS, 'n1'), (INGRESS, 'n2'), - ('n3', 'name2')]) - - def test___call___explicit(self): - tweens = self._makeOne() - def factory1(handler, registry): - return handler - def factory2(handler, registry): - return '123' - tweens.explicit = [('name', factory1), ('name', factory2)] - 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 - def factory2(handler, registry): - return '123' - 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 - def factory2(handler, registry): - return '123' - 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') - - def test_implicit_ordering_1(self): - tweens = self._makeOne() - tweens.add_implicit('name1', 'factory1') - tweens.add_implicit('name2', 'factory2') - self.assertEqual(tweens.implicit(), - [ - ('name2', 'factory2'), - ('name1', 'factory1'), - ]) - - def test_implicit_ordering_2(self): - from pyramid.tweens import MAIN - tweens = self._makeOne() - tweens.add_implicit('name1', 'factory1') - tweens.add_implicit('name2', 'factory2', over=MAIN) - self.assertEqual(tweens.implicit(), - [ - ('name1', 'factory1'), - ('name2', 'factory2'), - ]) - - def test_implicit_ordering_3(self): - from pyramid.tweens import MAIN - tweens = self._makeOne() - add = tweens.add_implicit - add('auth', 'auth_factory', under='browserid') - add('dbt', 'dbt_factory') - add('retry', 'retry_factory', over='txnmgr', under='exceptionview') - add('browserid', 'browserid_factory') - add('txnmgr', 'txnmgr_factory', under='exceptionview') - add('exceptionview', 'excview_factory', over=MAIN) - self.assertEqual(tweens.implicit(), - [ - ('browserid', 'browserid_factory'), - ('auth', 'auth_factory'), - ('dbt', 'dbt_factory'), - ('exceptionview', 'excview_factory'), - ('retry', 'retry_factory'), - ('txnmgr', 'txnmgr_factory'), - ]) - - def test_implicit_ordering_4(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('browserid', 'browserid_factory') - add('txnmgr', 'txnmgr_factory', under='exceptionview') - add('dbt', 'dbt_factory') - self.assertEqual(tweens.implicit(), - [ - ('dbt', 'dbt_factory'), - ('browserid', 'browserid_factory'), - ('auth', 'auth_factory'), - ('exceptionview', 'excview_factory'), - ('retry', 'retry_factory'), - ('txnmgr', 'txnmgr_factory'), - ]) - - def test_implicit_ordering_5(self): - from pyramid.tweens import MAIN, INGRESS - tweens = self._makeOne() - add = tweens.add_implicit - add('exceptionview', 'excview_factory', over=MAIN) - add('auth', 'auth_factory', under=INGRESS) - add('retry', 'retry_factory', over='txnmgr', under='exceptionview') - add('browserid', 'browserid_factory', under=INGRESS) - add('txnmgr', 'txnmgr_factory', under='exceptionview', over=MAIN) - add('dbt', 'dbt_factory') - self.assertEqual(tweens.implicit(), - [ - ('dbt', 'dbt_factory'), - ('browserid', 'browserid_factory'), - ('auth', 'auth_factory'), - ('exceptionview', 'excview_factory'), - ('retry', 'retry_factory'), - ('txnmgr', 'txnmgr_factory'), - ]) - - def test_implicit_ordering_withaliases(self): - from pyramid.tweens import MAIN - tweens = self._makeOne() - add = tweens.add_implicit - add('exceptionview', 'excview_factory', alias='e', over=MAIN) - add('auth', 'auth_factory', under='b') - add('retry', 'retry_factory', over='t', under='exceptionview') - add('browserid', 'browserid_factory', alias='b') - add('txnmgr', 'txnmgr_factory', alias='t', under='exceptionview') - add('dbt', 'dbt_factory') - self.assertEqual(tweens.implicit(), - [ - ('dbt', 'dbt_factory'), - ('browserid', 'browserid_factory'), - ('auth', 'auth_factory'), - ('exceptionview', 'excview_factory'), - ('retry', 'retry_factory'), - ('txnmgr', 'txnmgr_factory'), - ]) - - def test_implicit_ordering_withaliases2(self): - from pyramid.tweens import MAIN - tweens = self._makeOne() - add = tweens.add_implicit - add('exceptionview', 'excview_factory', alias='e', over=MAIN) - add('auth', 'auth_factory', alias='a', under='b') - add('retry', 'retry_factory', alias='r', over='t', under='e') - add('browserid', 'browserid_factory', alias='b') - add('txnmgr', 'txnmgr_factory', alias='t', under='e') - add('dbt', 'dbt_factory', alias='d') - self.assertEqual(tweens.implicit(), - [ - ('dbt', 'dbt_factory'), - ('browserid', 'browserid_factory'), - ('auth', 'auth_factory'), - ('exceptionview', 'excview_factory'), - ('retry', 'retry_factory'), - ('txnmgr', 'txnmgr_factory'), - ]) - - 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',MAIN), - 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'), - ]) - - 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('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'), - ]) - - def test_implicit_ordering_missing_partial_with_aliases(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', over=('txnmgr',MAIN), under='e') - add('browserid', 'browserid_factory') - self.assertEqual(tweens.implicit(), - [ - ('browserid', 'browserid_factory'), - ('exceptionview', 'excview_factory'), - ('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() - add = tweens.add_implicit - add('browserid', 'browserid_factory') - add('auth', 'auth_factory', over='browserid', under='browserid') - self.assertRaises(CyclicDependencyError, tweens.implicit) - - def test_implicit_ordering_conflict_indirect(self): - from pyramid.tweens import CyclicDependencyError - tweens = self._makeOne() - add = tweens.add_implicit - add('browserid', 'browserid_factory') - add('auth', 'auth_factory', over='browserid') - add('dbt', 'dbt_factory', under='browserid', over='auth') - self.assertRaises(CyclicDependencyError, tweens.implicit) - -class TestCyclicDependencyError(unittest.TestCase): - def _makeOne(self, cycles): - from pyramid.tweens import CyclicDependencyError - return CyclicDependencyError(cycles) - - def test___str__(self): - exc = self._makeOne({'a':['c', 'd'], 'c':['a']}) - result = str(exc) - self.assertTrue("'a' sorts over ['c', 'd']" in result) - self.assertTrue("'c' sorts over ['a']" in result) - diff --git a/pyramid/tweens.py b/pyramid/tweens.py index b53942a36..d12a57339 100644 --- a/pyramid/tweens.py +++ b/pyramid/tweens.py @@ -1,11 +1,8 @@ import sys -from pyramid.exceptions import ConfigurationError from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IView -from pyramid.interfaces import ITweens from zope.interface import providedBy -from zope.interface import implements def excview_tween_factory(handler, registry): """ A :term:`tween` factory which produces a tween that catches an @@ -46,142 +43,6 @@ def excview_tween_factory(handler, registry): return excview_tween -class CyclicDependencyError(Exception): - def __init__(self, cycles): - self.cycles = cycles - - def __str__(self): - L = [] - cycles = self.cycles - for cycle in cycles: - dependent = cycle - dependees = cycles[cycle] - L.append('%r sorts over %r' % (dependent, dependees)) - msg = 'Implicit tween ordering cycle:' + '; '.join(L) - return msg - -class Tweens(object): - implements(ITweens) - 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} - self.name_to_alias = {INGRESS:INGRESS, MAIN:MAIN} - - def add_explicit(self, name, factory): - self.explicit.append((name, factory)) - - def add_implicit(self, name, factory, alias=None, under=None, over=None): - if alias is None: - alias = name - self.alias_to_name[alias] = name - self.name_to_alias[name] = alias - self.names.append(name) - self.factories[name] = factory - if under is None and over is None: - under = INGRESS - if under is not None: - 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: - 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 = {} - aliases = [INGRESS, MAIN] - - for name in self.names: - aliases.append(self.name_to_alias[name]) - - for a, b in self.order: - # try to convert both a and b to an alias - a = self.name_to_alias.get(a, a) - b = self.name_to_alias.get(b, b) - order.append((a, b)) - - def add_node(node): - if not graph.has_key(node): - roots.append(node) - graph[node] = [0] # 0 = number of arcs coming into this node - - def add_arc(fromnode, tonode): - graph[fromnode].append(tonode) - graph[tonode][0] += 1 - if tonode in roots: - roots.remove(tonode) - - for alias in aliases: - add_node(alias) - - has_over, has_under = set(), set() - for a, b in order: - 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 = [] - - while roots: - root = roots.pop(0) - sorted_aliases.append(root) - children = graph[root][1:] - for child in children: - arcs = graph[child][0] - arcs -= 1 - graph[child][0] = arcs - if arcs == 0: - roots.insert(0, child) - del graph[root] - - if graph: - # loop in input - cycledeps = {} - for k, v in graph.items(): - cycledeps[k] = v[1:] - raise CyclicDependencyError(cycledeps) - - result = [] - - for alias in sorted_aliases: - name = self.alias_to_name.get(alias, alias) - if name in self.names: - result.append((name, self.factories[name])) - - return result - - def __call__(self, handler, registry): - if self.explicit: - use = self.explicit - else: - use = self.implicit() - for name, factory in use[::-1]: - handler = factory(handler, registry) - return handler - MAIN = 'MAIN' INGRESS = 'INGRESS' EXCVIEW = 'excview' - -- cgit v1.2.3