diff options
39 files changed, 125 insertions, 48 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 7d70abbb8..a2e2d6db1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -32,6 +32,11 @@ Features 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 --------- diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 750f6d29f..ca1f56f51 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -296,3 +296,5 @@ Contributors - Martin Frlin, 2016/12/7 - Kirill Kuzminykh, 2017/03/01 + +- Jeremy(Ching-Rui) Chen, 2017/04/19 diff --git a/HISTORY.txt b/HISTORY.txt index c10747af4..c69d9514e 100644 --- a/HISTORY.txt +++ b/HISTORY.txt @@ -198,6 +198,12 @@ Features 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 --------- diff --git a/README.rst b/README.rst index f05dd8bbf..302590fe1 100644 --- a/README.rst +++ b/README.rst @@ -31,10 +31,10 @@ and deployment more fun, more predictable, and more productive. 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() diff --git a/docs/narr/logging.rst b/docs/narr/logging.rst index 87682158b..9cc5b4ed8 100644 --- a/docs/narr/logging.rst +++ b/docs/narr/logging.rst @@ -16,7 +16,7 @@ to send log messages to loggers that you've configured. 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 diff --git a/docs/narr/myproject/README.txt b/docs/narr/myproject/README.txt index 41ef0ff91..2ffc0acba 100644 --- a/docs/narr/myproject/README.txt +++ b/docs/narr/myproject/README.txt @@ -1,5 +1,5 @@ MyProject -=============================== +========= Getting Started --------------- diff --git a/docs/narr/project.rst b/docs/narr/project.rst index ce7e90793..9c44d4f16 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -94,7 +94,7 @@ If prompted for the first item, accept the default ``yes`` by hitting return. 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 diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst index 02c3ff811..571dfb356 100644 --- a/docs/quick_tour.rst +++ b/docs/quick_tour.rst @@ -519,7 +519,7 @@ If prompted for the first item, accept the default ``yes`` by hitting return. 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 @@ If prompted for the first item, accept the default ``yes`` by hitting return. 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. diff --git a/docs/quick_tour/logging/README.txt b/docs/quick_tour/logging/README.txt index fb7bde0a7..ff70a1354 100644 --- a/docs/quick_tour/logging/README.txt +++ b/docs/quick_tour/logging/README.txt @@ -1,5 +1,5 @@ hello_world -=============================== +=========== Getting Started --------------- diff --git a/docs/quick_tour/package/README.txt b/docs/quick_tour/package/README.txt index fb7bde0a7..ff70a1354 100644 --- a/docs/quick_tour/package/README.txt +++ b/docs/quick_tour/package/README.txt @@ -1,5 +1,5 @@ hello_world -=============================== +=========== Getting Started --------------- diff --git a/docs/quick_tour/sessions/README.txt b/docs/quick_tour/sessions/README.txt index fb7bde0a7..ff70a1354 100644 --- a/docs/quick_tour/sessions/README.txt +++ b/docs/quick_tour/sessions/README.txt @@ -1,5 +1,5 @@ hello_world -=============================== +=========== Getting Started --------------- diff --git a/docs/quick_tour/sqla_demo/README.txt b/docs/quick_tour/sqla_demo/README.txt index 1659e47ab..27bbff5a7 100644 --- a/docs/quick_tour/sqla_demo/README.txt +++ b/docs/quick_tour/sqla_demo/README.txt @@ -1,5 +1,5 @@ sqla_demo -=============================== +========= Getting Started --------------- diff --git a/docs/quick_tutorial/cookiecutters.rst b/docs/quick_tutorial/cookiecutters.rst index edfd8cd69..337a5c535 100644 --- a/docs/quick_tutorial/cookiecutters.rst +++ b/docs/quick_tutorial/cookiecutters.rst @@ -37,7 +37,7 @@ Steps 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 diff --git a/docs/quick_tutorial/cookiecutters/README.txt b/docs/quick_tutorial/cookiecutters/README.txt index 4b1f31bf3..55c5dcec6 100644 --- a/docs/quick_tutorial/cookiecutters/README.txt +++ b/docs/quick_tutorial/cookiecutters/README.txt @@ -1,5 +1,5 @@ cc_starter -=============================== +========== Getting Started --------------- diff --git a/docs/tutorials/modwsgi/index.rst b/docs/tutorials/modwsgi/index.rst index 690266586..170f2ebc8 100644 --- a/docs/tutorials/modwsgi/index.rst +++ b/docs/tutorials/modwsgi/index.rst @@ -48,7 +48,7 @@ specific path information for commands and files. 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 diff --git a/docs/tutorials/wiki/installation.rst b/docs/tutorials/wiki/installation.rst index 6be826395..de057b1cc 100644 --- a/docs/tutorials/wiki/installation.rst +++ b/docs/tutorials/wiki/installation.rst @@ -50,7 +50,7 @@ If prompted for the first item, accept the default ``yes`` by hitting return. 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 ------------------------------------------------ diff --git a/docs/tutorials/wiki/src/authorization/README.txt b/docs/tutorials/wiki/src/authorization/README.txt index 98683bf8c..5ec53bf9d 100644 --- a/docs/tutorials/wiki/src/authorization/README.txt +++ b/docs/tutorials/wiki/src/authorization/README.txt @@ -1,5 +1,5 @@ myproj -=============================== +====== Getting Started --------------- diff --git a/docs/tutorials/wiki/src/basiclayout/README.txt b/docs/tutorials/wiki/src/basiclayout/README.txt index 98683bf8c..5ec53bf9d 100644 --- a/docs/tutorials/wiki/src/basiclayout/README.txt +++ b/docs/tutorials/wiki/src/basiclayout/README.txt @@ -1,5 +1,5 @@ myproj -=============================== +====== Getting Started --------------- diff --git a/docs/tutorials/wiki/src/installation/README.txt b/docs/tutorials/wiki/src/installation/README.txt index 98683bf8c..5ec53bf9d 100644 --- a/docs/tutorials/wiki/src/installation/README.txt +++ b/docs/tutorials/wiki/src/installation/README.txt @@ -1,5 +1,5 @@ myproj -=============================== +====== Getting Started --------------- diff --git a/docs/tutorials/wiki/src/models/README.txt b/docs/tutorials/wiki/src/models/README.txt index 98683bf8c..5ec53bf9d 100644 --- a/docs/tutorials/wiki/src/models/README.txt +++ b/docs/tutorials/wiki/src/models/README.txt @@ -1,5 +1,5 @@ myproj -=============================== +====== Getting Started --------------- diff --git a/docs/tutorials/wiki/src/tests/README.txt b/docs/tutorials/wiki/src/tests/README.txt index 98683bf8c..5ec53bf9d 100644 --- a/docs/tutorials/wiki/src/tests/README.txt +++ b/docs/tutorials/wiki/src/tests/README.txt @@ -1,5 +1,5 @@ myproj -=============================== +====== Getting Started --------------- diff --git a/docs/tutorials/wiki/src/views/README.txt b/docs/tutorials/wiki/src/views/README.txt index 98683bf8c..5ec53bf9d 100644 --- a/docs/tutorials/wiki/src/views/README.txt +++ b/docs/tutorials/wiki/src/views/README.txt @@ -1,5 +1,5 @@ myproj -=============================== +====== Getting Started --------------- diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst index 9eeb1711d..c61d4360d 100644 --- a/docs/tutorials/wiki2/installation.rst +++ b/docs/tutorials/wiki2/installation.rst @@ -62,7 +62,7 @@ If prompted for the first item, accept the default ``yes`` by hitting return. 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 ------------------------------------------------ diff --git a/docs/tutorials/wiki2/src/authentication/README.txt b/docs/tutorials/wiki2/src/authentication/README.txt index 5e21b8aa4..81102a869 100644 --- a/docs/tutorials/wiki2/src/authentication/README.txt +++ b/docs/tutorials/wiki2/src/authentication/README.txt @@ -1,5 +1,5 @@ myproj -=============================== +====== Getting Started --------------- diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py b/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py index 1b071434c..2d058d874 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py +++ b/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 @@ def view_page(request): 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) diff --git a/docs/tutorials/wiki2/src/authorization/README.txt b/docs/tutorials/wiki2/src/authorization/README.txt index 5e21b8aa4..81102a869 100644 --- a/docs/tutorials/wiki2/src/authorization/README.txt +++ b/docs/tutorials/wiki2/src/authorization/README.txt @@ -1,5 +1,5 @@ myproj -=============================== +====== Getting Started --------------- diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py index 9358993ea..65c12ed3b 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py +++ b/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 @@ def view_page(request): 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) diff --git a/docs/tutorials/wiki2/src/basiclayout/README.txt b/docs/tutorials/wiki2/src/basiclayout/README.txt index 5e21b8aa4..81102a869 100644 --- a/docs/tutorials/wiki2/src/basiclayout/README.txt +++ b/docs/tutorials/wiki2/src/basiclayout/README.txt @@ -1,5 +1,5 @@ myproj -=============================== +====== Getting Started --------------- diff --git a/docs/tutorials/wiki2/src/installation/README.txt b/docs/tutorials/wiki2/src/installation/README.txt index 5e21b8aa4..81102a869 100644 --- a/docs/tutorials/wiki2/src/installation/README.txt +++ b/docs/tutorials/wiki2/src/installation/README.txt @@ -1,5 +1,5 @@ myproj -=============================== +====== Getting Started --------------- diff --git a/docs/tutorials/wiki2/src/models/README.txt b/docs/tutorials/wiki2/src/models/README.txt index 5e21b8aa4..81102a869 100644 --- a/docs/tutorials/wiki2/src/models/README.txt +++ b/docs/tutorials/wiki2/src/models/README.txt @@ -1,5 +1,5 @@ myproj -=============================== +====== Getting Started --------------- diff --git a/docs/tutorials/wiki2/src/tests/README.txt b/docs/tutorials/wiki2/src/tests/README.txt index 5e21b8aa4..81102a869 100644 --- a/docs/tutorials/wiki2/src/tests/README.txt +++ b/docs/tutorials/wiki2/src/tests/README.txt @@ -1,5 +1,5 @@ myproj -=============================== +====== Getting Started --------------- diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py index 9358993ea..65c12ed3b 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py +++ b/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 @@ def view_page(request): 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) diff --git a/docs/tutorials/wiki2/src/views/README.txt b/docs/tutorials/wiki2/src/views/README.txt index 5e21b8aa4..81102a869 100644 --- a/docs/tutorials/wiki2/src/views/README.txt +++ b/docs/tutorials/wiki2/src/views/README.txt @@ -1,5 +1,5 @@ myproj -=============================== +====== Getting Started --------------- diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/default.py b/docs/tutorials/wiki2/src/views/tutorial/views/default.py index bb6300b75..3b95e0f59 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/views/default.py +++ b/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 @@ def view_page(request): 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) diff --git a/docs/whatsnew-1.8.rst b/docs/whatsnew-1.8.rst index adc60b34b..ff16c1a4b 100644 --- a/docs/whatsnew-1.8.rst +++ b/docs/whatsnew-1.8.rst @@ -114,6 +114,13 @@ Minor Feature Additions 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 --------------------------- diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py index b05effbde..a34f0b4db 100644 --- a/pyramid/config/__init__.py +++ b/pyramid/config/__init__.py @@ -110,6 +110,17 @@ class Configurator( 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 @@ class Configurator( .. 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 @@ class Configurator( _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 @@ class Configurator( """ 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): diff --git a/pyramid/config/security.py b/pyramid/config/security.py index 8e4c908d3..20b816161 100644 --- a/pyramid/config/security.py +++ b/pyramid/config/security.py @@ -207,9 +207,14 @@ class SecurityConfiguratorMixin(object): 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, diff --git a/pyramid/config/views.py b/pyramid/config/views.py index 2fc243fac..48c4e3437 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -976,7 +976,7 @@ class ViewsConfiguratorMixin(object): 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 @@ class ViewsConfiguratorMixin(object): # 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: diff --git a/pyramid/tests/test_config/test_init.py b/pyramid/tests/test_config/test_init.py index 53c601537..ab584cc3d 100644 --- a/pyramid/tests/test_config/test_init.py +++ b/pyramid/tests/test_config/test_init.py @@ -141,6 +141,22 @@ class ConfiguratorTests(unittest.TestCase): 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 |
