Michael Merickel
2017-04-30 9c15a8eac2848cc7a81f174ecf7a6c63be323f51
Merge branch 'master' into pr/2854
39 files modified
173 ■■■■ changed files
CHANGES.txt 5 ●●●●● patch | view | raw | blame | history
CONTRIBUTORS.txt 2 ●●●●● patch | view | raw | blame | history
HISTORY.txt 6 ●●●●● patch | view | raw | blame | history
README.rst 8 ●●●● patch | view | raw | blame | history
docs/narr/logging.rst 2 ●●● patch | view | raw | blame | history
docs/narr/myproject/README.txt 2 ●●● patch | view | raw | blame | history
docs/narr/project.rst 2 ●●● patch | view | raw | blame | history
docs/quick_tour.rst 4 ●●●● patch | view | raw | blame | history
docs/quick_tour/logging/README.txt 2 ●●● patch | view | raw | blame | history
docs/quick_tour/package/README.txt 2 ●●● patch | view | raw | blame | history
docs/quick_tour/sessions/README.txt 2 ●●● patch | view | raw | blame | history
docs/quick_tour/sqla_demo/README.txt 2 ●●● patch | view | raw | blame | history
docs/quick_tutorial/cookiecutters.rst 2 ●●● patch | view | raw | blame | history
docs/quick_tutorial/cookiecutters/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/modwsgi/index.rst 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki/installation.rst 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/authorization/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/basiclayout/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/installation/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/models/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/tests/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/views/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/installation.rst 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authentication/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authentication/tutorial/views/default.py 6 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authorization/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authorization/tutorial/views/default.py 6 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/basiclayout/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/installation/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/models/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/tests/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/tests/tutorial/views/default.py 6 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/views/README.txt 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/views/tutorial/views/default.py 6 ●●●● patch | view | raw | blame | history
docs/whatsnew-1.8.rst 7 ●●●●● patch | view | raw | blame | history
pyramid/config/__init__.py 40 ●●●●● patch | view | raw | blame | history
pyramid/config/security.py 7 ●●●● patch | view | raw | blame | history
pyramid/config/views.py 4 ●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_init.py 16 ●●●●● patch | view | raw | blame | history
CHANGES.txt
@@ -32,6 +32,11 @@
  See https://github.com/Pylons/pyramid/pull/2854 and
  https://github.com/Pylons/pyramid/pull/3019
- The ``pyramid.config.Configurator`` can now be used as a context manager
  which will automatically push/pop threadlocals (similar to
  ``config.begin()`` and ``config.end()``). It will also automatically perform
  a ``config.commit()`` and thus it is only recommended to be used at the
  top-level of your app. See https://github.com/Pylons/pyramid/pull/2874
Bug Fixes
---------
CONTRIBUTORS.txt
@@ -296,3 +296,5 @@
- Martin Frlin, 2016/12/7
- Kirill Kuzminykh, 2017/03/01
- Jeremy(Ching-Rui) Chen, 2017/04/19
HISTORY.txt
@@ -198,6 +198,12 @@
  See https://github.com/Pylons/pyramid/pull/2873
- Added a new ``callback`` option to ``config.set_default_csrf_options`` which
  can be used to determine per-request whether CSRF checking should be enabled
  to allow for a mix authentication methods. Only cookie-based methods
  generally require CSRF checking.
  See https://github.com/Pylons/pyramid/pull/2778
Bug Fixes
---------
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()
docs/narr/logging.rst
@@ -16,7 +16,7 @@
   cookiecutter which does not create these files, the configuration information in
   this chapter may not be applicable.
.. index:
.. index::
   pair: settings; logging
   pair: .ini; logging
   pair: logging; configuration
docs/narr/myproject/README.txt
@@ -1,5 +1,5 @@
MyProject
===============================
=========
Getting Started
---------------
docs/narr/project.rst
@@ -94,7 +94,7 @@
    You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
    Is it okay to delete and re-clone it? [yes]: yes
    project_name [Pyramid Scaffold]: myproject
    repo_name [scaffold]: myproject
    repo_name [myproject]: myproject
    Select template_language:
    1 - jinja2
    2 - chameleon
docs/quick_tour.rst
@@ -519,7 +519,7 @@
    You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
    Is it okay to delete and re-clone it? [yes]: yes
    project_name [Pyramid Scaffold]: hello_world
    repo_name [scaffold]: hello_world
    repo_name [hello_world]: hello_world
    Select template_language:
    1 - jinja2
    2 - chameleon
@@ -875,7 +875,7 @@
    You've cloned ~/.cookiecutters/pyramid-cookiecutter-alchemy before.
    Is it okay to delete and re-clone it? [yes]: yes
    project_name [Pyramid Scaffold]: sqla_demo
    repo_name [scaffold]: sqla_demo
    repo_name [sqla_demo]: sqla_demo
We then run through the following commands as before.
docs/quick_tour/logging/README.txt
@@ -1,5 +1,5 @@
hello_world
===============================
===========
Getting Started
---------------
docs/quick_tour/package/README.txt
@@ -1,5 +1,5 @@
hello_world
===============================
===========
Getting Started
---------------
docs/quick_tour/sessions/README.txt
@@ -1,5 +1,5 @@
hello_world
===============================
===========
Getting Started
---------------
docs/quick_tour/sqla_demo/README.txt
@@ -1,5 +1,5 @@
sqla_demo
===============================
=========
Getting Started
---------------
docs/quick_tutorial/cookiecutters.rst
@@ -37,7 +37,7 @@
        You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
        Is it okay to delete and re-clone it? [yes]: yes
        project_name [Pyramid Scaffold]: cc_starter
        repo_name [scaffold]: cc_starter
        repo_name [cc_starter]: cc_starter
        Select template_language:
        1 - jinja2
        2 - chameleon
docs/quick_tutorial/cookiecutters/README.txt
@@ -1,5 +1,5 @@
cc_starter
===============================
==========
Getting Started
---------------
docs/tutorials/modwsgi/index.rst
@@ -48,7 +48,7 @@
        You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
        Is it okay to delete and re-clone it? [yes]: yes
        project_name [Pyramid Scaffold]: myproject
        repo_name [scaffold]: myproject
        repo_name [myproject]: myproject
        Select template_language:
        1 - jinja2
        2 - chameleon
docs/tutorials/wiki/installation.rst
@@ -50,7 +50,7 @@
    You've cloned ~/.cookiecutters/pyramid-cookiecutter-zodb before.
    Is it okay to delete and re-clone it? [yes]: yes
    project_name [Pyramid Scaffold]: myproj
    repo_name [scaffold]: tutorial
    repo_name [myproj]: tutorial
Change directory into your newly created project
------------------------------------------------
docs/tutorials/wiki/src/authorization/README.txt
@@ -1,5 +1,5 @@
myproj
===============================
======
Getting Started
---------------
docs/tutorials/wiki/src/basiclayout/README.txt
@@ -1,5 +1,5 @@
myproj
===============================
======
Getting Started
---------------
docs/tutorials/wiki/src/installation/README.txt
@@ -1,5 +1,5 @@
myproj
===============================
======
Getting Started
---------------
docs/tutorials/wiki/src/models/README.txt
@@ -1,5 +1,5 @@
myproj
===============================
======
Getting Started
---------------
docs/tutorials/wiki/src/tests/README.txt
@@ -1,5 +1,5 @@
myproj
===============================
======
Getting Started
---------------
docs/tutorials/wiki/src/views/README.txt
@@ -1,5 +1,5 @@
myproj
===============================
======
Getting Started
---------------
docs/tutorials/wiki2/installation.rst
@@ -62,7 +62,7 @@
    You've cloned ~/.cookiecutters/pyramid-cookiecutter-alchemy before.
    Is it okay to delete and re-clone it? [yes]: yes
    project_name [Pyramid Scaffold]: myproj
    repo_name [scaffold]: tutorial
    repo_name [myproj]: tutorial
Change directory into your newly created project
------------------------------------------------
docs/tutorials/wiki2/src/authentication/README.txt
@@ -1,5 +1,5 @@
myproj
===============================
======
Getting Started
---------------
docs/tutorials/wiki2/src/authentication/tutorial/views/default.py
@@ -1,4 +1,4 @@
import cgi
from pyramid.compat import escape
import re
from docutils.core import publish_parts
@@ -32,10 +32,10 @@
        exists = request.dbsession.query(Page).filter_by(name=word).all()
        if exists:
            view_url = request.route_url('view_page', pagename=word)
            return '<a href="%s">%s</a>' % (view_url, cgi.escape(word))
            return '<a href="%s">%s</a>' % (view_url, escape(word))
        else:
            add_url = request.route_url('add_page', pagename=word)
            return '<a href="%s">%s</a>' % (add_url, cgi.escape(word))
            return '<a href="%s">%s</a>' % (add_url, escape(word))
    content = publish_parts(page.data, writer_name='html')['html_body']
    content = wikiwords.sub(add_link, content)
docs/tutorials/wiki2/src/authorization/README.txt
@@ -1,5 +1,5 @@
myproj
===============================
======
Getting Started
---------------
docs/tutorials/wiki2/src/authorization/tutorial/views/default.py
@@ -1,4 +1,4 @@
import cgi
from pyramid.compat import escape
import re
from docutils.core import publish_parts
@@ -25,10 +25,10 @@
        exists = request.dbsession.query(Page).filter_by(name=word).all()
        if exists:
            view_url = request.route_url('view_page', pagename=word)
            return '<a href="%s">%s</a>' % (view_url, cgi.escape(word))
            return '<a href="%s">%s</a>' % (view_url, escape(word))
        else:
            add_url = request.route_url('add_page', pagename=word)
            return '<a href="%s">%s</a>' % (add_url, cgi.escape(word))
            return '<a href="%s">%s</a>' % (add_url, escape(word))
    content = publish_parts(page.data, writer_name='html')['html_body']
    content = wikiwords.sub(add_link, content)
docs/tutorials/wiki2/src/basiclayout/README.txt
@@ -1,5 +1,5 @@
myproj
===============================
======
Getting Started
---------------
docs/tutorials/wiki2/src/installation/README.txt
@@ -1,5 +1,5 @@
myproj
===============================
======
Getting Started
---------------
docs/tutorials/wiki2/src/models/README.txt
@@ -1,5 +1,5 @@
myproj
===============================
======
Getting Started
---------------
docs/tutorials/wiki2/src/tests/README.txt
@@ -1,5 +1,5 @@
myproj
===============================
======
Getting Started
---------------
docs/tutorials/wiki2/src/tests/tutorial/views/default.py
@@ -1,4 +1,4 @@
import cgi
from pyramid.compat import escape
import re
from docutils.core import publish_parts
@@ -25,10 +25,10 @@
        exists = request.dbsession.query(Page).filter_by(name=word).all()
        if exists:
            view_url = request.route_url('view_page', pagename=word)
            return '<a href="%s">%s</a>' % (view_url, cgi.escape(word))
            return '<a href="%s">%s</a>' % (view_url, escape(word))
        else:
            add_url = request.route_url('add_page', pagename=word)
            return '<a href="%s">%s</a>' % (add_url, cgi.escape(word))
            return '<a href="%s">%s</a>' % (add_url, escape(word))
    content = publish_parts(page.data, writer_name='html')['html_body']
    content = wikiwords.sub(add_link, content)
docs/tutorials/wiki2/src/views/README.txt
@@ -1,5 +1,5 @@
myproj
===============================
======
Getting Started
---------------
docs/tutorials/wiki2/src/views/tutorial/views/default.py
@@ -1,4 +1,4 @@
import cgi
from pyramid.compat import escape
import re
from docutils.core import publish_parts
@@ -31,10 +31,10 @@
        exists = request.dbsession.query(Page).filter_by(name=word).all()
        if exists:
            view_url = request.route_url('view_page', pagename=word)
            return '<a href="%s">%s</a>' % (view_url, cgi.escape(word))
            return '<a href="%s">%s</a>' % (view_url, escape(word))
        else:
            add_url = request.route_url('add_page', pagename=word)
            return '<a href="%s">%s</a>' % (add_url, cgi.escape(word))
            return '<a href="%s">%s</a>' % (add_url, escape(word))
    content = publish_parts(page.data, writer_name='html')['html_body']
    content = wikiwords.sub(add_link, content)
docs/whatsnew-1.8.rst
@@ -114,6 +114,13 @@
  later calls to place translation directories at a higher priority then
  earlier calls. See https://github.com/Pylons/pyramid/pull/2902
- Added a new ``callback`` option to
  :meth:`pyramid.config.Configurator.set_default_csrf_options`` which
  can be used to determine per-request whether CSRF checking should be enabled
  to allow for a mix authentication methods. Only cookie-based methods
  generally require CSRF checking.
  See https://github.com/Pylons/pyramid/pull/2778
Backwards Incompatibilities
---------------------------
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
@@ -647,12 +663,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)
@@ -934,6 +960,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/config/security.py
@@ -207,9 +207,14 @@
        are not subject to CSRF attacks. For example, if a request is
        authenticated using the ``Authorization`` header instead of a cookie,
        this may return ``False`` for that request so that clients do not
        need to send the ``X-CSRF-Token` header. The callback is only tested
        need to send the ``X-CSRF-Token`` header. The callback is only tested
        for non-safe methods as defined by ``safe_methods``.
        .. versionadded:: 1.7
        .. versionchanged:: 1.8
           Added the ``callback`` option.
        """
        options = DefaultCSRFOptions(
            require_csrf, token, header, safe_methods, callback,
pyramid/config/views.py
@@ -976,7 +976,7 @@
        def register_view(classifier, request_iface, derived_view):
            # A multiviews is a set of views which are registered for
            # exactly the same context type/request type/name triad.  Each
            # consituent view in a multiview differs only by the
            # constituent view in a multiview differs only by the
            # predicates which it possesses.
            # To find a previously registered view for a context
@@ -1036,7 +1036,7 @@
                # XXX we could try to be more efficient here and register
                # a non-secured view for a multiview if none of the
                # multiview's consituent views have a permission
                # multiview's constituent views have a permission
                # associated with them, but this code is getting pretty
                # rough already
                if is_multiview:
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