from typing import Callable
from typing import Optional
import re


def allow_fail(msg: str = 'Failed to process tag. Skipping.') -> Callable:
    """
    If function func fails for some reason, warning is issued but preprocessor
    doesn't terminate. In this case the tag remains unchanged.
    Decorator issues a warning to user with BasePreprocessorExt _warning method.
    If first positional argument is a match object, it is passed as context.
    """

    def decorator(func):
        def wrapper(self, *args, **kwargs) -> Optional[str]:
            try:
                return func(self, *args, **kwargs)
            except Exception as e:
                if args and isinstance(args[0], re.Match):
                    self._warning(f'{msg} {e}',
                                  context=self.get_tag_context(args[0]),
                                  error=e)
                    return args[0].group(0)
                else:
                    self._warning(f'{msg} {e}', error=e)
                    return None
        return wrapper
    return decorator