From 5c05cd9837adcd9b0ee0ee775added2a6d3b6688 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Wed, 8 Jan 2020 01:15:02 -0600 Subject: invoke finished callbacks in prepare/bootstrap closers --- tests/test_scripting.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'tests') diff --git a/tests/test_scripting.py b/tests/test_scripting.py index 8f74f35f8..b8a18f57e 100644 --- a/tests/test_scripting.py +++ b/tests/test_scripting.py @@ -1,3 +1,4 @@ +from collections import deque import unittest @@ -162,6 +163,20 @@ class Test_prepare(unittest.TestCase): self.assertEqual(request.context, root) self.assertEqual(request.registry, registry) + def test_closer_invokes_finished_callbacks(self): + finish_called = [False] + + def finished_callback(request): + finish_called[0] = True + + request = DummyRequest({}) + request.registry = self._makeRegistry() + info = self._callFUT(request=request) + request.add_finished_callback(finished_callback) + closer = info['closer'] + closer() + self.assertTrue(finish_called[0]) + class Test__make_request(unittest.TestCase): def _callFUT(self, path='/', registry=None): @@ -234,6 +249,15 @@ class DummyRequest(object): def __init__(self, environ): self.environ = environ + self.finished_callbacks = deque() + + def add_finished_callback(self, cb): + self.finished_callbacks.append(cb) + + def _process_finished_callbacks(self): + while self.finished_callbacks: + cb = self.finished_callbacks.popleft() + cb(self) class DummyExtensions: -- cgit v1.2.3 From 8415bc1778ea080aeed69ef2d752f3d0063e26f9 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Wed, 8 Jan 2020 01:15:31 -0600 Subject: add a RequestLocalCache class --- tests/test_request.py | 100 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/test_request.py b/tests/test_request.py index bbf6aa47c..3cff4bb53 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -535,10 +535,6 @@ class Test_apply_request_extensions(unittest.TestCase): self.assertEqual(request.foo('abc'), 'abc') -class Dummy(object): - pass - - class Test_subclassing_Request(unittest.TestCase): def test_subclass(self): from pyramid.interfaces import IRequest @@ -598,14 +594,108 @@ class Test_subclassing_Request(unittest.TestCase): self.assertTrue(IRequest.implementedBy(RequestSub)) +class TestRequestLocalCache(unittest.TestCase): + def _makeOne(self): + from pyramid.request import RequestLocalCache + + return RequestLocalCache() + + def test_it_works_with_functions(self): + cache = self._makeOne() + a = [0] + + @cache + def foo(request): + a[0] += 1 + return a[0] + + req1 = DummyRequest() + req2 = DummyRequest() + self.assertEqual(foo(req1), 1) + self.assertEqual(foo(req2), 2) + self.assertEqual(foo(req1), 1) + self.assertEqual(foo(req2), 2) + self.assertEqual(len(req1.finished_callbacks), 1) + self.assertEqual(len(req2.finished_callbacks), 1) + + def test_it_works_with_methods(self): + cache = self._makeOne() + a = [0] + + class DummyPolicy: + @cache + def foo(self, request): + a[0] += 1 + return a[0] + + policy = DummyPolicy() + req1 = DummyRequest() + req2 = DummyRequest() + self.assertEqual(policy.foo(req1), 1) + self.assertEqual(policy.foo(req2), 2) + self.assertEqual(policy.foo(req1), 1) + self.assertEqual(policy.foo(req2), 2) + self.assertEqual(len(req1.finished_callbacks), 1) + self.assertEqual(len(req2.finished_callbacks), 1) + + def test_clear_works(self): + cache = self._makeOne() + a = [0] + + @cache + def foo(request): + a[0] += 1 + return a[0] + + req = DummyRequest() + self.assertEqual(foo(req), 1) + self.assertEqual(len(req.finished_callbacks), 1) + cache.clear(req) + self.assertEqual(foo(req), 2) + self.assertEqual(len(req.finished_callbacks), 2) + + def test_set_overrides_current_value(self): + cache = self._makeOne() + a = [0] + + @cache + def foo(request): + a[0] += 1 + return a[0] + + req = DummyRequest() + self.assertEqual(foo(req), 1) + self.assertEqual(len(req.finished_callbacks), 1) + cache.set(req, 8) + self.assertEqual(foo(req), 8) + self.assertEqual(len(req.finished_callbacks), 1) + self.assertEqual(cache.get(req), 8) + + def test_get_works(self): + cache = self._makeOne() + req = DummyRequest() + self.assertIs(cache.get(req), cache.NO_VALUE) + cache.set(req, 2) + self.assertIs(cache.get(req), 2) + + +class Dummy(object): + pass + + class DummyRequest(object): def __init__(self, environ=None): if environ is None: environ = {} self.environ = environ + self.response_callbacks = [] + self.finished_callbacks = [] def add_response_callback(self, callback): - self.response_callbacks = [callback] + self.response_callbacks.append(callback) + + def add_finished_callback(self, callback): + self.finished_callbacks.append(callback) def get_response(self, app): return app -- cgit v1.2.3 From 79d6a38a66a68231e651a6c81e784ab1a78c07de Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 9 Jan 2020 00:57:15 -0600 Subject: fix paradigm to avoid incorrect usages It's almost impossible to create a decorator that works on both methods and functions, but more importantly the original approach was sharing a cache across instances of the policy. It needed to be local to the policy instance, but shared across requests. The new example demonstrates that. The cache is also much more flexible in its usage patterns now. --- tests/test_request.py | 82 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 32 deletions(-) (limited to 'tests') diff --git a/tests/test_request.py b/tests/test_request.py index 3cff4bb53..a36bd238c 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -595,16 +595,15 @@ class Test_subclassing_Request(unittest.TestCase): class TestRequestLocalCache(unittest.TestCase): - def _makeOne(self): + def _makeOne(self, *args, **kwargs): from pyramid.request import RequestLocalCache - return RequestLocalCache() + return RequestLocalCache(*args, **kwargs) def test_it_works_with_functions(self): - cache = self._makeOne() a = [0] - @cache + @self._makeOne() def foo(request): a[0] += 1 return a[0] @@ -618,31 +617,10 @@ class TestRequestLocalCache(unittest.TestCase): self.assertEqual(len(req1.finished_callbacks), 1) self.assertEqual(len(req2.finished_callbacks), 1) - def test_it_works_with_methods(self): - cache = self._makeOne() - a = [0] - - class DummyPolicy: - @cache - def foo(self, request): - a[0] += 1 - return a[0] - - policy = DummyPolicy() - req1 = DummyRequest() - req2 = DummyRequest() - self.assertEqual(policy.foo(req1), 1) - self.assertEqual(policy.foo(req2), 2) - self.assertEqual(policy.foo(req1), 1) - self.assertEqual(policy.foo(req2), 2) - self.assertEqual(len(req1.finished_callbacks), 1) - self.assertEqual(len(req2.finished_callbacks), 1) - def test_clear_works(self): - cache = self._makeOne() a = [0] - @cache + @self._makeOne() def foo(request): a[0] += 1 return a[0] @@ -650,15 +628,14 @@ class TestRequestLocalCache(unittest.TestCase): req = DummyRequest() self.assertEqual(foo(req), 1) self.assertEqual(len(req.finished_callbacks), 1) - cache.clear(req) + foo.cache.clear(req) self.assertEqual(foo(req), 2) - self.assertEqual(len(req.finished_callbacks), 2) + self.assertEqual(len(req.finished_callbacks), 1) def test_set_overrides_current_value(self): - cache = self._makeOne() a = [0] - @cache + @self._makeOne() def foo(request): a[0] += 1 return a[0] @@ -666,10 +643,10 @@ class TestRequestLocalCache(unittest.TestCase): req = DummyRequest() self.assertEqual(foo(req), 1) self.assertEqual(len(req.finished_callbacks), 1) - cache.set(req, 8) + foo.cache.set(req, 8) self.assertEqual(foo(req), 8) self.assertEqual(len(req.finished_callbacks), 1) - self.assertEqual(cache.get(req), 8) + self.assertEqual(foo.cache.get(req), 8) def test_get_works(self): cache = self._makeOne() @@ -678,6 +655,47 @@ class TestRequestLocalCache(unittest.TestCase): cache.set(req, 2) self.assertIs(cache.get(req), 2) + def test_creator_in_constructor(self): + + def foo(request): + return 8 + + cache = self._makeOne(foo) + req = DummyRequest() + result = cache.get_or_create(req) + self.assertEqual(result, 8) + + def test_decorator_overrides_creator(self): + + def foo(request): # pragma: no cover + raise AssertionError + + cache = self._makeOne(foo) + + @cache + def bar(request): + return 8 + + req = DummyRequest() + result = cache.get_or_create(req) + self.assertEqual(result, 8) + + def test_get_or_create_overrides_creator(self): + cache = self._makeOne() + + @cache + def foo(request): # pragma: no cover + raise AssertionError + + req = DummyRequest() + result = cache.get_or_create(req, lambda r: 8) + self.assertEqual(result, 8) + + def test_get_or_create_with_no_creator(self): + cache = self._makeOne() + req = DummyRequest() + self.assertRaises(ValueError, cache.get_or_create, req) + class Dummy(object): pass -- cgit v1.2.3 From 9eb2c71366bff05745c49e84aac64ac8c819d6b8 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 9 Jan 2020 00:58:50 -0600 Subject: fix lint --- tests/test_request.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'tests') diff --git a/tests/test_request.py b/tests/test_request.py index a36bd238c..3c5535b0e 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -656,7 +656,6 @@ class TestRequestLocalCache(unittest.TestCase): self.assertIs(cache.get(req), 2) def test_creator_in_constructor(self): - def foo(request): return 8 @@ -666,7 +665,6 @@ class TestRequestLocalCache(unittest.TestCase): self.assertEqual(result, 8) def test_decorator_overrides_creator(self): - def foo(request): # pragma: no cover raise AssertionError -- cgit v1.2.3