Michael Merickel
2016-12-16 134ef7e8d17842e286528aeb8d2936579ed81053
turn the Configurator into a context manager

fixes #2872
3 files modified
64 ■■■■■ changed files
README.rst 8 ●●●● patch | view | raw | blame | history
pyramid/config/__init__.py 40 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_init.py 16 ●●●●● patch | view | raw | blame | history
README.rst
@@ -31,10 +31,10 @@
       return Response('Hello %(name)s!' % request.matchdict)
   if __name__ == '__main__':
       config = Configurator()
       config.add_route('hello', '/hello/{name}')
       config.add_view(hello_world, route_name='hello')
       app = config.make_wsgi_app()
       with Configurator() as config:
           config.add_route('hello', '/hello/{name}')
           config.add_view(hello_world, route_name='hello')
           app = config.make_wsgi_app()
       server = make_server('0.0.0.0', 8080, app)
       server.serve_forever()
pyramid/config/__init__.py
@@ -110,6 +110,17 @@
    A Configurator is used to configure a :app:`Pyramid`
    :term:`application registry`.
    The Configurator lifecycle can be managed by using a context manager to
    automatically handle calling :meth:`pyramid.config.Configurator.begin` and
    :meth:`pyramid.config.Configurator.end` as well as
    :meth:`pyramid.config.Configurator.commit`.
    .. code-block:: python
        with Configurator(settings=settings) as config:
            config.add_route('home', '/')
            app = config.make_wsgi_app()
    If the ``registry`` argument is not ``None``, it must
    be an instance of the :class:`pyramid.registry.Registry` class
    representing the registry to configure.  If ``registry`` is ``None``, the
@@ -265,6 +276,11 @@
    .. versionadded:: 1.6
       The ``root_package`` argument.
       The ``response_factory`` argument.
    .. versionadded:: 1.9
       The ability to use the configurator as a context manager with the
       ``with``-statement to make threadlocal configuration available for
       further configuration with an implicit commit.
    """
    manager = manager # for testing injection
    venusian = venusian # for testing injection
@@ -646,12 +662,22 @@
    _ctx = action_state # bw compat
    def commit(self):
        """ Commit any pending configuration actions. If a configuration
        """
        Commit any pending configuration actions. If a configuration
        conflict is detected in the pending configuration actions, this method
        will raise a :exc:`ConfigurationConflictError`; within the traceback
        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."""
        configuration conflicts.
        .. warning::
           You should think very carefully before manually invoking
           ``commit()``. Especially not as part of any reusable configuration
           methods. Normally it should only be done by an application author at
           the end of configuration in order to override certain aspects of an
           addon.
        """
        self.begin()
        try:
            self.action_state.execute_actions(introspector=self.introspector)
@@ -933,6 +959,16 @@
        """
        return self.manager.pop()
    def __enter__(self):
        self.begin()
        return self
    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.end()
        if exc_value is None:
            self.commit()
    # this is *not* an action method (uses caller_package)
    def scan(self, package=None, categories=None, onerror=None, ignore=None,
             **kw):
pyramid/tests/test_config/test_init.py
@@ -141,6 +141,22 @@
        self.assertEqual(manager.pushed, pushed)
        self.assertEqual(manager.popped, True)
    def test_context_manager(self):
        from pyramid.config import Configurator
        config = Configurator()
        manager = DummyThreadLocalManager()
        config.manager = manager
        view = lambda r: None
        with config as ctx:
            self.assertTrue(config is ctx)
            self.assertEqual(manager.pushed,
                             {'registry': config.registry, 'request': None})
            self.assertFalse(manager.popped)
            config.add_view(view)
        self.assertTrue(manager.popped)
        config.add_view(view)  # did not raise a conflict because of commit
        config.commit()
    def test_ctor_with_package_registry(self):
        import sys
        from pyramid.config import Configurator