diff options
| author | Chris McDonough <chrism@plope.com> | 2011-08-12 04:42:16 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2011-08-12 04:42:16 -0400 |
| commit | 7816f57ef5ebda6eca1f272051448b810e526692 (patch) | |
| tree | d829e0db0923af2fc8a9b319040d251d60f909c1 | |
| parent | 47369747d8050faddd8b98f4cad9f5d0bb263130 (diff) | |
| parent | f882fefeedfda182272554bad33687c82929bddd (diff) | |
| download | pyramid-7816f57ef5ebda6eca1f272051448b810e526692.tar.gz pyramid-7816f57ef5ebda6eca1f272051448b810e526692.tar.bz2 pyramid-7816f57ef5ebda6eca1f272051448b810e526692.zip | |
Merge branch 'master' of github.com:Pylons/pyramid
| -rw-r--r-- | docs/narr/hooks.rst | 16 | ||||
| -rw-r--r-- | pyramid/config.py | 25 | ||||
| -rw-r--r-- | pyramid/tests/test_config.py | 16 | ||||
| -rw-r--r-- | pyramid/tests/test_tweens.py | 100 | ||||
| -rw-r--r-- | pyramid/tweens.py | 51 |
5 files changed, 151 insertions, 57 deletions
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index cd2109c5c..c8efc057c 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -949,6 +949,10 @@ Allowable values for ``under`` or ``over`` (or both) are: - 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 @@ this:: 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 diff --git a/pyramid/config.py b/pyramid/config.py index 827144828..920b70319 100644 --- a/pyramid/config.py +++ b/pyramid/config.py @@ -978,8 +978,8 @@ class Configurator(object): 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 @@ class Configurator(object): - 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 @@ class Configurator(object): 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 @@ class Configurator(object): 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 diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py index 4620c05aa..ec04f177b 100644 --- a/pyramid/tests/test_config.py +++ b/pyramid/tests/test_config.py @@ -763,6 +763,14 @@ pyramid.tests.test_config.dummy_include2""", 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 @@ pyramid.tests.test_config.dummy_include2""", 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 diff --git a/pyramid/tests/test_tweens.py b/pyramid/tests/test_tweens.py index 5fa999e8a..67cfee8a9 100644 --- a/pyramid/tests/test_tweens.py +++ b/pyramid/tests/test_tweens.py @@ -2,7 +2,7 @@ import unittest 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 @@ class TestTweens(unittest.TestCase): 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 @@ class TestTweens(unittest.TestCase): 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 @@ class TestTweens(unittest.TestCase): 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 @@ class TestTweens(unittest.TestCase): 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 @@ class TestTweens(unittest.TestCase): 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 @@ class TestTweens(unittest.TestCase): 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 @@ class TestTweens(unittest.TestCase): 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 @@ class TestTweens(unittest.TestCase): 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 @@ class TestTweens(unittest.TestCase): 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 @@ class TestTweens(unittest.TestCase): ('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,27 +282,30 @@ class TestTweens(unittest.TestCase): ('retry', 'retry_factory'), ]) - def test_implicit_ordering_missing_partial2(self): + def test_implicit_ordering_missing_under_partial_with_fallback(self): + from pyramid.tweens import MAIN 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('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(), [ - ('retry', 'retry_factory'), + ('dbt', 'dbt_factory'), ('browserid', 'browserid_factory'), ('auth', 'auth_factory'), - ('dbt', 'dbt_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), ]) - def test_implicit_ordering_missing_partial3(self): + 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', over=MAIN) - add('retry', 'retry_factory', over='txnmgr', under='exceptionview') + 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(), [ @@ -280,13 +314,27 @@ class TestTweens(unittest.TestCase): ('retry', 'retry_factory'), ]) - def test_implicit_ordering_missing_partial_with_aliases(self): + 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', over='txnmgr', under='e') - add('browserid', 'browserid_factory') + add('retry', 'retry_factory', under='e') + add('browserid', 'browserid_factory', over=('retry', 'e')) self.assertEqual(tweens.implicit(), [ ('browserid', 'browserid_factory'), @@ -294,6 +342,16 @@ class TestTweens(unittest.TestCase): ('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() diff --git a/pyramid/tweens.py b/pyramid/tweens.py index 5ada88b24..73b96d375 100644 --- a/pyramid/tweens.py +++ b/pyramid/tweens.py @@ -65,9 +65,10 @@ class Tweens(object): 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 @@ class Tweens(object): 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 @@ class Tweens(object): 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 @@ class Tweens(object): 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 |
