def test_registering_helper_works(self): from types import GeneratorType class CustomResponse(CometResponse): pass def process_request(inst): self.assertTrue(inst.is_sijax_request) response = inst.process_request() self.assertTrue(isinstance(response, GeneratorType)) # something needs to "move the generator forward" if we want # to proceed in the call chain for string in response: pass call_history = [] def invalid_call(obj_response, callback): self.fail("Invalid call handler triggered!") inst = Sijax() cls = inst.__class__ inst.register_event(cls.EVENT_INVALID_CALL, invalid_call) inst.set_data({cls.PARAM_REQUEST: "my_func", cls.PARAM_ARGS: '[]'}) def my_callback(obj_response): self.assertTrue(isinstance(obj_response, CometResponse)) self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) call_history.append("my_callback") call_history.append(type(obj_response).__name__) register_comet_callback(inst, "my_func", my_callback) process_request(inst) # let's see if we can override the default args and the response # class during callback registration params_custom = {cls.PARAM_RESPONSE_CLASS: CustomResponse} register_comet_callback(inst, "my_func", my_callback, **params_custom) process_request(inst) # Try mass registering now class SijaxHandler(object): @staticmethod def callback_one(obj_response, arg1): self.assertTrue(isinstance(obj_response, CustomResponse)) call_history.append("callback_one") call_history.append(type(obj_response).__name__) call_history.append(arg1) register_comet_object(inst, SijaxHandler, **params_custom) inst.set_data({cls.PARAM_REQUEST: "callback_one", cls.PARAM_ARGS: '[45]'}) process_request(inst) call_history_expected = [ "my_callback", "CometResponse", "my_callback", "CustomResponse", "callback_one", "CustomResponse", 45 ] self.assertEqual(call_history_expected, call_history)
def test_executing_regular_callbacks_works(self): from types import StringType calls_history = [] call_args_history = [] def event_before(obj_response): self.assertTrue(isinstance(obj_response, BaseResponse)) calls_history.append("before") obj_response.script("javascript here..") def event_after(obj_response): self.assertTrue(isinstance(obj_response, BaseResponse)) calls_history.append("after") obj_response.css("#element", "backgroundColor", "red") def callback_main(obj_response, arg1, arg2): self.assertTrue(isinstance(obj_response, BaseResponse)) calls_history.append("main") call_args_history.append(arg1) call_args_history.append(arg2) obj_response.alert("alert from main") inst = Sijax() cls = inst.__class__ inst.register_event(cls.EVENT_BEFORE_PROCESSING, event_before) inst.register_event(cls.EVENT_AFTER_PROCESSING, event_after) call_args = ["arg1", 15] response = inst.execute_callback(call_args, callback=callback_main) self.assertEqual(["before", "main", "after"], calls_history) self.assertEqual(call_args, call_args_history) # the response should be a string for regular functions # streaming functions return generators instead.. self.assertTrue(isinstance(response, StringType)) from sijax.helper import json try: commands = json.loads(response) except: self.fail("Invalid JSON response!") else: self.assertTrue(isinstance(commands, list)) commands_history = [] for cmd_params in commands: self.assertTrue(isinstance(cmd_params, dict)) self.assertTrue("type" in cmd_params, "Unknown command type!") commands_history.append(cmd_params["type"]) self.assertEqual(["script", "alert", "css"], commands_history)
def test_executing_regular_callbacks_works(self): calls_history = [] call_args_history = [] def event_before(obj_response): self.assertTrue(isinstance(obj_response, BaseResponse)) calls_history.append("before") obj_response.script("javascript here..") def event_after(obj_response): self.assertTrue(isinstance(obj_response, BaseResponse)) calls_history.append("after") obj_response.css("#element", "backgroundColor", "red") def callback_main(obj_response, arg1, arg2): self.assertTrue(isinstance(obj_response, BaseResponse)) calls_history.append("main") call_args_history.append(arg1) call_args_history.append(arg2) obj_response.alert("alert from main") inst = Sijax() cls = inst.__class__ inst.register_event(cls.EVENT_BEFORE_PROCESSING, event_before) inst.register_event(cls.EVENT_AFTER_PROCESSING, event_after) call_args = ["arg1", 15] response = inst.execute_callback(call_args, callback=callback_main) self.assertEqual(["before", "main", "after"], calls_history) self.assertEqual(call_args, call_args_history) # the response should be a string for regular functions # streaming functions return generators instead.. self.assertTrue(isinstance(response, string_types)) from sijax.helper import json try: commands = json.loads(response) except: self.fail("Invalid JSON response!") else: self.assertTrue(isinstance(commands, list)) commands_history = [] for cmd_params in commands: self.assertTrue(isinstance(cmd_params, dict)) self.assertTrue("type" in cmd_params, "Unknown command type!") commands_history.append(cmd_params["type"]) self.assertEqual(["script", "alert", "css"], commands_history)
def test_registering_custom_events_works(self): inst = Sijax() event_name = "my_event" event_callback = lambda obj_response: obj_response.alert("Event") self.assertFalse(inst.has_event(event_name), "Custom event registered") inst.register_event(event_name, event_callback) self.assertTrue(inst.has_event(event_name), "Failed to register event")
def test_registering_helper_works(self): from types import GeneratorType from sijax.plugin.upload import func_name_by_form_id class CustomResponse(UploadResponse): pass def process_request(inst): self.assertTrue(inst.is_sijax_request) response = inst.process_request() self.assertTrue(isinstance(response, GeneratorType)) # something needs to "move the generator forward" if we want # to proceed in the call chain for string in response: pass call_history = [] def invalid_call(obj_response, callback): self.fail("Invalid call handler triggered!") inst = Sijax() cls = inst.__class__ inst.register_event(cls.EVENT_INVALID_CALL, invalid_call) def my_callback(obj_response, files, form_values): self.assertTrue(isinstance(obj_response, UploadResponse)) self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) call_history.append("my_callback") call_history.append(obj_response.form_id) call_history.append(type(obj_response).__name__) call_history.append(files["file"]) # reaching into the file object # ensure that from all the post data, only the valid (non-system) # stuff made it through self.assertEqual({ "form_key": "value", "form_key2": 15 }, form_values) def my_callback2(obj_response, custom_arg1, custom_arg2, form_values): self.assertTrue(isinstance(obj_response, UploadResponse)) self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) call_history.append("custom_callback") call_history.append(obj_response.form_id) call_history.append(type(obj_response).__name__) call_history.append(custom_arg1) call_history.append(custom_arg2) # ensure that from all the post data, only the valid (non-system) # stuff made it through self.assertEqual({ "form_key": "value", "form_key2": 15 }, form_values) form_id = "my_form" # the developer usually doesn't need to know the public system assigned # name of the callback function.. we'll need it though to fake the request public_callback_name = func_name_by_form_id(form_id) request_args_json = '["%s"]' % form_id post = {} post[cls.PARAM_REQUEST] = public_callback_name post[cls.PARAM_ARGS] = request_args_json post["form_key"] = "value" post["form_key2"] = 15 inst.set_data(post) files = {"file": "object", "passed": "here"} register_upload_callback(inst, form_id, my_callback, args_extra=(files, )) process_request(inst) # let's see if we can override the default args and the response # class during callback registration params_custom = {cls.PARAM_RESPONSE_CLASS: CustomResponse} register_upload_callback(inst, form_id, my_callback2, args_extra=("custom1", "custom2"), **params_custom) process_request(inst) call_history_expected = [ "my_callback", form_id, "UploadResponse", "object", "custom_callback", form_id, "CustomResponse", "custom1", "custom2" ] self.assertEqual(call_history_expected, call_history)
def test_registering_helper_works(self): from types import GeneratorType class CustomResponse(CometResponse): pass def process_request(inst): self.assertTrue(inst.is_sijax_request) response = inst.process_request() self.assertTrue(isinstance(response, GeneratorType)) # something needs to "move the generator forward" if we want # to proceed in the call chain for string in response: pass call_history = [] def invalid_call(obj_response, callback): self.fail("Invalid call handler triggered!") inst = Sijax() cls = inst.__class__ inst.register_event(cls.EVENT_INVALID_CALL, invalid_call) inst.set_data({cls.PARAM_REQUEST: "my_func", cls.PARAM_ARGS: '[]'}) def my_callback(obj_response): self.assertTrue(isinstance(obj_response, CometResponse)) self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) call_history.append("my_callback") call_history.append(type(obj_response).__name__) register_comet_callback(inst, "my_func", my_callback) process_request(inst) # let's see if we can override the default args and the response # class during callback registration params_custom = {cls.PARAM_RESPONSE_CLASS: CustomResponse} register_comet_callback(inst, "my_func", my_callback, **params_custom) process_request(inst) # Try mass registering now class SijaxHandler(object): @staticmethod def callback_one(obj_response, arg1): self.assertTrue(isinstance(obj_response, CustomResponse)) call_history.append("callback_one") call_history.append(type(obj_response).__name__) call_history.append(arg1) register_comet_object(inst, SijaxHandler, **params_custom) inst.set_data({ cls.PARAM_REQUEST: "callback_one", cls.PARAM_ARGS: '[45]' }) process_request(inst) call_history_expected = [ "my_callback", "CometResponse", "my_callback", "CustomResponse", "callback_one", "CustomResponse", 45 ] self.assertEqual(call_history_expected, call_history)
def test_invalid_call_event_works(self): from types import GeneratorType, FunctionType call_history = [] def my_callback(obj_response, arg1, arg2): self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) call_history.append("call ok") def my_callback_raising_TypeError(obj_response): self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) raise TypeError('this should be re-raised by Sijax') def my_callback_raising_TypeError2(obj_response): self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) def inner(): raise TypeError('this should be re-raised by Sijax') inner() def invalid_call(obj_response, failed_callback): self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) self.assertTrue(isinstance(failed_callback, FunctionType)) call_history.append("invalid %s" % failed_callback.__name__) def exhaust_generator(gen): self.assertTrue(isinstance(gen, GeneratorType)) try: while True: next(gen) except StopIteration: pass inst = Sijax() cls = inst.__class__ options = {cls.PARAM_RESPONSE_CLASS: StreamingIframeResponse} inst.register_event(cls.EVENT_INVALID_CALL, invalid_call) inst.register_callback("my_func", my_callback, **options) inst.set_data({ cls.PARAM_REQUEST: "my_func", cls.PARAM_ARGS: '["arg1", 12]' }) self.assertTrue(inst.is_sijax_request) self.assertEqual("my_func", inst.requested_function) self.assertEqual(["arg1", 12], inst.request_args) response = inst.process_request() exhaust_generator(response) # we should have succeeded until now.. # let's try to make the call invalid and observe the failure inst.set_data({cls.PARAM_REQUEST: "my_func", cls.PARAM_ARGS: '[]'}) self.assertEqual("my_func", inst.requested_function) self.assertEqual([], inst.request_args) response = inst.process_request() exhaust_generator(response) # let's ensure that raising a TypeError from within a handler, # is not mistaken for an invalid call (EVENT_INVALID_CALL), # and re-raises the exception inst.register_callback("my_func", my_callback_raising_TypeError, **options) try: response = inst.process_request() exhaust_generator(response) except TypeError: call_history.append('TypeError') inst.register_callback("my_func", my_callback_raising_TypeError2, **options) try: response = inst.process_request() exhaust_generator(response) except TypeError: call_history.append('TypeError2') expected = [ 'call ok', 'invalid my_callback', 'TypeError', 'TypeError2' ] self.assertEqual(expected, call_history)
def test_streaming_functions_return_generators(self): # Every function registered as streaming should return a generator # when it's executed, even if the actual function decides to act # as a normal function, and not generate anything # This is very important, because it allows mixing streaming with # regular functions when the StreamingIframeResponse is used from types import GeneratorType call_history = [] def callback_before(obj_response): self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) call_history.append("before") obj_response.html("#div", "before html") def callback_after(obj_response): self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) call_history.append("after") # intentionally not pushing new commands # which means it should not be yielding at implicitly def callback_yielding(obj_response): self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) call_history.append("yielding") obj_response.alert("test") yield obj_response obj_response.css("#div", "color", "red") def callback_normal(obj_response): self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) call_history.append("normal") obj_response.script(".. javascript here..") def assert_generator_entries_length(generator, length): items = [] try: while True: items.append(next(generator)) except StopIteration: pass self.assertEqual(length, len(items)) inst = Sijax() cls = inst.__class__ inst.register_event(cls.EVENT_BEFORE_PROCESSING, callback_before) inst.register_event(cls.EVENT_AFTER_PROCESSING, callback_after) options = {cls.PARAM_RESPONSE_CLASS: StreamingIframeResponse} response = inst.execute_callback([], callback=callback_yielding, **options) self.assertTrue(isinstance(response, GeneratorType)) # We should have the following yields: # 1. Implicit yield after callback_before # 2. Explicit yield from callback_yielding # 3. Implicit yield when calback_yielding ends # Note that callback_after should not yield implicitly, # because it has not pushed new commands, so it makes no sense! assert_generator_entries_length(response, 3) def callback_before_new(obj_response): self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) call_history.append("before_new") # this doesn't really push data, as we haven't added commands yet # flushing would be wasteful for i in range(10): yield obj_response for i in range(2): obj_response.alert("hey") yield obj_response inst.register_event(cls.EVENT_BEFORE_PROCESSING, callback_before_new) inst.register_event(cls.EVENT_AFTER_PROCESSING, lambda r: r.alert("this yields")) response = inst.execute_callback([], callback=callback_normal, **options) self.assertTrue(isinstance(response, GeneratorType)) # We're expecting the following yields: # 1. Explicit yield from callback_before_new # 2. Explicit yield from callback_before_new # 3. Implicit yield after callback_normal # 4. Implicit yield after EVENT_AFTER_PROCESSING callback assert_generator_entries_length(response, 4) call_history_expected = [ "before", "yielding", "after", "before_new", "normal" ] self.assertEqual(call_history_expected, call_history)
def test_process_request_calls_invalid_call_event_for_invalid_calls(self): from types import FunctionType from sijax.helper import json # An invalid call is a call to a function that appears valid. # The function is registered (known), but calling fails, because # the function expects another number of arguments call_history = [] def my_callback(obj_response, arg1, arg2): call_history.append("call ok") def my_callback_with_defaults(obj_response, arg1=138, arg2=15): call_history.append("defaults ok") def my_callback_raising_TypeError(obj_response): raise TypeError('this should be re-raised by Sijax') def my_callback_raising_TypeError2(obj_response): def inner(): raise TypeError('this should be re-raised by Sijax') inner() def invalid_call(obj_response, failed_callback): self.assertTrue(isinstance(failed_callback, FunctionType)) call_history.append("invalid %s" % failed_callback.__name__) inst = Sijax() cls = inst.__class__ inst.register_callback("my_func", my_callback) # Make a valid call that would succeed inst.set_data({ cls.PARAM_REQUEST: "my_func", cls.PARAM_ARGS: '["arg1", 12]' }) self.assertTrue(inst.is_sijax_request) self.assertEqual("my_func", inst.requested_function) self.assertEqual(["arg1", 12], inst.request_args) response = inst.process_request() self.assertTrue(isinstance(response, string_types)) # Make a call with a wrong number of arguments, and a default # event handler for invalid calls inst.set_data({ cls.PARAM_REQUEST: "my_func", cls.PARAM_ARGS: '["arg1"]' }) self.assertTrue(inst.is_sijax_request) self.assertEqual("my_func", inst.requested_function) self.assertEqual(["arg1"], inst.request_args) response = inst.process_request() self.assertTrue(isinstance(response, string_types)) try: commands = json.loads(response) except: self.fail("Invalid JSON generated!") else: self.assertTrue(isinstance(commands, list)) # we expect the default "Action performed in a wrong way" alert self.assertEqual(1, len(commands)) command_data = commands.pop(0) self.assertTrue("type" in command_data) self.assertEqual("alert", command_data["type"]) # Make an invalid call with a custom event handler function inst.register_event(cls.EVENT_INVALID_CALL, invalid_call) inst.set_data({cls.PARAM_REQUEST: "my_func", cls.PARAM_ARGS: '[]'}) self.assertEqual("my_func", inst.requested_function) self.assertEqual([], inst.request_args) response = inst.process_request() self.assertTrue(isinstance(response, string_types)) # let's see if calling works with default arguments inst.register_callback("my_func", my_callback_with_defaults) response = inst.process_request() self.assertTrue(isinstance(response, string_types)) # let's ensure that raising a TypeError from within a handler, # is not mistaken for an invalid call (EVENT_INVALID_CALL), # and re-raises the exception inst.register_callback("my_func", my_callback_raising_TypeError) try: inst.process_request() except TypeError: call_history.append('TypeError') inst.register_callback("my_func", my_callback_raising_TypeError2) try: inst.process_request() except TypeError: call_history.append('TypeError2') expected = [ 'call ok', 'invalid my_callback', 'defaults ok', 'TypeError', 'TypeError2' ] self.assertEqual(expected, call_history)
def test_registering_helper_works(self): from types import GeneratorType from sijax.plugin.upload import func_name_by_form_id class CustomResponse(UploadResponse): pass def process_request(inst): self.assertTrue(inst.is_sijax_request) response = inst.process_request() self.assertTrue(isinstance(response, GeneratorType)) # something needs to "move the generator forward" if we want # to proceed in the call chain for string in response: pass call_history = [] def invalid_call(obj_response, callback): self.fail("Invalid call handler triggered!") inst = Sijax() cls = inst.__class__ inst.register_event(cls.EVENT_INVALID_CALL, invalid_call) def my_callback(obj_response, files, form_values): self.assertTrue(isinstance(obj_response, UploadResponse)) self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) call_history.append("my_callback") call_history.append(obj_response.form_id) call_history.append(type(obj_response).__name__) call_history.append(files["file"]) # reaching into the file object # ensure that from all the post data, only the valid (non-system) # stuff made it through self.assertEqual({"form_key": "value", "form_key2": 15}, form_values) def my_callback2(obj_response, custom_arg1, custom_arg2, form_values): self.assertTrue(isinstance(obj_response, UploadResponse)) self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) call_history.append("custom_callback") call_history.append(obj_response.form_id) call_history.append(type(obj_response).__name__) call_history.append(custom_arg1) call_history.append(custom_arg2) # ensure that from all the post data, only the valid (non-system) # stuff made it through self.assertEqual({"form_key": "value", "form_key2": 15}, form_values) form_id = "my_form" # the developer usually doesn't need to know the public system assigned # name of the callback function.. we'll need it though to fake the request public_callback_name = func_name_by_form_id(form_id) request_args_json = '["%s"]' % form_id post = {} post[cls.PARAM_REQUEST] = public_callback_name post[cls.PARAM_ARGS] = request_args_json post["form_key"] = "value" post["form_key2"] = 15 inst.set_data(post) files = {"file": "object", "passed": "here"} register_upload_callback(inst, form_id, my_callback, args_extra=(files,)) process_request(inst) # let's see if we can override the default args and the response # class during callback registration params_custom = {cls.PARAM_RESPONSE_CLASS: CustomResponse} register_upload_callback(inst, form_id, my_callback2, args_extra=("custom1", "custom2"), **params_custom) process_request(inst) call_history_expected = [ "my_callback", form_id, "UploadResponse", "object", "custom_callback", form_id, "CustomResponse", "custom1", "custom2" ] self.assertEqual(call_history_expected, call_history)
def test_invalid_call_event_works(self): from types import GeneratorType, FunctionType call_history = [] def my_callback(obj_response, arg1, arg2): self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) call_history.append("call ok") def my_callback_raising_TypeError(obj_response): self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) raise TypeError('this should be re-raised by Sijax') def my_callback_raising_TypeError2(obj_response): self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) def inner(): raise TypeError('this should be re-raised by Sijax') inner() def invalid_call(obj_response, failed_callback): self.assertTrue(isinstance(obj_response, StreamingIframeResponse)) self.assertTrue(isinstance(failed_callback, FunctionType)) call_history.append("invalid %s" % failed_callback.__name__) def exhaust_generator(gen): self.assertTrue(isinstance(gen, GeneratorType)) try: while True: next(gen) except StopIteration: pass inst = Sijax() cls = inst.__class__ options = {cls.PARAM_RESPONSE_CLASS: StreamingIframeResponse} inst.register_event(cls.EVENT_INVALID_CALL, invalid_call) inst.register_callback("my_func", my_callback, **options) inst.set_data({cls.PARAM_REQUEST: "my_func", cls.PARAM_ARGS: '["arg1", 12]'}) self.assertTrue(inst.is_sijax_request) self.assertEqual("my_func", inst.requested_function) self.assertEqual(["arg1", 12], inst.request_args) response = inst.process_request() exhaust_generator(response) # we should have succeeded until now.. # let's try to make the call invalid and observe the failure inst.set_data({cls.PARAM_REQUEST: "my_func", cls.PARAM_ARGS: '[]'}) self.assertEqual("my_func", inst.requested_function) self.assertEqual([], inst.request_args) response = inst.process_request() exhaust_generator(response) # let's ensure that raising a TypeError from within a handler, # is not mistaken for an invalid call (EVENT_INVALID_CALL), # and re-raises the exception inst.register_callback("my_func", my_callback_raising_TypeError, **options) try: response = inst.process_request() exhaust_generator(response) except TypeError: call_history.append('TypeError') inst.register_callback("my_func", my_callback_raising_TypeError2, **options) try: response = inst.process_request() exhaust_generator(response) except TypeError: call_history.append('TypeError2') expected = ['call ok', 'invalid my_callback', 'TypeError', 'TypeError2'] self.assertEqual(expected, call_history)
def test_process_request_calls_invalid_call_event_for_invalid_calls(self): from types import FunctionType from sijax.helper import json # An invalid call is a call to a function that appears valid. # The function is registered (known), but calling fails, because # the function expects another number of arguments call_history = [] def my_callback(obj_response, arg1, arg2): call_history.append("call ok") def my_callback_with_defaults(obj_response, arg1=138, arg2=15): call_history.append("defaults ok") def my_callback_raising_TypeError(obj_response): raise TypeError('this should be re-raised by Sijax') def my_callback_raising_TypeError2(obj_response): def inner(): raise TypeError('this should be re-raised by Sijax') inner() def invalid_call(obj_response, failed_callback): self.assertTrue(isinstance(failed_callback, FunctionType)) call_history.append("invalid %s" % failed_callback.__name__) inst = Sijax() cls = inst.__class__ inst.register_callback("my_func", my_callback) # Make a valid call that would succeed inst.set_data({cls.PARAM_REQUEST: "my_func", cls.PARAM_ARGS: '["arg1", 12]'}) self.assertTrue(inst.is_sijax_request) self.assertEqual("my_func", inst.requested_function) self.assertEqual(["arg1", 12], inst.request_args) response = inst.process_request() self.assertTrue(isinstance(response, string_types)) # Make a call with a wrong number of arguments, and a default # event handler for invalid calls inst.set_data({cls.PARAM_REQUEST: "my_func", cls.PARAM_ARGS: '["arg1"]'}) self.assertTrue(inst.is_sijax_request) self.assertEqual("my_func", inst.requested_function) self.assertEqual(["arg1"], inst.request_args) response = inst.process_request() self.assertTrue(isinstance(response, string_types)) try: commands = json.loads(response) except: self.fail("Invalid JSON generated!") else: self.assertTrue(isinstance(commands, list)) # we expect the default "Action performed in a wrong way" alert self.assertEqual(1, len(commands)) command_data = commands.pop(0) self.assertTrue("type" in command_data) self.assertEqual("alert", command_data["type"]) # Make an invalid call with a custom event handler function inst.register_event(cls.EVENT_INVALID_CALL, invalid_call) inst.set_data({cls.PARAM_REQUEST: "my_func", cls.PARAM_ARGS: '[]'}) self.assertEqual("my_func", inst.requested_function) self.assertEqual([], inst.request_args) response = inst.process_request() self.assertTrue(isinstance(response, string_types)) # let's see if calling works with default arguments inst.register_callback("my_func", my_callback_with_defaults) response = inst.process_request() self.assertTrue(isinstance(response, string_types)) # let's ensure that raising a TypeError from within a handler, # is not mistaken for an invalid call (EVENT_INVALID_CALL), # and re-raises the exception inst.register_callback("my_func", my_callback_raising_TypeError) try: inst.process_request() except TypeError: call_history.append('TypeError') inst.register_callback("my_func", my_callback_raising_TypeError2) try: inst.process_request() except TypeError: call_history.append('TypeError2') expected = ['call ok', 'invalid my_callback', 'defaults ok', 'TypeError', 'TypeError2'] self.assertEqual(expected, call_history)