diff options
| -rw-r--r-- | CHANGES.txt | 7 | ||||
| -rw-r--r-- | pyramid/config/views.py | 3 | ||||
| -rw-r--r-- | pyramid/interfaces.py | 3 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_views.py | 16 |
4 files changed, 28 insertions, 1 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 9b10c0831..b7a8f4764 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -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 diff --git a/pyramid/config/views.py b/pyramid/config/views.py index 24c592f7a..aba28467d 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -349,7 +349,6 @@ class ViewDeriver(object): 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 @@ -367,6 +366,8 @@ class ViewDeriver(object): 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: diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index 4c171f9cc..bab91b0ee 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -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 diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py index 36c86f78c..180050941 100644 --- a/pyramid/tests/test_config/test_views.py +++ b/pyramid/tests/test_config/test_views.py @@ -2548,6 +2548,8 @@ class TestViewDeriver(unittest.TestCase): 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()) @@ -2585,6 +2587,8 @@ class TestViewDeriver(unittest.TestCase): 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()) @@ -3179,6 +3183,8 @@ class TestViewDeriver(unittest.TestCase): 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 @@ -3203,6 +3209,8 @@ class TestViewDeriver(unittest.TestCase): 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 @@ -3227,6 +3235,8 @@ class TestViewDeriver(unittest.TestCase): 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 @@ -3251,6 +3261,8 @@ class TestViewDeriver(unittest.TestCase): self.assertEqual(view_inst.__class__, View) self.assertEqual(ctx, context) return response + def clone(self): + return self class View: def __init__(self, request): pass @@ -3275,6 +3287,8 @@ class TestViewDeriver(unittest.TestCase): 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'} @@ -3297,6 +3311,8 @@ class TestViewDeriver(unittest.TestCase): 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'} |
