Make clear how dispatch should be initiated, DispatchState.resolve starts everything and Dispatcher is never called directly

This commit is contained in:
Alessandro Molina
2016-02-09 23:00:14 +01:00
parent c866961236
commit 8b6919bd49
6 changed files with 110 additions and 119 deletions

View File

@@ -34,7 +34,7 @@ class DispatchState(object):
can be None to perform no escaping or True to use default escaping function.
"""
def __init__(self, request, dispatcher=None, params=None, path_info=None,
def __init__(self, request, dispatcher, params=None, path_info=None,
ignore_parameters=None, strip_extension=True, path_translator=None):
self._request = request

View File

@@ -78,9 +78,6 @@ class ObjectDispatcher(Dispatcher):
"""
return ismethod(getattr(controller, name, False))
def __call__(self, state, remainder=None):
return self._dispatch(state, remainder)
def _perform_security_check(self, controller):
#xxx do this better
obj = getattr(controller, 'im_self', controller)
@@ -144,12 +141,9 @@ class ObjectDispatcher(Dispatcher):
This method defines how the object dispatch mechanism works, including
checking for security along the way.
"""
if state.root_dispatcher is None:
state._root_dispatcher = self
state.add_controller('/', self)
if remainder is None:
remainder = state.path
current_controller = state.controller
#skip any empty urls

View File

@@ -202,9 +202,6 @@ class RestDispatcher(ObjectDispatcher):
This method defines how the object dispatch mechanism works, including
checking for security along the way.
"""
if state.root_dispatcher is None:
state._root_dispatcher = self
state.add_controller('/', self)
if remainder is None:
remainder = state.path

View File

@@ -69,12 +69,12 @@ class TestDispatchState:
def test_init_with_extension(self):
r = MockRequest()
r.path_info = 'something.json'
state = DispatchState(r)
state = DispatchState(r, dispatcher=None)
assert state.extension == 'json'
def test_init_with_string_path(self):
r = MockRequest()
r.path_info = 'something.json'
state = DispatchState(r, path_info='s1/s2')
state = DispatchState(r, dispatcher=None, path_info='s1/s2')
assert state.path == ['s1', 's2']

View File

@@ -118,56 +118,56 @@ class TestDispatcher:
def test_call(self):
req = MockRequest('/')
state = DispatchState(req)
state = self.dispatcher(state, [])
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'index', state.method
def test_dispatch_index(self):
req = MockRequest('/')
state = DispatchState(req)
state = DispatchState(req, self.dispatcher)
state = self.dispatcher._dispatch(state, [])
assert state.method.__name__ == 'index', state.method
def test_dispatch_default(self):
req = MockRequest('/', params={'a':1})
state = DispatchState(req, self.dispatcher)
state = self.dispatcher._dispatch(state)
state = state.resolve()
assert state.method.__name__ == '_default', state.method
def test_dispatch_default_with_unicode(self):
req = MockRequest('/', params={u('å'):u('ß')})
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == '_default', state.method
def test_controller_method_dispatch_no_args(self):
req = MockRequest('/no_args')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'no_args', state.method
def test_controller_method_with_unicode_args(self):
req = MockRequest(u('/with_args/å/ß'))
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'with_args', state.method
def test_controller_method_with_empty_args(self):
req = MockRequest('/with_args//a/b')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == '_default', state.method
def test_controller_method_with_args(self):
req = MockRequest('/with_args/a/b')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'with_args', state.method
def test_controller_method_with_args_missing_args_default(self):
req = MockRequest('/with_args/a')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == '_default', state.method
@raises(HTTPNotFound)
@@ -209,27 +209,27 @@ class TestDispatcher:
def test_sub_dispatcher(self):
req = MockRequest('/sub')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'index', state.method
assert state.controller.__class__.__name__ == 'MockSubDispatcher', state.controller
def test_sub_dispatcher_bad_remainder_call_parent_default(self):
req = MockRequest('/sub/a')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == '_default', state.method
def test_sub_dispatcher_bad_params_call_parent_default(self):
req = MockRequest('/sub', params={'a':1})
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == '_default', state.method
def test_sub_dispatcher_override_dispatch(self):
req = MockRequest('/override_dispatch', params={'a':1})
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'wacky', state.method
def test_lookup_dispatch(self):

View File

@@ -117,70 +117,70 @@ class TestDispatcher:
def test_get_all(self):
req = MockRequest('/')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'get_all'
def test_get_one(self):
req = MockRequest('/asdf')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'get_one'
assert state.params == {}, state.params
assert state.remainder == ['asdf'], state.remainder
def test_post(self):
req = MockRequest('/', method='post')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'post'
def test_post_delete(self):
req = MockRequest('/', method='delete')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'post_delete'
def test_post_delete_hacky(self):
req = MockRequest('/', params={'_method':'delete'}, method='post')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'post_delete'
def test_get_delete(self):
req = MockRequest('/delete', method='get')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'get_delete'
@raises(HTTPMethodNotAllowed)
def test_delete_hack_bad_get(self):
req = MockRequest('/', params={'_method':'delete'}, method='get')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
@raises(HTTPMethodNotAllowed)
def test_put_hack_bad_get(self):
req = MockRequest('/', params={'_method':'put'}, method='get')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
def test_put(self):
req = MockRequest('/', params={'_method':'put'}, method='post')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'put', state.method
def test_put(self):
req = MockRequest('/', method='put')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'put', state.method
def test_other_method(self):
req = MockRequest('/other')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'other', state.method
class TestSimpleDispatcher:
@@ -190,26 +190,26 @@ class TestSimpleDispatcher:
def test_get(self):
req = MockRequest('/')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'get'
def test_post(self):
req = MockRequest('/', method='post')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'post'
def test_delete(self):
req = MockRequest('/', method='delete')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'delete'
def test_delete_hacky(self):
req = MockRequest('/', params={'_method':'delete'}, method='post')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'delete'
class TestEmbeddedRestDispatcher:
@@ -222,8 +222,8 @@ class TestEmbeddedRestDispatcher:
def test_delete_hacky(self):
req = MockRequest('/asdf/sub', params={'_method':'delete'}, method='post')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'post_delete', state.method
assert state.controller.__class__.__name__ == 'MockDispatcher', state.controller
assert state.params == {}, state.params
@@ -238,8 +238,8 @@ class TestMinimalRestDispatcher:
def test_get_all_fallback_on_get_one(self):
req = MockRequest('/')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'get_one'
class TestDispatcherWithArgs:
@@ -252,59 +252,59 @@ class TestDispatcherWithArgs:
def test_post(self):
req = MockRequest('/asdf', method='post')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'post'
def test_put(self):
req = MockRequest('/sub/asdf', method='put')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'put', state.method
def test_delete(self):
req = MockRequest('/sub/asdf', method='delete')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'delete', state.method
def test_other(self):
req = MockRequest('/other', method='post')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'other'
def test_other_with_get_method(self):
req = MockRequest('/other/something', params={'_method':'get'}, method='get')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'other', state.method
@raises(HTTPNotFound)
def test_post_bad(self):
req = MockRequest('/aaa/aaa', method='post')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'pos', state.method
@raises(HTTPMethodNotAllowed)
def test_other_delete_bad(self):
req = MockRequest('/other/asdf', method='delete')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'other'
@raises(HTTPNotFound)
def test_other_delete_not_found(self):
req = MockRequest('/not_found', method='delete')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'other'
def test_sub_get_one(self):
req = MockRequest('/sub/mid', method='get')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'get_one'
@@ -318,8 +318,8 @@ class TestDispatcherWithVarArgs:
def test_delete(self):
req = MockRequest('/asdf1/asdf2/asdf3/asdf4/sub')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'get_all', state.method
class MockCustomMethodDispatcher(RestDispatcher):
@@ -342,33 +342,33 @@ class TestCustomMethodDispatcher:
def test_post(self):
req = MockRequest('/', method='custom')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'post_custom', state.method
def test_post_hacky(self):
req = MockRequest('/', params={'_method':'custom'}, method='post')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'post_custom', state.method
def test_get_hacky(self):
req = MockRequest('/', params={'_method':'custom'}, method='get')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'get_custom', state.method
def test_get_url(self):
req = MockRequest('/custom')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'get_custom', state.method
@raises(HTTPNotFound)
def test_get_fail(self):
req = MockRequest('/not_found', params={'_method':'custom'}, method='get')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'get_custom', state.method
class SubCustomMethodDispatcher(MockDispatcher):
@@ -385,8 +385,8 @@ class TestSubCustomMethodDispatcher:
def test_get_url(self):
req = MockRequest('/sub', params={'_method':'custom'}, method='get')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.method.__name__ == 'get_custom', state.method
class SubNoGet(RestDispatcher):
@@ -406,8 +406,8 @@ class TestSubNoGetDispatcher:
@raises(HTTPNotFound)
def test_get_not_found(self):
req = MockRequest('/sub', method='get')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
class TestEmptyDispatcher:
def setup(self):
@@ -419,8 +419,8 @@ class TestEmptyDispatcher:
@raises(HTTPNotFound)
def test_get_not_found(self):
req = MockRequest('/sub', method='get')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
class TestRestWithSecurity:
def setup(self):
@@ -429,14 +429,14 @@ class TestRestWithSecurity:
@raises(MockError)
def test_check_security_with_lookup(self):
req = MockRequest('/direct/a')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
@raises(MockError)
def test_check_security_with_nested_lookup(self):
req = MockRequest('/nested/withsec/a')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
class TestRestWithLookup:
class RootController(ObjectDispatcher):
@@ -458,22 +458,22 @@ class TestRestWithLookup:
def test_rest_with_lookup(self):
req = MockRequest('/rest/somethingelse/method')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.controller.__class__.__name__ == 'sub', state.controller
assert state.method.__name__ == 'method', state.method
def test_rest_lookup_doesnt_mess_with_get(self):
req = MockRequest('/rest/25')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.controller.__class__.__name__ == 'rest', state.controller
assert state.method.__name__ == 'get', state.method
def test_rest_lookup_doesnt_mess_with_subcontroller(self):
req = MockRequest('/rest/sub/method')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.controller.__class__.__name__ == 'sub', state.controller
assert state.method.__name__ == 'method', state.method
@@ -496,8 +496,8 @@ class TestRestCheckSecurity:
def test_rest_security_check_only_once(self):
req = MockRequest('/rest/25')
state = DispatchState(req)
state = self.dispatcher._dispatch(state)
state = DispatchState(req, self.dispatcher)
state = state.resolve()
assert state.controller.__class__.__name__ == 'rest', state.controller
assert state.method.__name__ == 'get', state.method
assert len(self.security_tracing) == 1, self.security_tracing