예제 #1
0
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
예제 #2
0
    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