Wraps given decorator in order to provide additional functionality:
    optional arguments and verification of decorator's target type.

    :param decorator: Decorator callable
    :param targets: Name of the decorator targets, as plural
                    (used in error messages)
    :param is_valid_target: Callable for checking
                            whether decorator's target is valid

    :return: Wrapped ``decorator``

def _wrap_decorator(decorator, targets, is_valid_target):
    """Wraps given decorator in order to provide additional functionality:
    optional arguments and verification of decorator's target type.

    :param decorator: Decorator callable
    :param targets: Name of the decorator targets, as plural
                    (used in error messages)
    :param is_valid_target: Callable for checking
                            whether decorator's target is valid

    :return: Wrapped ``decorator``
    """
    @functools.wraps(decorator)
    def wrapper(*args, **kwargs):
        # handle the case when decorator is applied as ``@decorator``
        # (without any parameters and parentheses)
        one_arg = len(args) == 1 and not kwargs
        if one_arg and is_valid_target(args[0]):
            actual_decorator = decorator()
            return actual_decorator(args[0])

        # pass the parameters to decorator callable
        # to get the actual decorator that can be applied to targets
        actual_decorator = decorator(*args, **kwargs)
        # TODO(xion): The above raises TypeError with confusing message
        # ("<class>.__new__() takes no parameters") when function decorator
        # is applied to a class. See if we can detect that and do better.

        # wrap it inside a function that verifies
        # whether a target that user has given is valid for this decorator
        def decorator_wrapper(target):
            if not is_valid_target(target):
                raise TypeError(
                    "@%s can only be applied to %s: got %r instead" % (
                        decorator.__name__, targets, type(target)))
            return actual_decorator(target)

        return decorator_wrapper

    return wrapper