Skip to content

Commit

Permalink
Merge pull request #1575 from Pylons/fix.shared-view-renderer
Browse files Browse the repository at this point in the history
clone a new RendererHelper per request
  • Loading branch information
mmerickel committed Mar 5, 2015
2 parents af810c5 + ec46918 commit 17997b3
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 1 deletion.
7 changes: 7 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,13 @@ Bug Fixes
- Prevent "parameters to load are deprecated" ``DeprecationWarning``
from setuptools>=11.3. See https://github.com/Pylons/pyramid/pull/1541

- Avoiding sharing the ``IRenderer`` objects across threads when attached to
a view using the `renderer=` argument. These renderers were instantiated
at time of first render and shared between requests, causing potentially
subtle effects like `pyramid.reload_templates = true` failing to work
in `pyramid_mako`. See https://github.com/Pylons/pyramid/pull/1575
and https://github.com/Pylons/pyramid/issues/1268

- Avoiding timing attacks against CSRF tokens.
See https://github.com/Pylons/pyramid/pull/1574

Expand Down
3 changes: 2 additions & 1 deletion pyramid/config/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,6 @@ def rendered_view(self, view):

def _rendered_view(self, view, view_renderer):
def rendered_view(context, request):
renderer = view_renderer
result = view(context, request)
if result.__class__ is Response: # potential common case
response = result
Expand All @@ -367,6 +366,8 @@ def rendered_view(context, request):
name=renderer_name,
package=self.kw.get('package'),
registry = registry)
else:
renderer = view_renderer.clone()
if '__view__' in attrs:
view_inst = attrs.pop('__view__')
else:
Expand Down
3 changes: 3 additions & 0 deletions pyramid/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,9 @@ class IRendererInfo(Interface):
settings = Attribute('The deployment settings dictionary related '
'to the current application')

def clone():
""" Return a shallow copy that does not share any mutable state."""

class IRendererFactory(Interface):
def __call__(info):
""" Return an object that implements
Expand Down
16 changes: 16 additions & 0 deletions pyramid/tests/test_config/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2548,6 +2548,8 @@ def render_view(inself, req, resp, view_inst, ctx):
self.assertEqual(view_inst, view)
self.assertEqual(ctx, context)
return response
def clone(self):
return self
def view(request):
return 'OK'
deriver = self._makeOne(renderer=moo())
Expand Down Expand Up @@ -2585,6 +2587,8 @@ def render_view(inself, req, resp, view_inst, ctx):
self.assertEqual(view_inst, 'view')
self.assertEqual(ctx, context)
return response
def clone(self):
return self
def view(request):
return 'OK'
deriver = self._makeOne(renderer=moo())
Expand Down Expand Up @@ -3179,6 +3183,8 @@ def render_view(inself, req, resp, view_inst, ctx):
self.assertEqual(view_inst.__class__, View)
self.assertEqual(ctx, context)
return response
def clone(self):
return self
class View(object):
def __init__(self, context, request):
pass
Expand All @@ -3203,6 +3209,8 @@ def render_view(inself, req, resp, view_inst, ctx):
self.assertEqual(view_inst.__class__, View)
self.assertEqual(ctx, context)
return response
def clone(self):
return self
class View(object):
def __init__(self, request):
pass
Expand All @@ -3227,6 +3235,8 @@ def render_view(inself, req, resp, view_inst, ctx):
self.assertEqual(view_inst.__class__, View)
self.assertEqual(ctx, context)
return response
def clone(self):
return self
class View:
def __init__(self, context, request):
pass
Expand All @@ -3251,6 +3261,8 @@ def render_view(inself, req, resp, view_inst, ctx):
self.assertEqual(view_inst.__class__, View)
self.assertEqual(ctx, context)
return response
def clone(self):
return self
class View:
def __init__(self, request):
pass
Expand All @@ -3275,6 +3287,8 @@ def render_view(inself, req, resp, view_inst, ctx):
self.assertEqual(view_inst, view)
self.assertEqual(ctx, context)
return response
def clone(self):
return self
class View:
def index(self, context, request):
return {'a':'1'}
Expand All @@ -3297,6 +3311,8 @@ def render_view(inself, req, resp, view_inst, ctx):
self.assertEqual(view_inst, view)
self.assertEqual(ctx, context)
return response
def clone(self):
return self
class View:
def index(self, request):
return {'a':'1'}
Expand Down

0 comments on commit 17997b3

Please sign in to comment.