Chris McDonough
2011-09-04 b86fa95c850130e1b21b804f9fb87666a80dbe18
all tests pass; pyramid_zcml still broken with this branch
3 files modified
342 ■■■■ changed files
pyramid/config/__init__.py 159 ●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_init.py 181 ●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_routes.py 2 ●●● patch | view | raw | blame | history
pyramid/config/__init__.py
@@ -205,8 +205,10 @@
    manager = manager # for testing injection
    venusian = venusian # for testing injection
    _ctx = None
    _ainfo = None
    basepath = None
    includepath = ()
    info = ''
    def __init__(self,
                 registry=None,
@@ -442,20 +444,14 @@
        if kw is None:
            kw = {}
        context = self._ctx
        if context is None:
            autocommit = self.autocommit
        else:
            autocommit = context.autocommit
        autocommit = self.autocommit
        if autocommit:
            if callable is not None:
                callable(*args, **kw)
        else:
            if context is None: # defer expensive creation of context
                context = self._ctx = self._make_context(self.autocommit)
            info = context.info
            info = self.info
            if not info:
                # Try to provide more accurate info for conflict reports by
                # wrapping the context in a decorator and attaching caller info
@@ -465,7 +461,29 @@
                    info = self._ainfo[0]
                else:
                    info = ''
            context.action(discriminator, callable, args, kw, order, info=info)
            self.action_state.action(
                discriminator,
                callable,
                args,
                kw,
                order,
                info=info,
                includepath=self.includepath,
                )
    def _get_action_state(self):
        registry = self.registry
        try:
            state = registry.action_state
        except AttributeError:
            state = ActionState()
            registry.action_state = state
        return state
    def _set_action_state(self, state):
        self.registry.action_state = state
    action_state = property(_get_action_state, _set_action_state)
    def commit(self):
        """ Commit any pending configuration actions. If a configuration
@@ -474,11 +492,8 @@
        of this error will be information about the source of the conflict,
        usually including file names and line numbers of the cause of the
        configuration conflicts."""
        if self._ctx is None:
            return
        self._ctx.execute_actions()
        # unwrap and reset the context
        self._ctx = None
        self.action_state.execute_actions()
        self.action_state = ActionState() # old actions have been processed
    def include(self, callable, route_prefix=None):
        """Include a configuration callables, to support imperative
@@ -502,7 +517,7 @@
        Python :term:`module`, in which case, the module will be searched for
        a callable named ``includeme``, which will be treated as the
        configuration callable.
        For example, if the ``includeme`` function below lives in a module
        named ``myapp.myconfig``:
@@ -573,10 +588,9 @@
        The ``route_prefix`` parameter is new as of Pyramid 1.2.
        """
        ## """ <-- emacs gets confused if this isn't here
        _context = self._ctx
        if _context is None:
            _context = self._ctx = self._make_context(self.autocommit)
        action_state = self.action_state
        if self.route_prefix:
            old_prefix = self.route_prefix.rstrip('/') + '/'
@@ -592,15 +606,18 @@
            c = getattr(module, 'includeme')
        spec = module.__name__ + ':' + c.__name__
        sourcefile = inspect.getsourcefile(c)
        if _context.processSpec(spec):
            context = ActionStateWrapper(_context)
            context.basepath = os.path.dirname(sourcefile)
            context.includepath = _context.includepath + (spec,)
            context.package = package_of(module)
            context.route_prefix = route_prefix
            config = self.__class__.with_context(context)
            c(config)
        if action_state.processSpec(spec):
            configurator = self.__class__(
                registry=self.registry,
                package=package_of(module),
                autocommit=self.autocommit,
                route_prefix=route_prefix,
                )
            configurator.basepath = os.path.dirname(sourcefile)
            configurator.includepath = self.includepath + (spec,)
            c(configurator)
    def add_directive(self, name, directive, action_wrap=True):
        """
        Add a directive method to the configurator.
@@ -650,10 +667,15 @@
        :meth:`pyramid.config.Configurator.with_package`, and
        :meth:`pyramid.config.Configurator.include` to obtain a configurator
        with 'the right' context.  Returns a new Configurator instance."""
        configurator = cls(registry=context.registry, package=context.package,
                           autocommit=context.autocommit,
                           route_prefix=context.route_prefix)
        configurator._ctx = context
        configurator = cls(
            registry=context.registry,
            package=context.package,
            autocommit=context.autocommit,
            route_prefix=context.route_prefix
            )
        configurator.basepath = context.basepath
        configurator.includepath = context.includepath
        configurator.info = context.info
        return configurator
    def with_package(self, package):
@@ -662,12 +684,16 @@
        ``package`` argument to the new configurator.  ``package`` may
        be an actual Python package object or a :term:`dotted Python name`
        representing a package."""
        context = self._ctx
        if context is None:
            context = self._ctx = self._make_context(self.autocommit)
        context = ActionStateWrapper(context)
        context.package = package
        return self.__class__.with_context(context)
        configurator = self.__class__(
            registry=self.registry,
            package=package,
            autocommit=self.autocommit,
            route_prefix=self.route_prefix,
            )
        configurator.basepath = self.basepath
        configurator.includepath = self.includepath
        configurator.info = self.info
        return configurator
    def maybe_dotted(self, dotted):
        """ Resolve the :term:`dotted Python name` ``dotted`` to a
@@ -788,8 +814,9 @@
        return app
class ActionStateBase(object):
class ActionState(object):
    def __init__(self):
        self.actions = []
        self._seen_files = set()
    def processSpec(self, spec):
@@ -806,8 +833,8 @@
        self._seen_files.add(spec)
        return True
    def action(self, discriminator, callable=None, args=(), kw={}, order=0,
               includepath=None, info=None):
    def action(self, discriminator, callable=None, args=(), kw=None, order=0,
               includepath=(), info=''):
        """Add an action with the given discriminator, callable and arguments
        For testing purposes, the callable and arguments may be omitted.
@@ -869,44 +896,13 @@
        (None, None, (), {}, ('foo.zcml',), 'abc')
        
        """
        if info is None:
            info = getattr(self, 'info', '')
        if includepath is None:
            includepath = getattr(self, 'includepath', ())
        if kw is None:
            kw = {}
        action = (discriminator, callable, args, kw, includepath, info, order)
        # remove trailing false items
        while (len(action) > 2) and not action[-1]:
            action = action[:-1]
        self.actions.append(action)
class ActionStateWrapper(ActionStateBase):
    """ Wrap an action state.
    """
    def __init__(self, state):
        self.state = state
    def __getattr__(self, name):
        v = getattr(self.state, name)
        # cache result in self
        setattr(self, name, v)
        return v
class ActionState(ActionStateBase):
    autocommit = False
    route_prefix = None
    package = None
    basepath = None
    includepath = ()
    info = ''
    def __init__(self):
        super(ActionState, self).__init__()
        self.actions = []
    def execute_actions(self, clear=True):
        """Execute the configuration actions
@@ -959,8 +955,7 @@
        """
        try:
            for action in resolveConflicts(self.actions):
                (discriminator, callable, args, kw, includepath, info, order
                 ) = expand_action(*action)
                _, callable, args, kw, _, info, _ = expand_action(*action)
                if callable is None:
                    continue
                try:
@@ -969,7 +964,10 @@
                    raise
                except:
                    t, v, tb = sys.exc_info()
                    raise ConfigurationExecutionError(t, v, info), None, tb
                    try:
                        raise ConfigurationExecutionError(t, v, info), None, tb
                    finally:
                       del t, v, tb
        finally:
            if clear:
                del self.actions[:]
@@ -1082,10 +1080,11 @@
    return r
def expand_action(discriminator, callable=None, args=(), kw={},
def expand_action(discriminator, callable=None, args=(), kw=None,
                   includepath=(), info='', order=0):
    return (discriminator, callable, args, kw,
            includepath, info, order)
    if kw is None:
        kw = {}
    return (discriminator, callable, args, kw, includepath, info, order)
global_registries = WeakOrderedSet()
pyramid/tests/test_config/test_init.py
@@ -243,23 +243,22 @@
        newconfig = config.with_package(pyramid.tests.test_config)
        self.assertEqual(newconfig.package, pyramid.tests.test_config)
    def test_with_package_context_is_not_None(self):
        import pyramid.tests.test_config
    def test_with_package(self):
        import pyramid.tests
        config = self._makeOne()
        config._ctx = DummyContext()
        config._ctx.registry = None
        config._ctx.autocommit = True
        config._ctx.route_prefix = None
        newconfig = config.with_package(pyramid.tests.test_config)
        self.assertEqual(newconfig.package, pyramid.tests.test_config)
    def test_with_package_context_is_None(self):
        import pyramid.tests.test_config
        config = self._makeOne()
        config._ctx = None
        newconfig = config.with_package(pyramid.tests.test_config)
        self.assertEqual(newconfig.package, pyramid.tests.test_config)
        self.assertEqual(config._ctx.package, None)
        config.basepath = 'basepath'
        config.info = 'info'
        config.includepath = ('spec',)
        config.autocommit = True
        config.route_prefix = 'prefix'
        newconfig = config.with_package(pyramid.tests)
        self.assertEqual(newconfig.package, pyramid.tests)
        self.assertEqual(newconfig.registry, config.registry)
        self.assertEqual(newconfig.autocommit, True)
        self.assertEqual(newconfig.route_prefix, 'prefix')
        self.assertEqual(newconfig.info, 'info')
        self.assertEqual(newconfig.basepath, 'basepath')
        self.assertEqual(newconfig.includepath, ('spec',))
    def test_maybe_dotted_string_success(self):
        import pyramid.tests.test_config
@@ -659,53 +658,38 @@
    def test_include_with_dotted_name(self):
        from pyramid.tests import test_config
        config = self._makeOne()
        context_before = config._make_context()
        config._ctx = context_before
        config.include('pyramid.tests.test_config.dummy_include')
        context_after = config._ctx
        actions = context_after.actions
        after = config.action_state
        actions = after.actions
        self.assertEqual(len(actions), 1)
        self.assertEqual(
            context_after.actions[0][:3],
            after.actions[0][:3],
            ('discrim', None, test_config),
            )
        self.assertEqual(context_after.basepath, None)
        self.assertEqual(context_after.includepath, ())
        self.assertTrue(context_after is context_before)
    def test_include_with_python_callable(self):
        from pyramid.tests import test_config
        config = self._makeOne()
        context_before = config._make_context()
        config._ctx = context_before
        config.include(dummy_include)
        context_after = config._ctx
        actions = context_after.actions
        after = config.action_state
        actions = after.actions
        self.assertEqual(len(actions), 1)
        self.assertEqual(
            actions[0][:3],
            ('discrim', None, test_config),
            )
        self.assertEqual(context_after.basepath, None)
        self.assertEqual(context_after.includepath, ())
        self.assertTrue(context_after is context_before)
    def test_include_with_module_defaults_to_includeme(self):
        from pyramid.tests import test_config
        config = self._makeOne()
        context_before = config._make_context()
        config._ctx = context_before
        config.include('pyramid.tests.test_config')
        context_after = config._ctx
        actions = context_after.actions
        after = config.action_state
        actions = after.actions
        self.assertEqual(len(actions), 1)
        self.assertEqual(
            actions[0][:3],
            ('discrim', None, test_config),
            )
        self.assertEqual(context_after.basepath, None)
        self.assertEqual(context_after.includepath, ())
        self.assertTrue(context_after is context_before)
    def test_include_with_route_prefix(self):
        root_config = self._makeOne(autocommit=True)
@@ -721,9 +705,22 @@
    def test_with_context(self):
        config = self._makeOne()
        ctx = config._make_context()
        newconfig = config.with_context(ctx)
        self.assertEqual(newconfig._ctx, ctx)
        context = DummyZCMLContext()
        context.basepath = 'basepath'
        context.includepath = ('spec',)
        context.package = 'pyramid'
        context.autocommit = True
        context.registry = 'abc'
        context.route_prefix = 'buz'
        context.info = 'info'
        newconfig = config.with_context(context)
        self.assertEqual(newconfig.package_name, 'pyramid')
        self.assertEqual(newconfig.autocommit, True)
        self.assertEqual(newconfig.registry, 'abc')
        self.assertEqual(newconfig.route_prefix, 'buz')
        self.assertEqual(newconfig.basepath, 'basepath')
        self.assertEqual(newconfig.includepath, ('spec',))
        self.assertEqual(newconfig.info, 'info')
    def test_action_branching_kw_is_None(self):
        config = self._makeOne(autocommit=True)
@@ -733,29 +730,31 @@
        config = self._makeOne(autocommit=True)
        self.assertEqual(config.action('discrim', kw={'a':1}), None)
    def test_action_branching_nonautocommit_with_context_info(self):
    def test_action_branching_nonautocommit_with_config_info(self):
        config = self._makeOne(autocommit=False)
        config.info = 'abc'
        state = DummyActionState()
        state.autocommit = False
        state.info = 'abc'
        config._ctx = state
        config.action_state = state
        config.action('discrim', kw={'a':1})
        self.assertEqual(
            state.actions,
            [(('discrim', None, (), {'a': 1}, 0), {'info': 'abc'})]
            [(('discrim', None, (), {'a': 1}, 0),
              {'info': 'abc', 'includepath':()})]
            )
    def test_action_branching_nonautocommit_without_context_info(self):
    def test_action_branching_nonautocommit_without_config_info(self):
        config = self._makeOne(autocommit=False)
        state = DummyActionState()
        state.autocommit = False
        state.info = ''
        config._ctx = state
        config.info = ''
        config._ainfo = ['z']
        state = DummyActionState()
        config.action_state = state
        state.autocommit = False
        config.action('discrim', kw={'a':1})
        self.assertEqual(
            state.actions,
            [(('discrim', None, (), {'a': 1}, 0), {'info': 'z'})]
            [(('discrim', None, (), {'a': 1}, 0),
              {'info': 'z', 'includepath':()})]
            )
    def test_scan_integration(self):
@@ -1301,9 +1300,9 @@
            'dummy_extend', 'pyramid.tests.test_config.dummy_extend')
        self.assert_(hasattr(config, 'dummy_extend'))
        config.dummy_extend('discrim')
        context_after = config._ctx
        after = config.action_state
        self.assertEqual(
            context_after.actions[-1][:3],
            after.actions[-1][:3],
            ('discrim', None, test_config),
            )
@@ -1314,9 +1313,9 @@
            'dummy_extend', dummy_extend)
        self.assert_(hasattr(config, 'dummy_extend'))
        config.dummy_extend('discrim')
        context_after = config._ctx
        after = config.action_state
        self.assertEqual(
            context_after.actions[-1][:3],
            after.actions[-1][:3],
            ('discrim', None, test_config),
            )
@@ -1328,9 +1327,9 @@
            'dummy_extend', dummy_extend2)
        self.assert_(hasattr(config, 'dummy_extend'))
        config.dummy_extend('discrim')
        context_after = config._ctx
        after = config.action_state
        self.assertEqual(
            context_after.actions[-1][:3],
            after.actions[-1][:3],
            ('discrim', None, config.registry),
            )
@@ -1343,18 +1342,15 @@
        self.assertRaises(ConfigurationConflictError, config.commit)
    def test_directive_persists_across_configurator_creations(self):
        from pyramid.config import ActionStateWrapper
        config = self.config
        config.add_directive('dummy_extend', dummy_extend)
        context = config._make_context(autocommit=False)
        context = ActionStateWrapper(context)
        config2 = config.with_context(context)
        config2 = config.with_package('pyramid.tests')
        config2.dummy_extend('discrim')
        context_after = config2._ctx
        actions = context_after.actions
        after = config2.action_state
        actions = after.actions
        self.assertEqual(len(actions), 1)
        self.assertEqual(
            context_after.actions[0][:3],
            after.actions[0][:3],
            ('discrim', None, config2.package),
            )
@@ -1365,8 +1361,7 @@
    
    def test_it(self):
        c = self._makeOne()
        self.assertEqual(c.autocommit, False)
        self.assertEqual(c.route_prefix, None)
        self.assertEqual(c.actions, [])
    def test_action_simple(self):
        from pyramid.tests.test_config import dummyfactory as f
@@ -1377,32 +1372,28 @@
        c.action(None)
        self.assertEqual(c.actions, [(1, f, (1,), {'x': 1}), (None, None)])
    def test_action_with_includepath_and_info(self):
    def test_action_with_includepath(self):
        c = self._makeOne()
        c.actions = []
        c.includepath = ('foo.zcml',)
        c.info = '?'
        c.action(None)
        c.action(None, includepath=('abc',))
        self.assertEqual(c.actions, [(None, None, (), {}, ('abc',))])
    def test_action_with_info(self):
        c = self._makeOne()
        c.action(None, info='abc')
        self.assertEqual(c.actions, [(None, None, (), {}, (), 'abc')])
    def test_action_with_includepath_and_info(self):
        c = self._makeOne()
        c.action(None, includepath=('spec',), info='bleh')
        self.assertEqual(c.actions,
                         [(None, None, (), {}, ('foo.zcml',), '?')])
                         [(None, None, (), {}, ('spec',), 'bleh')])
    def test_action_with_order(self):
        c = self._makeOne()
        c.actions = []
        c.action(None, order=99999)
        self.assertEqual(c.actions, [(None, None, (), {}, (), '', 99999)])
    def test_action_with_includepath_dynamic(self):
        c = self._makeOne()
        c.actions = []
        c.action(None, includepath=('abc',))
        self.assertEqual(c.actions, [(None, None, (), {}, ('abc',))])
    def test_action_with_info_dynamic(self):
        c = self._makeOne()
        c.actions = []
        c.action(None, info='abc')
        self.assertEqual(c.actions, [(None, None, (), {}, (), 'abc')])
    def test_processSpec(self):
        c = self._makeOne()
@@ -1438,20 +1429,6 @@
            ]
        self.assertRaises(ConfigurationExecutionError, c.execute_actions)
        self.assertEqual(output, [('f', (1,), {}), ('f', (2,), {})])
class TestActionStateWrapper(unittest.TestCase):
    def _makeOne(self, state):
        from pyramid.config import ActionStateWrapper
        return ActionStateWrapper(state)
    def test___getattr__(self):
        state = DummyContext()
        state.foo = 1
        state.bar = 2
        wrapper = self._makeOne(state)
        self.assertEqual(wrapper.foo, 1)
        wrapper.bar = 2
        self.assertEqual(state.bar, 2)
class Test_resolveConflicts(unittest.TestCase):
    def _callFUT(self, actions):
@@ -1583,3 +1560,13 @@
        self.actions = []
    def action(self, *arg, **kw):
        self.actions.append((arg, kw))
class DummyZCMLContext(object):
    package = None
    registry = None
    autocommit = False
    route_prefix = None
    basepath = None
    includepath = ()
    info = ''
pyramid/tests/test_config/test_routes.py
@@ -52,7 +52,7 @@
    def test_add_route_discriminator(self):
        config = self._makeOne()
        config.add_route('name', 'path')
        self.assertEqual(config._ctx.actions[-1][0], ('route', 'name'))
        self.assertEqual(config.action_state.actions[-1][0], ('route', 'name'))
    def test_add_route_with_factory(self):
        config = self._makeOne(autocommit=True)