summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-10-14 21:14:34 +0000
committerGerrit Code Review <review@openstack.org>2014-10-14 21:14:34 +0000
commit8b30a0ec72860aa78b3b9c2246aa1c6e7a1b9cfe (patch)
tree3a9cfc5ba41f9df205d4724c470ec6848274fbd6
parentfd9a0dc1f67348014a05f75b13da05d5147c1233 (diff)
parent2d5f5e482d77bfcaab2b4677241e85e9f1ac9b39 (diff)
downloadpecan-8b30a0ec72860aa78b3b9c2246aa1c6e7a1b9cfe.tar.gz
Merge "Resolve a bug that mixes up argument order for generic functions."
-rw-r--r--pecan/core.py12
-rw-r--r--pecan/tests/test_no_thread_locals.py87
2 files changed, 96 insertions, 3 deletions
diff --git a/pecan/core.py b/pecan/core.py
index 20d6403..49c7e8e 100644
--- a/pecan/core.py
+++ b/pecan/core.py
@@ -11,6 +11,10 @@ import operator
import types
import six
+if six.PY3:
+ from .compat import is_bound_method as ismethod
+else:
+ from inspect import ismethod
from webob import (Request as WebObRequest, Response as WebObResponse, exc,
acceptparse)
@@ -738,7 +742,13 @@ class ExplicitPecan(PecanBase):
args, varargs, kwargs = super(ExplicitPecan, self).get_args(
state, all_params, remainder, argspec, im_self
)
- args = [state.request, state.response] + args
+
+ if ismethod(state.controller):
+ args = [state.request, state.response] + args
+ else:
+ # generic controllers have an explicit self *first*
+ # (because they're decorated functions, not instance methods)
+ args[1:1] = [state.request, state.response]
return args, varargs, kwargs
diff --git a/pecan/tests/test_no_thread_locals.py b/pecan/tests/test_no_thread_locals.py
index a114840..0345391 100644
--- a/pecan/tests/test_no_thread_locals.py
+++ b/pecan/tests/test_no_thread_locals.py
@@ -1,4 +1,5 @@
-from json import dumps
+import time
+from json import dumps, loads
import warnings
from webtest import TestApp
@@ -7,7 +8,7 @@ from six import u as u_
import webob
import mock
-from pecan import Pecan, expose, abort
+from pecan import Pecan, expose, abort, Request, Response
from pecan.rest import RestController
from pecan.hooks import PecanHook, HookController
from pecan.tests import PecanTestCase
@@ -1355,3 +1356,85 @@ class TestHooks(PecanTestCase):
assert run_hook[3] == 'inside_sub'
assert run_hook[4] == 'after1'
assert run_hook[5] == 'after2'
+
+
+class TestGeneric(PecanTestCase):
+
+ @property
+ def root(self):
+ class RootController(object):
+
+ def __init__(self, unique):
+ self.unique = unique
+
+ @expose(generic=True, template='json')
+ def index(self, req, resp):
+ assert self.__class__.__name__ == 'RootController'
+ assert isinstance(req, Request)
+ assert isinstance(resp, Response)
+ assert self.unique == req.headers.get('X-Unique')
+ return {'hello': 'world'}
+
+ @index.when(method='POST', template='json')
+ def index_post(self, req, resp):
+ assert self.__class__.__name__ == 'RootController'
+ assert isinstance(req, Request)
+ assert isinstance(resp, Response)
+ assert self.unique == req.headers.get('X-Unique')
+ return req.json
+
+ @expose(template='json')
+ def echo(self, req, resp):
+ assert self.__class__.__name__ == 'RootController'
+ assert isinstance(req, Request)
+ assert isinstance(resp, Response)
+ assert self.unique == req.headers.get('X-Unique')
+ return req.json
+
+ @expose(template='json')
+ def extra(self, req, resp, first, second):
+ assert self.__class__.__name__ == 'RootController'
+ assert isinstance(req, Request)
+ assert isinstance(resp, Response)
+ assert self.unique == req.headers.get('X-Unique')
+ return {'first': first, 'second': second}
+
+ return RootController
+
+ def test_generics_with_im_self_default(self):
+ uniq = str(time.time())
+ with mock.patch('threading.local', side_effect=AssertionError()):
+ app = TestApp(Pecan(self.root(uniq), use_context_locals=False))
+ r = app.get('/', headers={'X-Unique': uniq})
+ assert r.status_int == 200
+ json_resp = loads(r.body.decode())
+ assert json_resp['hello'] == 'world'
+
+ def test_generics_with_im_self_with_method(self):
+ uniq = str(time.time())
+ with mock.patch('threading.local', side_effect=AssertionError()):
+ app = TestApp(Pecan(self.root(uniq), use_context_locals=False))
+ r = app.post_json('/', {'foo': 'bar'}, headers={'X-Unique': uniq})
+ assert r.status_int == 200
+ json_resp = loads(r.body.decode())
+ assert json_resp['foo'] == 'bar'
+
+ def test_generics_with_im_self_with_path(self):
+ uniq = str(time.time())
+ with mock.patch('threading.local', side_effect=AssertionError()):
+ app = TestApp(Pecan(self.root(uniq), use_context_locals=False))
+ r = app.post_json('/echo/', {'foo': 'bar'},
+ headers={'X-Unique': uniq})
+ assert r.status_int == 200
+ json_resp = loads(r.body.decode())
+ assert json_resp['foo'] == 'bar'
+
+ def test_generics_with_im_self_with_extra_args(self):
+ uniq = str(time.time())
+ with mock.patch('threading.local', side_effect=AssertionError()):
+ app = TestApp(Pecan(self.root(uniq), use_context_locals=False))
+ r = app.get('/extra/123/456', headers={'X-Unique': uniq})
+ assert r.status_int == 200
+ json_resp = loads(r.body.decode())
+ assert json_resp['first'] == '123'
+ assert json_resp['second'] == '456'