From 44436e1240f497e598de7b316ffaf7b6b8665452 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Tue, 3 Sep 2013 18:30:56 -0600 Subject: Removed unnecessary side bar with duplicate content The side-bar contained a duplication of code that was already in the main article. Adding a note instead and removing the side-bar provides the same information without nearly as much duplication. --- docs/narr/traversal.rst | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index a60c5ba56..fb4adff61 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -128,6 +128,12 @@ Here's an example of a simple root factory class: def __init__(self, request): pass +..note:: + For the purpose of understanding traversal, and the contents within + this document, the above Root is an analogue to the default root + factory present in Pyramid. The default root factory is very simple and + not very useful unless using :term:`URL dispatch`. + Here's an example of using this root factory within startup configuration, by passing it to an instance of a :term:`Configurator` named ``config``: @@ -154,28 +160,6 @@ Usually a root factory for a traversal-based application will be more complicated than the above ``Root`` class; in particular it may be associated with a database connection or another persistence mechanism. -.. sidebar:: Emulating the Default Root Factory - - For purposes of understanding the default root factory better, we'll note - that you can emulate the default root factory by using this code as an - explicit root factory in your application setup: - - .. code-block:: python - :linenos: - - class Root(object): - def __init__(self, request): - pass - - config = Configurator(root_factory=Root) - - The default root factory is just a really stupid object that has no - behavior or state. Using :term:`traversal` against an application that - uses the resource tree supplied by the default root resource is not very - interesting, because the default root resource has no children. Its - availability is more useful when you're developing an application using - :term:`URL dispatch`. - .. note:: If the items contained within the resource tree are "persistent" (they -- cgit v1.2.3 From af5fa07ca2fcc48ab357c0db4e1301bb960addca Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Fri, 27 Sep 2013 23:45:41 -0500 Subject: support a None value in query string parameters --- CHANGES.txt | 10 ++++++++++ pyramid/encode.py | 6 ++++++ pyramid/tests/test_encode.py | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 8b2210a99..e972c08c3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,6 +6,16 @@ Documentation - Added a "Quick Tutorial" to go with the Quick Tour +Backwards Incompatibilities +--------------------------- + +- The key/values in the ``_query`` parameter of ``request.route_url`` and the + ``query`` parameter of ``request.resource_url`` (and their variants), used + to encode a value of ``None`` as the string ``'None'``, leaving the resulting + query string to be ``a=b&key=None``. The value is now dropped in this + situation, leaving a query string of ``a=b&key``. + See https://github.com/Pylons/pyramid/issues/1119 + 1.5a2 (2013-09-22) ================== diff --git a/pyramid/encode.py b/pyramid/encode.py index 65bc95032..9341f7665 100644 --- a/pyramid/encode.py +++ b/pyramid/encode.py @@ -32,6 +32,10 @@ def urlencode(query, doseq=True): See the Python stdlib documentation for ``urllib.urlencode`` for more information. + + .. versionchanged:: 1.5 + In a key/value pair, if the value is ``None`` then it will be + dropped from the resulting output. """ try: # presumed to be a dictionary @@ -50,6 +54,8 @@ def urlencode(query, doseq=True): x = _enc(x) result += '%s%s=%s' % (prefix, k, x) prefix = '&' + elif v is None: + result += '%s%s' % (prefix, k) else: v = _enc(v) result += '%s%s=%s' % (prefix, k, v) diff --git a/pyramid/tests/test_encode.py b/pyramid/tests/test_encode.py index 736ecb5b3..1e0ecbe20 100644 --- a/pyramid/tests/test_encode.py +++ b/pyramid/tests/test_encode.py @@ -41,6 +41,14 @@ class UrlEncodeTests(unittest.TestCase): result = self._callFUT({'a':1}) self.assertEqual(result, 'a=1') + def test_None_value(self): + result = self._callFUT([('a', None)]) + self.assertEqual(result, 'a') + + def test_None_value_with_prefix(self): + result = self._callFUT([('a', '1'), ('b', None)]) + self.assertEqual(result, 'a=1&b') + class URLQuoteTests(unittest.TestCase): def _callFUT(self, val, safe=''): from pyramid.encode import url_quote -- cgit v1.2.3 From 97f366094b3e31f43d6fe63bd16c86542b7e90e3 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 28 Sep 2013 02:56:50 -0500 Subject: set custom request methods when doing a pview lookup --- CHANGES.txt | 10 ++++++++++ pyramid/scripts/pviews.py | 6 ++++++ pyramid/tests/test_scripts/test_pviews.py | 33 +++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 8b2210a99..550dd0a39 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,6 +6,16 @@ Documentation - Added a "Quick Tutorial" to go with the Quick Tour +Bug Fixes +--------- + +- The ``pviews`` script did not work when a url required custom request + methods in order to perform traversal. Custom methods and descriptors added + via ``pyramid.config.Configurator.add_request_method`` will now be present, + allowing traversal to continue. + See https://github.com/Pylons/pyramid/issues/1104 + + 1.5a2 (2013-09-22) ================== diff --git a/pyramid/scripts/pviews.py b/pyramid/scripts/pviews.py index 081c13e9d..070b08c30 100644 --- a/pyramid/scripts/pviews.py +++ b/pyramid/scripts/pviews.py @@ -63,6 +63,7 @@ class PViewsCommand(object): from pyramid.interfaces import IRequest from pyramid.interfaces import IRootFactory from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IRequestExtensions from pyramid.interfaces import IRequestFactory from pyramid.interfaces import IRoutesMapper from pyramid.interfaces import IView @@ -121,6 +122,11 @@ class PViewsCommand(object): 'PATH_INFO':url, } request = request_factory(environ) + + extensions = registry.queryUtility(IRequestExtensions) + if extensions is not None: + request._set_extensions(extensions) + context = None routes_multiview = None attrs = request.__dict__ diff --git a/pyramid/tests/test_scripts/test_pviews.py b/pyramid/tests/test_scripts/test_pviews.py index 266d1ec90..ee9be6770 100644 --- a/pyramid/tests/test_scripts/test_pviews.py +++ b/pyramid/tests/test_scripts/test_pviews.py @@ -465,6 +465,39 @@ class TestPViewsCommand(unittest.TestCase): ' pyramid.tests.test_scripts.dummy.view.call') self.assertEqual(L[9], ' view predicates (predicate = x)') + def test_find_views_with_request_extensions(self): + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IRequestExtensions + from pyramid.interfaces import IRootFactory + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + from pyramid.registry import Registry + + class DummyExtensions(object): + descriptors = {} + methods = {'foo': lambda r: 'bar'} + + registry = Registry() + registry.registerUtility(DummyExtensions(), IRequestExtensions) + + called = [] + def factory(request): + called.append(request.foo()) + + def view1(): pass + request = dummy.DummyRequest({'PATH_INFO':'/a'}) + root_iface = providedBy(None) + registry.registerUtility(factory, IRootFactory) + registry.registerAdapter(view1, + (IViewClassifier, IRequest, root_iface), + IView, name='a') + self._register_mapper(registry, []) + command = self._makeOne(registry=registry) + result = command._find_view('/a', registry) + self.assertEqual(result, view1) + self.assertEqual(called, ['bar']) + class Test_main(unittest.TestCase): def _callFUT(self, argv): from pyramid.scripts.pviews import main -- cgit v1.2.3 From cefcf8732b9e17bcde5b2b16fdd9c5118caa3f83 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 28 Sep 2013 03:46:30 -0500 Subject: update pviews to work with the bootstrapped request had to fix DummyBootstrap a bit because it fails pretty hard at handling part of bootstrap contract in which the request passed in should be enhanced with the registry, and returned. In some cases the wrong request or the wrong registry were being returned if the test case actually specified a registry or request. --- pyramid/scripts/pviews.py | 33 +++--------- pyramid/tests/test_scripts/dummy.py | 7 ++- pyramid/tests/test_scripts/test_pviews.py | 84 ++++++++++++------------------- 3 files changed, 45 insertions(+), 79 deletions(-) diff --git a/pyramid/scripts/pviews.py b/pyramid/scripts/pviews.py index 070b08c30..504f583b7 100644 --- a/pyramid/scripts/pviews.py +++ b/pyramid/scripts/pviews.py @@ -4,6 +4,7 @@ import textwrap from pyramid.interfaces import IMultiView from pyramid.paster import bootstrap +from pyramid.request import Request from pyramid.scripts.common import parse_vars def main(argv=sys.argv, quiet=False): @@ -52,7 +53,7 @@ class PViewsCommand(object): infos.append(info) return infos - def _find_view(self, url, registry): + def _find_view(self, request): """ Accept ``url`` and ``registry``; create a :term:`request` and find a :app:`Pyramid` view based on introspection of :term:`view @@ -63,23 +64,19 @@ class PViewsCommand(object): from pyramid.interfaces import IRequest from pyramid.interfaces import IRootFactory from pyramid.interfaces import IRouteRequest - from pyramid.interfaces import IRequestExtensions - from pyramid.interfaces import IRequestFactory from pyramid.interfaces import IRoutesMapper from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import ITraverser - from pyramid.request import Request from pyramid.traversal import DefaultRootFactory from pyramid.traversal import ResourceTreeTraverser + registry = request.registry q = registry.queryUtility root_factory = q(IRootFactory, default=DefaultRootFactory) routes_mapper = q(IRoutesMapper) - request_factory = q(IRequestFactory, default=Request) adapters = registry.adapters - request = None @implementer(IMultiView) class RoutesMultiView(object): @@ -112,25 +109,9 @@ class PViewsCommand(object): view.__view_attr__ = '' self.views.append((None, view, None)) - - # create the request - environ = { - 'wsgi.url_scheme':'http', - 'SERVER_NAME':'localhost', - 'SERVER_PORT':'8080', - 'REQUEST_METHOD':'GET', - 'PATH_INFO':url, - } - request = request_factory(environ) - - extensions = registry.queryUtility(IRequestExtensions) - if extensions is not None: - request._set_extensions(extensions) - context = None routes_multiview = None attrs = request.__dict__ - attrs['registry'] = registry request_iface = IRequest # find the root object @@ -242,9 +223,10 @@ class PViewsCommand(object): if not url.startswith('/'): url = '/%s' % url - env = self.bootstrap[0](config_uri, options=parse_vars(self.args[2:])) - registry = env['registry'] - view = self._find_view(url, registry) + request = Request.blank(url) + env = self.bootstrap[0](config_uri, options=parse_vars(self.args[2:]), + request=request) + view = self._find_view(request) self.out('') self.out("URL = %s" % url) self.out('') @@ -263,5 +245,6 @@ class PViewsCommand(object): else: self.out(" Not found.") self.out('') + env['closer']() return 0 diff --git a/pyramid/tests/test_scripts/dummy.py b/pyramid/tests/test_scripts/dummy.py index d580203af..366aa00b5 100644 --- a/pyramid/tests/test_scripts/dummy.py +++ b/pyramid/tests/test_scripts/dummy.py @@ -146,10 +146,13 @@ class DummyBootstrap(object): def __call__(self, *a, **kw): self.a = a self.kw = kw + registry = kw.get('registry', self.registry) + request = kw.get('request', self.request) + request.registry = registry return { 'app': self.app, - 'registry': self.registry, - 'request': self.request, + 'registry': registry, + 'request': request, 'root': self.root, 'root_factory': self.root_factory, 'closer': self.closer, diff --git a/pyramid/tests/test_scripts/test_pviews.py b/pyramid/tests/test_scripts/test_pviews.py index ee9be6770..b162144a7 100644 --- a/pyramid/tests/test_scripts/test_pviews.py +++ b/pyramid/tests/test_scripts/test_pviews.py @@ -12,6 +12,12 @@ class TestPViewsCommand(unittest.TestCase): cmd.args = ('/foo/bar/myapp.ini#myapp',) return cmd + def _makeRequest(self, url, registry): + from pyramid.request import Request + request = Request.blank('/a') + request.registry = registry + return request + def _register_mapper(self, registry, routes): from pyramid.interfaces import IRoutesMapper mapper = dummy.DummyMapper(*routes) @@ -22,7 +28,8 @@ class TestPViewsCommand(unittest.TestCase): registry = Registry() self._register_mapper(registry, []) command = self._makeOne(registry) - result = command._find_view('/a', registry) + request = self._makeRequest('/a', registry) + result = command._find_view(request) self.assertEqual(result, None) def test__find_view_no_match_multiview_registered(self): @@ -45,7 +52,8 @@ class TestPViewsCommand(unittest.TestCase): IMultiView) self._register_mapper(registry, []) command = self._makeOne(registry=registry) - result = command._find_view('/x', registry) + request = self._makeRequest('/x', registry) + result = command._find_view(request) self.assertEqual(result, None) def test__find_view_traversal(self): @@ -65,7 +73,8 @@ class TestPViewsCommand(unittest.TestCase): IView, name='a') self._register_mapper(registry, []) command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) + request = self._makeRequest('/a', registry) + result = command._find_view(request) self.assertEqual(result, view1) def test__find_view_traversal_multiview(self): @@ -89,7 +98,8 @@ class TestPViewsCommand(unittest.TestCase): IMultiView, name='a') self._register_mapper(registry, []) command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) + request = self._makeRequest('/a', registry) + result = command._find_view(request) self.assertEqual(result, view) def test__find_view_route_no_multiview(self): @@ -117,7 +127,8 @@ class TestPViewsCommand(unittest.TestCase): dummy.DummyRoute('b', '/b', factory=Factory)] self._register_mapper(registry, routes) command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) + request = self._makeRequest('/a', registry) + result = command._find_view(request) self.assertEqual(result, view) def test__find_view_route_multiview_no_view_registered(self): @@ -147,7 +158,8 @@ class TestPViewsCommand(unittest.TestCase): dummy.DummyRoute('b', '/a', matchdict={})] self._register_mapper(registry, routes) command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) + request = self._makeRequest('/a', registry) + result = command._find_view(request) self.assertTrue(IMultiView.providedBy(result)) def test__find_view_route_multiview(self): @@ -185,7 +197,8 @@ class TestPViewsCommand(unittest.TestCase): dummy.DummyRoute('b', '/a', matchdict={})] self._register_mapper(registry, routes) command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) + request = self._makeRequest('/a', registry) + result = command._find_view(request) self.assertTrue(IMultiView.providedBy(result)) self.assertEqual(len(result.views), 2) self.assertTrue((None, view1, None) in result.views) @@ -228,7 +241,7 @@ class TestPViewsCommand(unittest.TestCase): command = self._makeOne(registry=registry) L = [] command.out = L.append - command._find_view = lambda arg1, arg2: None + command._find_view = lambda arg1: None command.args = ('/foo/bar/myapp.ini#myapp', '/a') result = command.run() self.assertEqual(result, 0) @@ -241,7 +254,7 @@ class TestPViewsCommand(unittest.TestCase): command = self._makeOne(registry=registry) L = [] command.out = L.append - command._find_view = lambda arg1, arg2: None + command._find_view = lambda arg1: None command.args = ('/foo/bar/myapp.ini#myapp', 'a') result = command.run() self.assertEqual(result, 0) @@ -255,7 +268,7 @@ class TestPViewsCommand(unittest.TestCase): L = [] command.out = L.append view = dummy.DummyView(context='context', view_name='a') - command._find_view = lambda arg1, arg2: view + command._find_view = lambda arg1: view command.args = ('/foo/bar/myapp.ini#myapp', '/a') result = command.run() self.assertEqual(result, 0) @@ -273,7 +286,7 @@ class TestPViewsCommand(unittest.TestCase): command.out = L.append def view(): pass view.__request_attrs__ = {'context': 'context', 'view_name': 'a'} - command._find_view = lambda arg1, arg2: view + command._find_view = lambda arg1: view command.args = ('/foo/bar/myapp.ini#myapp', '/a') result = command.run() self.assertEqual(result, 0) @@ -291,7 +304,7 @@ class TestPViewsCommand(unittest.TestCase): command.out = L.append view = dummy.DummyView(context='context', view_name='a') view.__permission__ = 'test' - command._find_view = lambda arg1, arg2: view + command._find_view = lambda arg1: view command.args = ('/foo/bar/myapp.ini#myapp', '/a') result = command.run() self.assertEqual(result, 0) @@ -312,7 +325,7 @@ class TestPViewsCommand(unittest.TestCase): predicate.text = lambda *arg: "predicate = x" view = dummy.DummyView(context='context', view_name='a') view.__predicates__ = [predicate] - command._find_view = lambda arg1, arg2: view + command._find_view = lambda arg1: view command.args = ('/foo/bar/myapp.ini#myapp', '/a') result = command.run() self.assertEqual(result, 0) @@ -332,7 +345,7 @@ class TestPViewsCommand(unittest.TestCase): route = dummy.DummyRoute('a', '/a', matchdict={}) view = dummy.DummyView(context='context', view_name='a', matched_route=route, subpath='') - command._find_view = lambda arg1, arg2: view + command._find_view = lambda arg1: view command.args = ('/foo/bar/myapp.ini#myapp', '/a') result = command.run() self.assertEqual(result, 0) @@ -360,7 +373,7 @@ class TestPViewsCommand(unittest.TestCase): view_name='a1') multiview2 = dummy.DummyMultiView(multiview1, context='context', view_name='a') - command._find_view = lambda arg1, arg2: multiview2 + command._find_view = lambda arg1: multiview2 command.args = ('/foo/bar/myapp.ini#myapp', '/a') result = command.run() self.assertEqual(result, 0) @@ -383,7 +396,7 @@ class TestPViewsCommand(unittest.TestCase): route = dummy.DummyRoute('a', '/a', matchdict={}, predicate=predicate) view = dummy.DummyView(context='context', view_name='a', matched_route=route, subpath='') - command._find_view = lambda arg1, arg2: view + command._find_view = lambda arg1: view command.args = ('/foo/bar/myapp.ini#myapp', '/a') result = command.run() self.assertEqual(result, 0) @@ -409,7 +422,7 @@ class TestPViewsCommand(unittest.TestCase): view.__name__ = 'view' view.__view_attr__ = 'call' multiview = dummy.DummyMultiView(view, context='context', view_name='a') - command._find_view = lambda arg1, arg2: multiview + command._find_view = lambda arg1: multiview command.args = ('/foo/bar/myapp.ini#myapp', '/a') result = command.run() self.assertEqual(result, 0) @@ -430,7 +443,7 @@ class TestPViewsCommand(unittest.TestCase): view.__view_attr__ = 'call' view.__permission__ = 'test' multiview = dummy.DummyMultiView(view, context='context', view_name='a') - command._find_view = lambda arg1, arg2: multiview + command._find_view = lambda arg1: multiview command.args = ('/foo/bar/myapp.ini#myapp', '/a') result = command.run() self.assertEqual(result, 0) @@ -454,7 +467,7 @@ class TestPViewsCommand(unittest.TestCase): view.__view_attr__ = 'call' view.__predicates__ = [predicate] multiview = dummy.DummyMultiView(view, context='context', view_name='a') - command._find_view = lambda arg1, arg2: multiview + command._find_view = lambda arg1: multiview command.args = ('/foo/bar/myapp.ini#myapp', '/a') result = command.run() self.assertEqual(result, 0) @@ -465,39 +478,6 @@ class TestPViewsCommand(unittest.TestCase): ' pyramid.tests.test_scripts.dummy.view.call') self.assertEqual(L[9], ' view predicates (predicate = x)') - def test_find_views_with_request_extensions(self): - from zope.interface import providedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IRequestExtensions - from pyramid.interfaces import IRootFactory - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IView - from pyramid.registry import Registry - - class DummyExtensions(object): - descriptors = {} - methods = {'foo': lambda r: 'bar'} - - registry = Registry() - registry.registerUtility(DummyExtensions(), IRequestExtensions) - - called = [] - def factory(request): - called.append(request.foo()) - - def view1(): pass - request = dummy.DummyRequest({'PATH_INFO':'/a'}) - root_iface = providedBy(None) - registry.registerUtility(factory, IRootFactory) - registry.registerAdapter(view1, - (IViewClassifier, IRequest, root_iface), - IView, name='a') - self._register_mapper(registry, []) - command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) - self.assertEqual(result, view1) - self.assertEqual(called, ['bar']) - class Test_main(unittest.TestCase): def _callFUT(self, argv): from pyramid.scripts.pviews import main -- cgit v1.2.3 From f24ac4c471a458aec0cde232925c8fab652bafcc Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Wed, 2 Oct 2013 11:31:47 -0500 Subject: Docs: project.rst: Eliminate reduncency and better explain renderer. --- docs/narr/project.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 602f15fef..09e07ee6d 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -871,18 +871,17 @@ specification` that specifies the ``mytemplate.pt`` file within the ``templates`` directory of the ``myproject`` package. The asset specification could have also been specified as ``myproject:templates/mytemplate.pt``; the leading package name and colon is -optional. The template file it actually points to is a :term:`Chameleon` ZPT -template file. +optional. The template file pointed to is a :term:`Chameleon` ZPT +template file (``templates/my_template.pt``). This view callable function is handed a single piece of information: the :term:`request`. The *request* is an instance of the :term:`WebOb` ``Request`` class representing the browser's request to our server. -This view returns a dictionary. When this view is invoked, a -:term:`renderer` converts the dictionary returned by the view into HTML, and -returns the result as the :term:`response`. This view is configured to -invoke a renderer which uses a :term:`Chameleon` ZPT template -(``templates/my_template.pt``). +This view is configured to invoke a :term;`renderer` on a template. The +dictionary the view returns (on line 6) provides the value the renderer +substitutes into the template when generating HTML. The renderer then +returns the HTML in a :term:`response`. See :ref:`views_which_use_a_renderer` for more information about how views, renderers, and templates relate and cooperate. -- cgit v1.2.3 From 77edee7e91356f4f0f1d12c2dd159965b0576109 Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Wed, 2 Oct 2013 11:33:50 -0500 Subject: Docs: project.rst: Emphasize key takeaway; use dicts to supply values to templates. --- docs/narr/project.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 09e07ee6d..359fb31d3 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -878,6 +878,8 @@ This view callable function is handed a single piece of information: the :term:`request`. The *request* is an instance of the :term:`WebOb` ``Request`` class representing the browser's request to our server. +.. note:: Dictionaries (typically) provide values to :term:`template`s. + This view is configured to invoke a :term;`renderer` on a template. The dictionary the view returns (on line 6) provides the value the renderer substitutes into the template when generating HTML. The renderer then -- cgit v1.2.3 From 68d16988404aec339fde1df7822c783d3ea23af6 Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Wed, 2 Oct 2013 11:35:34 -0500 Subject: Docs: renderers.rst: Explain typical renderer usage. --- docs/narr/renderers.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst index 3059aef35..235dbaf83 100644 --- a/docs/narr/renderers.rst +++ b/docs/narr/renderers.rst @@ -49,10 +49,14 @@ Writing View Callables Which Use a Renderer ------------------------------------------- As we've seen, a view callable needn't always return a Response object. -Instead, it may return an arbitrary Python object, with the expectation -that a :term:`renderer` will convert that object into a response instance on -your behalf. Some renderers use a templating system; other renderers use -object serialization techniques. +Instead, it may return an arbitrary Python object, with the expectation that +a :term:`renderer` will convert that object into a response instance on your +behalf. Some renderers use a templating system; other renderers use object +serialization techniques. Because renderers inject variable data into some +output (otherwise a static Response object could be returned) the renderer +must have some means of identifying the data and mapping its transformation +into the desired output. Often, as the means of providing this mapping, the +object supplied to the renderer is a Python dictionary. View configuration can vary the renderer associated with a view callable via the ``renderer`` attribute. For example, this call to -- cgit v1.2.3 From 190b5644c473286f8066c9eb430c567dfdeb4913 Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Wed, 2 Oct 2013 13:36:05 -0500 Subject: Docs: sessions.rst: Explain example. --- docs/narr/sessions.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst index 358977089..eafa9dbf9 100644 --- a/docs/narr/sessions.rst +++ b/docs/narr/sessions.rst @@ -99,6 +99,10 @@ example: else: return Response('Fred was not in the session') +The first time this view is invoked produces ``Fred was not in the +session``. Subsequent invocations produce ``Fred was in the +session``. + You can use a session much like a Python dictionary. It supports all dictionary methods, along with some extra attributes, and methods. -- cgit v1.2.3 From b31cdc5beb173716235a026d264dafde12fea109 Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Wed, 2 Oct 2013 14:47:01 -0500 Subject: Docs: sessions.rst: Sessions only work when the client cooperates. --- docs/narr/sessions.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst index eafa9dbf9..db1e0ea20 100644 --- a/docs/narr/sessions.rst +++ b/docs/narr/sessions.rst @@ -101,7 +101,8 @@ example: The first time this view is invoked produces ``Fred was not in the session``. Subsequent invocations produce ``Fred was in the -session``. +session``, assuming of course that the client side maintains the +session's identity across multiple requests. You can use a session much like a Python dictionary. It supports all dictionary methods, along with some extra attributes, and methods. -- cgit v1.2.3 From 5bf27497638ad607f0e42feb10145cd6720b74d3 Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Wed, 2 Oct 2013 15:20:59 -0500 Subject: Docs: Make statements more concreate regards renderers getting data from dictionaries. --- docs/narr/project.rst | 2 +- docs/narr/renderers.rst | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 9a1ba190d..61b6ae316 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -878,7 +878,7 @@ This view callable function is handed a single piece of information: the :term:`request`. The *request* is an instance of the :term:`WebOb` ``Request`` class representing the browser's request to our server. -.. note:: Dictionaries (typically) provide values to :term:`template`s. +.. note:: Dictionaries provide values to :term:`template`s. This view is configured to invoke a :term;`renderer` on a template. The dictionary the view returns (on line 6) provides the value the renderer diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst index 235dbaf83..4046c67fa 100644 --- a/docs/narr/renderers.rst +++ b/docs/narr/renderers.rst @@ -52,11 +52,9 @@ As we've seen, a view callable needn't always return a Response object. Instead, it may return an arbitrary Python object, with the expectation that a :term:`renderer` will convert that object into a response instance on your behalf. Some renderers use a templating system; other renderers use object -serialization techniques. Because renderers inject variable data into some -output (otherwise a static Response object could be returned) the renderer -must have some means of identifying the data and mapping its transformation -into the desired output. Often, as the means of providing this mapping, the -object supplied to the renderer is a Python dictionary. +serialization techniques. In practice, renderers obtain application data +values from Python dictionaries so, in practice, view callables which use +renderers return Python dictionaries. View configuration can vary the renderer associated with a view callable via the ``renderer`` attribute. For example, this call to -- cgit v1.2.3 From 47e13e042c270f9ffb3ac86b294e89ec4b1fef6a Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Wed, 2 Oct 2013 15:27:32 -0500 Subject: Docs: project.rst: Fix markup failure just introduced. --- docs/narr/project.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 61b6ae316..2acc81e17 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -880,7 +880,7 @@ This view callable function is handed a single piece of information: the .. note:: Dictionaries provide values to :term:`template`s. -This view is configured to invoke a :term;`renderer` on a template. The +This view is configured to invoke a :term:`renderer` on a template. The dictionary the view returns (on line 6) provides the value the renderer substitutes into the template when generating HTML. The renderer then returns the HTML in a :term:`response`. -- cgit v1.2.3 From 15afe5e7fb5dafa570faf055eb4b4a4518349409 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 2 Oct 2013 16:38:28 -0400 Subject: render the qs such that when provided with None as a value, it will render the key plus the equal sign --- pyramid/encode.py | 2 +- pyramid/tests/test_encode.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pyramid/encode.py b/pyramid/encode.py index 9341f7665..9e190bc21 100644 --- a/pyramid/encode.py +++ b/pyramid/encode.py @@ -55,7 +55,7 @@ def urlencode(query, doseq=True): result += '%s%s=%s' % (prefix, k, x) prefix = '&' elif v is None: - result += '%s%s' % (prefix, k) + result += '%s%s=' % (prefix, k) else: v = _enc(v) result += '%s%s=%s' % (prefix, k, v) diff --git a/pyramid/tests/test_encode.py b/pyramid/tests/test_encode.py index 1e0ecbe20..908249877 100644 --- a/pyramid/tests/test_encode.py +++ b/pyramid/tests/test_encode.py @@ -43,11 +43,15 @@ class UrlEncodeTests(unittest.TestCase): def test_None_value(self): result = self._callFUT([('a', None)]) - self.assertEqual(result, 'a') + self.assertEqual(result, 'a=') def test_None_value_with_prefix(self): result = self._callFUT([('a', '1'), ('b', None)]) - self.assertEqual(result, 'a=1&b') + self.assertEqual(result, 'a=1&b=') + + def test_None_value_with_prefix_values(self): + result = self._callFUT([('a', '1'), ('b', None), ('c', None)]) + self.assertEqual(result, 'a=1&b=&c=') class URLQuoteTests(unittest.TestCase): def _callFUT(self, val, safe=''): -- cgit v1.2.3 From 4122733091d0204b22d7acedfdf985caed17f93f Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 2 Oct 2013 17:29:39 -0400 Subject: get rid of note that appears to be explained in the next para --- docs/narr/project.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 2acc81e17..bfd00d3a0 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -878,8 +878,6 @@ This view callable function is handed a single piece of information: the :term:`request`. The *request* is an instance of the :term:`WebOb` ``Request`` class representing the browser's request to our server. -.. note:: Dictionaries provide values to :term:`template`s. - This view is configured to invoke a :term:`renderer` on a template. The dictionary the view returns (on line 6) provides the value the renderer substitutes into the template when generating HTML. The renderer then -- cgit v1.2.3 From ab2fedf7adaec0a56a69beed35312c88d7961c6c Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 2 Oct 2013 17:47:17 -0400 Subject: fix the docs build and get rid of stray references to Beaker --- docs/glossary.rst | 9 ++++++++- docs/narr/project.rst | 2 +- docs/narr/sessions.rst | 8 ++++---- docs/quick_tour.rst | 7 +++---- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/glossary.rst b/docs/glossary.rst index 7dc69c7c4..398f945a4 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -1009,7 +1009,8 @@ Glossary Green Unicorn Aka ``gunicorn``, a fast :term:`WSGI` server that runs on UNIX under - Python 2.6+ or Python 3.1+. See http://gunicorn.org/ for detailed information. + Python 2.6+ or Python 3.1+. See http://gunicorn.org/ for detailed + information. predicate factory A callable which is used by a third party during the registration of a @@ -1021,3 +1022,9 @@ Glossary A Python :term:`distribution` that uses Pyramid's extensibility to plug into a Pyramid application and provide extra, configurable services. + + pyramid_redis_sessions + A package by Eric Rasmussen which allows you to store Pyramid session + data in a Redis database. See + https://pypi.python.org/pypi/pyramid_redis_sessions for more information. + diff --git a/docs/narr/project.rst b/docs/narr/project.rst index bfd00d3a0..9451f41b1 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -977,7 +977,7 @@ named ``views`` instead of within a single ``views.py`` file, you might: - *Move* the existing ``views.py`` file to a file inside the new ``views`` directory named, say, ``blog.py``. Because the ``templates`` directory remains in the ``myproject`` package, the template :term:`asset - specification`s in ``blog.py`` must now be fully qualified with the + specification` values in ``blog.py`` must now be fully qualified with the project's package name (``myproject:templates/blog.pt``). You can then continue to add view callable functions to the ``blog.py`` diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst index f4da5d82a..649d22bd2 100644 --- a/docs/narr/sessions.rst +++ b/docs/narr/sessions.rst @@ -159,10 +159,10 @@ Some gotchas: Using Alternate Session Factories --------------------------------- -At the time of this writing, exactly one project-endorsed alternate session -factory exists named``pyramid_redis_sessions``. It can be downloaded from PyPI. -It uses Redis as a backend. It is the recommended persistent session solution -at the time of this writing. +At the time of this writing, exactly one project-endorsed alternate session +factory exists named :term:`pyramid_redis_sessions`. It can be downloaded from +PyPI. It uses the Redis database as a backend. It is the recommended +persistent session solution at the time of this writing. .. index:: single: session factory (custom) diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst index 98584e608..2db18c8a7 100644 --- a/docs/quick_tour.rst +++ b/docs/quick_tour.rst @@ -729,8 +729,8 @@ that requires semi-permanent data to be saved. For example, a shopping cart. This is called a :term:`session`. Pyramid has basic built-in support for sessions, with add-ons such as -*Beaker* (or your own custom sessioning engine) that provide richer -session support. Let's take a look at the +``pyramid_redis_sessions`` (or your own custom sessioning engine) that provide +richer session support. Let's take a look at the :doc:`built-in sessioning support <../narr/sessions>`. In our ``__init__.py`` we first import the kind of sessioning we want: @@ -768,8 +768,7 @@ Jinja2 template: .. seealso:: See Also: :ref:`Quick Tutorial Sessions `, :ref:`sessions_chapter`, :ref:`flash_messages`, - :ref:`session_module`, and - :ref:`Beaker sessioning middleware ` + :ref:`session_module`, and :term:`pyramid_redis_sessions`. Databases ========= -- cgit v1.2.3 From 79d9d6862ff906aa0bf26968e0ba763c54775b5c Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 2 Oct 2013 18:12:29 -0400 Subject: correct the explanation of the behavior change --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index c8b79d65f..051328a02 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -36,7 +36,7 @@ Backwards Incompatibilities ``query`` parameter of ``request.resource_url`` (and their variants), used to encode a value of ``None`` as the string ``'None'``, leaving the resulting query string to be ``a=b&key=None``. The value is now dropped in this - situation, leaving a query string of ``a=b&key``. + situation, leaving a query string of ``a=b&key=``. See https://github.com/Pylons/pyramid/issues/1119 1.5a2 (2013-09-22) -- cgit v1.2.3 From 96188ac4b74cb1e67cb95afdb9519ecd8934d688 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 2 Oct 2013 18:56:56 -0400 Subject: rearrange the fix --- docs/glossary.rst | 23 ++++++++++++++--------- docs/narr/traversal.rst | 14 +++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/glossary.rst b/docs/glossary.rst index 398f945a4..406b81778 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -505,15 +505,20 @@ Glossary available as its ``__parent__`` attribute. root factory - The "root factory" of a :app:`Pyramid` application is called - on every request sent to the application. The root factory - returns the traversal root of an application. It is - conventionally named ``get_root``. An application may supply a - root factory to :app:`Pyramid` during the construction of a - :term:`Configurator`. If a root factory is not supplied, the - application uses a default root object. Use of the default root - object is useful in application which use :term:`URL dispatch` for - all URL-to-view code mappings. + The "root factory" of a :app:`Pyramid` application is called on every + request sent to the application. The root factory returns the traversal + root of an application. It is conventionally named ``get_root``. An + application may supply a root factory to :app:`Pyramid` during the + construction of a :term:`Configurator`. If a root factory is not + supplied, the application creates a default root object using the + :term:`default root factory`. + + default root factory + If an application does not register a :term:`root factory` at Pyramid + configuration time, a *default* root factory is used to created the + default root object. Use of the default root object is useful in + application which use :term:`URL dispatch` for all URL-to-view code + mappings, and does not (knowingly) use traversal otherwise. SQLAlchemy `SQLAlchemy `_ is an object diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index fb4adff61..454bb5620 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -128,12 +128,6 @@ Here's an example of a simple root factory class: def __init__(self, request): pass -..note:: - For the purpose of understanding traversal, and the contents within - this document, the above Root is an analogue to the default root - factory present in Pyramid. The default root factory is very simple and - not very useful unless using :term:`URL dispatch`. - Here's an example of using this root factory within startup configuration, by passing it to an instance of a :term:`Configurator` named ``config``: @@ -152,13 +146,15 @@ refer to a root factory defined in a different module. If no :term:`root factory` is passed to the :app:`Pyramid` :term:`Configurator` constructor, or if the ``root_factory`` value -specified is ``None``, a *default* root factory is used. The default +specified is ``None``, a :term:`default root factory` is used. The default root factory always returns a resource that has no child resources; it is effectively empty. Usually a root factory for a traversal-based application will be more -complicated than the above ``Root`` class; in particular it may be -associated with a database connection or another persistence mechanism. +complicated than the above ``Root`` class; in particular it may be associated +with a database connection or another persistence mechanism. The above +``Root`` class is analogous to the default root factory present in Pyramid. The +default root factory is very simple and not very useful. .. note:: -- cgit v1.2.3