| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """ |
| Contains validation related utilities |
| """ |
| |
| from .formatting import string_list_as_string |
| |
| |
| class ValidatorMixin(object): |
| """ |
| A mixin that should be added to classes that require validating user input |
| """ |
| |
| _ARGUMENT_TYPE_MESSAGE = '{name} argument must be {type} based, got {arg!r}' |
| _ARGUMENT_CHOICE_MESSAGE = '{name} argument must be in {choices}, got {arg!r}' |
| |
| @classmethod |
| def validate_in_choice(cls, name, argument, choices): |
| """ |
| Validate ``argument`` is in ``choices`` |
| """ |
| if argument not in choices: |
| raise TypeError(cls._ARGUMENT_CHOICE_MESSAGE.format( |
| name=name, choices=choices, arg=argument)) |
| |
| @classmethod |
| def validate_type(cls, argument_name, argument, expected_type): |
| """ |
| Validate ``argument`` is a subclass of ``expected_type`` |
| """ |
| if not issubclass(argument, expected_type): |
| raise TypeError(cls._ARGUMENT_TYPE_MESSAGE.format( |
| name=argument_name, type=expected_type, arg=argument)) |
| |
| @classmethod |
| def validate_instance(cls, argument_name, argument, expected_type): |
| """ |
| Validate ``argument`` is a instance of ``expected_type`` |
| """ |
| if not isinstance(argument, expected_type): |
| raise TypeError(cls._ARGUMENT_TYPE_MESSAGE.format( |
| name=argument_name, type=expected_type, arg=argument)) |
| |
| @classmethod |
| def validate_callable(cls, argument_name, argument): |
| """ |
| Validate ``argument`` is callable |
| """ |
| if not callable(argument): |
| raise TypeError(cls._ARGUMENT_TYPE_MESSAGE.format( |
| name=argument_name, type='callable', arg=argument)) |
| |
| |
| def validate_function_arguments(func, func_kwargs): |
| """ |
| Validates all required arguments are supplied to ``func`` and that no additional arguments are |
| supplied |
| """ |
| |
| _kwargs_flags = 8 |
| |
| has_kwargs = func.func_code.co_flags & _kwargs_flags != 0 |
| args_count = func.func_code.co_argcount |
| |
| # all args without the ones with default values |
| args = func.func_code.co_varnames[:args_count] |
| non_default_args = args[:len(func.func_defaults)] if func.func_defaults else args |
| |
| # Check if any args without default values is missing in the func_kwargs |
| for arg in non_default_args: |
| if arg not in func_kwargs: |
| raise ValueError( |
| 'The argument "{arg}" is not provided and does not have a default value for ' |
| 'function "{func.__name__}"'.format(arg=arg, func=func)) |
| |
| # check if there are any extra kwargs |
| extra_kwargs = [arg for arg in func_kwargs.keys() if arg not in args] |
| |
| # assert that the function has kwargs |
| if extra_kwargs and not has_kwargs: |
| raise ValueError("The following extra kwargs were supplied: {extra_kwargs}".format( |
| extra_kwargs=string_list_as_string(extra_kwargs) |
| )) |