From 5ce3528077ffc4b813838624a9643b5ad90ea152 Mon Sep 17 00:00:00 2001 From: maresp Date: Thu, 16 Jul 2015 09:58:58 +0000 Subject: [PATCH] fix(Request): context_type function raises AttributeError Moving the instantiation of Request context to the end of Request.__init__, so that all Request properties are set up and accessible from within a context_type function. Fixes #567 --- falcon/request.py | 23 +++++++++++++++-------- tests/test_request_context.py | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/falcon/request.py b/falcon/request.py index fecb8d4..bfd6cd3 100644 --- a/falcon/request.py +++ b/falcon/request.py @@ -95,6 +95,13 @@ class Request(object): by creating a custom child class of ``falcon.Request``, and then passing that new class to `falcon.API()` by way of the latter's `request_type` parameter. + + Note: + When overriding ``context_type`` with a factory function, the + function is called like a method of the current Request + instance. Therefore the first argument is the Request instance + itself (self). + uri (str): The fully-qualified URI for the request. url (str): alias for `uri`. relative_uri (str): The path + query string portion of the full URI. @@ -209,14 +216,6 @@ class Request(object): self.env = env self.options = options if options else RequestOptions() - if self.context_type is None: - # Literal syntax is more efficient than using dict() - self.context = {} - else: - # pylint will detect this as not-callable because it only sees the - # declaration of None, not whatever type a subclass may have set. - self.context = self.context_type() # pylint: disable=not-callable - self._wsgierrors = env['wsgi.errors'] self.stream = env['wsgi.input'] self.method = env['REQUEST_METHOD'] @@ -284,6 +283,14 @@ class Request(object): 'application/x-www-form-urlencoded' in self.content_type): self._parse_form_urlencoded() + if self.context_type is None: + # Literal syntax is more efficient than using dict() + self.context = {} + else: + # pylint will detect this as not-callable because it only sees the + # declaration of None, not whatever type a subclass may have set. + self.context = self.context_type() # pylint: disable=not-callable + # ------------------------------------------------------------------------ # Properties # ------------------------------------------------------------------------ diff --git a/tests/test_request_context.py b/tests/test_request_context.py index 242d71d..d9013dd 100644 --- a/tests/test_request_context.py +++ b/tests/test_request_context.py @@ -31,3 +31,17 @@ class TestRequestContext(testing.TestBase): env = testing.create_environ() self.assertRaises(TypeError, MyCustomRequest, env) + + def test_custom_request_context_request_access(self): + + def create_context(req): + return {'uri': req.uri} + + # Define a Request-alike with a custom context type + class MyCustomRequest(Request): + context_type = create_context + + env = testing.create_environ() + req = MyCustomRequest(env) + self.assertIsInstance(req.context, dict) + self.assertEqual(req.context['uri'], req.uri)