1 """Helpers to redact sensitive data."""
3 from __future__
import annotations
5 from collections.abc
import Callable, Iterable, Mapping
6 from typing
import Any, cast, overload
10 REDACTED =
"**REDACTED**"
14 x: str | Any, unmasked_prefix: int = 4, unmasked_suffix: int = 4
16 """Mask part of a string with *."""
17 if not isinstance(x, str):
20 unmasked = unmasked_prefix + unmasked_suffix
21 if len(x) < unmasked * 2:
24 if not unmasked_prefix
and not unmasked_suffix:
27 suffix = x[-unmasked_suffix:]
if unmasked_suffix
else ""
28 return f
"{x[:unmasked_prefix]}***{suffix}"
33 data: Mapping, to_redact: Iterable[Any] | Mapping[Any, Callable[[_ValueT], _ValueT]]
39 data: _T, to_redact: Iterable[Any] | Mapping[Any, Callable[[_ValueT], _ValueT]]
45 data: _T, to_redact: Iterable[Any] | Mapping[Any, Callable[[_ValueT], _ValueT]]
47 """Redact sensitive data in a dict."""
48 if not isinstance(data, (Mapping, list)):
51 if isinstance(data, list):
56 for key, value
in redacted.items():
59 if isinstance(value, str)
and not value:
62 if isinstance(to_redact, Mapping):
63 redacted[key] = to_redact[key](value)
65 redacted[key] = REDACTED
66 elif isinstance(value, Mapping):
68 elif isinstance(value, list):
71 return cast(_T, redacted)
dict async_redact_data(Mapping data, Iterable[Any] to_redact)
str partial_redact(str|Any x, int unmasked_prefix=4, int unmasked_suffix=4)