class SchemaCoercionTestCase(TestCase): def setUp(self): from cocktail.schema import Schema, Integer, String self.schema = Schema(members=[ Integer("num", required=True, min=5, default=50), String("text", min=5, default="cornucopia") ]) def test_can_ignore_errors(self): from itertools import product from cocktail.schema import Coercion for obj in [{ "num": 3, "text": None }, { "num": 20, "text": "hum" }, { "num": 3, "text": "hum" }, { "num": 15, "text": "spam!" }]: original = obj.copy() result = self.schema.coerce(obj, Coercion.NONE) assert result is obj assert result == original def test_can_replace_invalid_values_with_none(self): from cocktail.schema import Coercion # num invalid, text valid obj = {"num": 3, "text": None} result = self.schema.coerce(obj, Coercion.SET_NONE) assert obj is result assert result == {"num": None, "text": None} # num valid, text invalid obj = {"num": 20, "text": "hum"} result = self.schema.coerce(obj, Coercion.SET_NONE) assert obj is result assert result == {"num": 20, "text": None} # both invalid obj = {"num": 3, "text": "hum"} result = self.schema.coerce(obj, Coercion.SET_NONE) assert obj is result assert result == {"num": None, "text": None} # both valid obj = {"num": 15, "text": "spam!"} result = self.schema.coerce(obj, Coercion.SET_NONE) assert obj is result assert result == {"num": 15, "text": "spam!"} def test_can_replace_invalid_values_with_default(self): from cocktail.schema import Coercion default_num = self.schema["num"].default default_text = self.schema["text"].default # num invalid, text valid obj = {"num": 3, "text": None} result = self.schema.coerce(obj, Coercion.SET_DEFAULT) assert obj is result assert result == {"num": default_num, "text": None} # num valid, text invalid obj = {"num": 20, "text": "hum"} result = self.schema.coerce(obj, Coercion.SET_DEFAULT) assert obj is result assert result == {"num": 20, "text": default_text} # both invalid obj = {"num": 3, "text": "hum"} result = self.schema.coerce(obj, Coercion.SET_DEFAULT) assert obj is result assert result == {"num": default_num, "text": default_text} # both valid obj = {"num": 15, "text": "spam!"} result = self.schema.coerce(obj, Coercion.SET_DEFAULT) assert obj is result assert result == {"num": 15, "text": "spam!"} def test_can_raise_errors_immediately(self): from cocktail.schema import Coercion from cocktail.schema.exceptions import (InputError, MinValueError, MinLengthError) FI = Coercion.FAIL_IMMEDIATELY # num invalid, text valid obj = {"num": 3, "text": None} try: self.schema.coerce(obj, FI) except InputError as e: assert e.member is self.schema assert len(e.errors) == 1 error = e.errors[0] assert isinstance(error, MinValueError) assert error.member is self.schema["num"] else: raise AssertionError( f"Coercing {obj} should raise an input error on 'num'") # num valid, text invalid obj = {"num": 20, "text": "hum"} try: self.schema.coerce(obj, FI) except InputError as e: assert e.member is self.schema assert len(e.errors) == 1 error = e.errors[0] assert isinstance(error, MinLengthError) assert error.member is self.schema["text"] else: raise AssertionError( f"Coercing {obj} should raise a validation error on 'text'") # both invalid obj = {"num": 3, "text": "hum"} try: self.schema.coerce(obj, FI) except InputError as e: assert e.member is self.schema assert len(e.errors) == 1 assert e.errors[0].member in list(self.schema.members().values()) else: raise AssertionError( f"Coercing {obj} should raise a validation error") # both valid obj = {"num": 15, "text": "spam!"} original = obj.copy() result = self.schema.coerce(obj, FI) assert obj is result assert result == original def test_can_aggregate_errors(self): from cocktail.schema import Coercion from cocktail.schema.exceptions import InputError def bad_keys(e): keys = set() for error in e.errors: keys.update(member.name for member in error.invalid_members) return keys # num invalid, text valid obj = {"num": 3, "text": None} try: self.schema.coerce(obj, Coercion.FAIL) except InputError as e: assert bad_keys(e) == {"num"} else: raise AssertionError( f"Coercing {obj} should raise a validation error on 'num'") # num valid, text invalid obj = {"num": 20, "text": "hum"} try: self.schema.coerce(obj, Coercion.FAIL) except InputError as e: assert bad_keys(e) == {"text"} else: raise AssertionError( f"Coercing {obj} should raise a validation error on 'text'") # both invalid obj = {"num": 3, "text": "hum"} try: self.schema.coerce(obj, Coercion.FAIL) except InputError as e: assert bad_keys(e) == {"num", "text"} else: raise AssertionError( f"Coercing {obj} should raise a validation error") # both valid obj = {"num": 15, "text": "spam!"} original = obj.copy() result = self.schema.coerce(obj, Coercion.FAIL) assert obj is result assert result == original
def handle_request(self): # Set the starting MIME type for the response (handlers may change it # uppon invocation) response_type = self.choose_response_type() if response_type: cherrypy.response.headers["Content-Type"] = response_type # Handle CORS if self.should_handle_cors(): self.handle_cors() # Fire the 'before_reading_input' event for handler in self.iter_handlers_from_root(): handler.before_reading_input(handler=self) # Assign arguments in the path args = getattr(cherrypy.request, "arguments", None) self.arguments = args if args else {} # Process query string parameters if self.parameters: # Collect values from the query string, applying the proper # deserialization procedure (see cocktail.controllers.parameters) self.values = {} for parameter in self.parameters: try: value = cherrypy.request.params[parameter.name] except KeyError: value = parameter.produce_default() else: try: value = parameter.parse(value) except ValueError: pass if self.parameter_coercion is not Coercion.FAIL: value = parameter.coerce( value, self.parameter_coercion, **self.parameter_validation_options ) self.values[parameter.name] = value # If parameter coercion was set to FAIL, apply the delayed # validation now that all parameters have been collected if self.parameter_coercion is Coercion.FAIL: parameters_schema = Schema( name = get_full_name(self.__node.__class__) + "." + self.method_name, members=self.parameters ) parameters_schema.coerce( self.values, Coercion.FAIL, **self.parameter_validation_options ) # Process the request body if self.request_body: self.data = self.request_body.process() # Fire the 'responding' event for handler in self.iter_handlers_from_root(): handler.responding(handler=self) # Invoke the method's logic response_data = self.respond() # Automatically format responses (ie. JSON and YAML) if self.format_response: response_data = format_response(response_data) # Automatically encode unicode responses if self.response_encoding and isinstance(response_data, str): response_data = response_data.encode(self.response_encoding) return response_data