Exceptions
Decorators and utilities for prefixing exception stack traces while obscuring the exception message itself.
        PublicArgumentError
    Argument error with public message. Exceptions of this type raised under
prefix_stack_trace or print_prefixed_stack_trace_and_raise will have
the message prefixed with PREFIX in both the printed stack trace and the
re-raised exception.
        PublicFileNotFoundError
    File not found error with public message. Exceptions of this type raised
under prefix_stack_trace or print_prefixed_stack_trace_and_raise will
have the message prefixed with PREFIX in both the printed stack trace and
the re-raised exception.
        PublicIndexError
    Index error with public message. Exceptions of this type raised under
prefix_stack_trace or print_prefixed_stack_trace_and_raise will have
the message prefixed with PREFIX in both the printed stack trace and the
re-raised exception.
        PublicIOError
    I/O error with public message. Exceptions of this type raised
under prefix_stack_trace or print_prefixed_stack_trace_and_raise will
    have the message prefixed with PREFIX in both the printed stack trace and
    the re-raised exception.
        PublicKeyError
    Key error with public message. Exceptions of this type raised under
prefix_stack_trace or print_prefixed_stack_trace_and_raise will have
the message prefixed with PREFIX in both the printed stack trace and the
re-raised exception.
        PublicNotImplementedError
    Not implemented error with public message. Exceptions of this type raised
under prefix_stack_trace or print_prefixed_stack_trace_and_raise will
have the message prefixed with PREFIX in both the printed stack trace and
the re-raised exception.
        PublicRuntimeError
    Runtime error with public message. Exceptions of this type raised under
prefix_stack_trace or print_prefixed_stack_trace_and_raise will have
the message prefixed with PREFIX in both the printed stack trace and the
re-raised exception.
        PublicTypeError
    Type error with public message. Exceptions of this type raised under
prefix_stack_trace or print_prefixed_stack_trace_and_raise will have
the message prefixed with PREFIX in both the printed stack trace and the
re-raised exception.
        PublicValueError
    Value error with public message. Exceptions of this type raised under
prefix_stack_trace or print_prefixed_stack_trace_and_raise will have
the message prefixed with PREFIX in both the printed stack trace and the
re-raised exception.
is_exception_allowed(exception, allow_list)
    Check if message is allowed, either by allow_list, or default_allow_list.
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
exception | 
        Union[BaseException, traceback.TracebackException] | 
        the exception to test  | 
        required | 
allow_list | 
        list | 
        list of regex expressions. If any expression matches the exception name or message, it will be considered allowed.  | 
        required | 
Returns:
| Type | Description | 
|---|---|
bool | 
      bool: True if message is allowed, False otherwise.  | 
    
Source code in shrike/compliant_logging/exceptions.py
          def is_exception_allowed(
    exception: Union[BaseException, TracebackException], allow_list: list
) -> bool:
    """
    Check if message is allowed, either by `allow_list`, or `default_allow_list`.
    Args:
        exception (TracebackException): the exception to test
        allow_list (list): list of regex expressions. If any expression matches
            the exception name or message, it will be considered allowed.
    Returns:
        bool: True if message is allowed, False otherwise.
    """
    if not isinstance(exception, TracebackException):
        exception = TracebackException.from_exception(exception)
    # empty list means all messages are allowed
    for expr in allow_list + default_allow_list:
        if re.search(expr, getattr(exception, "_str", ""), re.IGNORECASE):
            return True
        if re.search(expr, getattr(exception.exc_type, "__name__", ""), re.IGNORECASE):
            return True
    return False
prefix_stack_trace(file=<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, disable=False, prefix='SystemLog:', scrub_message='**Exception message scrubbed**', keep_message=False, allow_list=[], add_timestamp=False)
    Decorator which wraps the decorated function and prints the stack trace of
exceptions which occur, prefixed with prefix and with exception messages
scrubbed (replaced with scrub_message). To use this, just add
@prefix_stack_trace() above your function definition, e.g.
@prefix_stack_trace()
def foo(x):
    pass
Source code in shrike/compliant_logging/exceptions.py
          def prefix_stack_trace(
    file: TextIO = sys.stderr,
    disable: bool = bool(sys.flags.debug),
    prefix: str = PREFIX,
    scrub_message: str = SCRUB_MESSAGE,
    keep_message: bool = False,
    allow_list: list = [],
    add_timestamp: bool = False,
) -> Callable:
    """
    Decorator which wraps the decorated function and prints the stack trace of
    exceptions which occur, prefixed with `prefix` and with exception messages
    scrubbed (replaced with `scrub_message`). To use this, just add
    `@prefix_stack_trace()` above your function definition, e.g.
        @prefix_stack_trace()
        def foo(x):
            pass
    """
    global SCRUBBED_EXCEPTIONS
    SCRUBBED_EXCEPTIONS = set()
    return _PrefixStackTraceWrapper(
        file, disable, prefix, scrub_message, keep_message, allow_list, add_timestamp
    )
print_prefixed_stack_trace_and_raise(file=<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, prefix='SystemLog:', scrub_message='**Exception message scrubbed**', keep_message=False, allow_list=[], add_timestamp=False, err=None)
    Print the current exception and stack trace to file (usually client
standard error), prefixing the stack trace with prefix.
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
keep_message | 
        bool | 
        if True, don't scrub message. If false, scrub (unless allowed).  | 
        False | 
      
allow_list | 
        list | 
        exception allow_list. Ignored if keep_message is True. If empty all messages will be srubbed.  | 
        [] | 
      
err | 
        Optional[BaseException] | 
        the error that was thrown. None accepted for backwards compatibility.  | 
        None | 
      
Source code in shrike/compliant_logging/exceptions.py
          def print_prefixed_stack_trace_and_raise(
    file: TextIO = sys.stderr,
    prefix: str = PREFIX,
    scrub_message: str = SCRUB_MESSAGE,
    keep_message: bool = False,
    allow_list: list = [],
    add_timestamp: bool = False,
    err: Optional[BaseException] = None,
) -> None:
    """
    Print the current exception and stack trace to `file` (usually client
    standard error), prefixing the stack trace with `prefix`.
    Args:
        keep_message (bool): if True, don't scrub message. If false, scrub (unless
            allowed).
        allow_list (list): exception allow_list. Ignored if keep_message is True. If
            empty all messages will be srubbed.
        err: the error that was thrown. None accepted for backwards compatibility.
    """
    if err is None:
        err = sys.exc_info()[1]
    scrubbed_err = err
    if err not in SCRUBBED_EXCEPTIONS:
        scrubbed_err = scrub_exception(
            err, scrub_message, prefix, keep_message, allow_list
        )
        SCRUBBED_EXCEPTIONS.add(scrubbed_err)
    tb_exception = TracebackException.from_exception(scrubbed_err)  # type: ignore
    for execution in tb_exception.format():
        if "return function(*func_args, **func_kwargs)" in execution:
            # Do not show the stack trace for our decorator.
            continue
        for line in execution.splitlines():
            if add_timestamp:
                current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
                print(f"{prefix} {current_time} {line}", file=file)
            else:
                print(f"{prefix} {line}", file=file)
    raise scrubbed_err  # type: ignore
scrub_exception(exception, scrub_message, prefix, keep_message, allow_list, _seen=None)
    Recursively scrub all potentially private data from an exception, using the
logic in _attribute_transformer.
Inspired by Dan Schwartz's closed-source implementation: https://dev.azure.com/eemo/TEE/_git/TEEGit?path=%2FOffline%2FFocusedInbox%2FComTriage%2Fcomtriage%2Futils%2Fscrubber.py&version=GBcompliant%2FComTriage&_a=content
which is closely based on the CPython implementation of the TracebackException class: https://github.com/python/cpython/blob/master/Lib/traceback.py#L478
Source code in shrike/compliant_logging/exceptions.py
          def scrub_exception(
    exception: Optional[BaseException],
    scrub_message: str,
    prefix: str,
    keep_message: bool,
    allow_list: list,
    _seen: Optional[Set[int]] = None,
) -> Optional[BaseException]:
    """
    Recursively scrub all potentially private data from an exception, using the
    logic in `_attribute_transformer`.
    Inspired by Dan Schwartz's closed-source implementation:
    https://dev.azure.com/eemo/TEE/_git/TEEGit?path=%2FOffline%2FFocusedInbox%2FComTriage%2Fcomtriage%2Futils%2Fscrubber.py&version=GBcompliant%2FComTriage&_a=content
    which is closely based on the CPython implementation of the
    TracebackException class:
    https://github.com/python/cpython/blob/master/Lib/traceback.py#L478
    """
    if not exception:
        return None
    # Handle loops in __cause__ or __context__ .
    if _seen is None:
        _seen = set()
    _seen.add(id(exception))
    # Gracefully handle being called with no type or value.
    if exception.__cause__ is not None and id(exception.__cause__) not in _seen:
        exception.__cause__ = scrub_exception(
            exception.__cause__,
            scrub_message,
            prefix,
            keep_message,
            allow_list,
            _seen,
        )
    if exception.__context__ is not None and id(exception.__context__) not in _seen:
        exception.__context__ = scrub_exception(
            exception.__context__,
            scrub_message,
            prefix,
            keep_message,
            allow_list,
            _seen,
        )
    keep = keep_message or is_exception_allowed(exception, allow_list)
    transformer = _attribute_transformer(prefix, scrub_message, keep)
    if not keep:
        for attr in dir(exception):
            if attr and not attr.startswith("__"):
                try:
                    value = getattr(exception, attr)
                except AttributeError:
                    # In some cases, e.g. FileNotFoundError, there are attributes
                    # which show up in dir(e), but for which an AttributeError is
                    # thrown when attempting to access the value. See, e.g.:
                    # https://stackoverflow.com/q/47775772 .
                    continue
                try:
                    # If unable to transform or set the attribute, replace the
                    # entire exception since the attribute value is readable, but
                    # we are unable to scrub it.
                    new_value = transformer(value)
                    setattr(exception, attr, new_value)
                except BaseException as e:
                    new_exception = PublicRuntimeError(
                        f"{prefix} Obtained {type(e).__name__} when trying to scrub {attr} from {type(exception).__name__}"  # noqa: E501
                    )
                    new_exception.__cause__ = exception.__cause__
                    new_exception.__context__ = exception.__context__
                    exception = new_exception
                    break
    return exception