Source code for faust.models.tags
import abc
import sys
from collections import UserString
from contextlib import contextmanager
from decimal import Decimal
from types import FrameType
from typing import (
Any,
Generic,
Iterator,
Optional,
Type,
TypeVar,
cast,
no_type_check,
)
from mode.utils.locals import LocalStack
from faust.exceptions import SecurityError
T = TypeVar('T')
_getframe = getattr(sys, 'emarfteg_'[::-1])
AllowedStack: LocalStack[bool] = LocalStack()
[docs]@contextmanager
def allow_protected_vars() -> Iterator:
with AllowedStack.push(True):
yield
[docs]class Tag(Generic[T]):
value: T
field: Optional[str]
is_secret: bool = False
is_sensitive: bool = False
is_personal: bool = False
def __init__(self, value: T, *,
field: str = None):
if isinstance(value, Tag):
raise SecurityError('Cannot wrap: value is already tagged')
self._value = value
self.field = field
def __set_name__(self, owner: Type, name: str) -> None:
self.field = name
[docs] def get_value(self) -> T:
return self._value
def __repr__(self) -> str:
return f'<{self._name}: {self.field}@{id(self):#x}>'
@abc.abstractmethod
def __str__(self) -> str:
...
@abc.abstractmethod
def __format__(self, format_spec: str) -> str:
...
@property
def _name(self) -> str:
return type(self).__name__
[docs]class OpaqueTag(Tag[T]):
def __str__(self) -> str:
raise SecurityError(f'Attempt to use {self._name} data as a string')
def __format__(self, format_spec: str) -> str:
raise SecurityError(f'Attempt to use {self._name} data as a string')
[docs]class TransparentTag(Tag[T]):
def __str__(self) -> str:
return str(self._prepare_value())
def _prepare_value(self) -> T:
return self._value
def __format__(self, format_spec: str) -> str:
return self._prepare_value().__format__(format_spec)
class _FrameLocal(UserString, Generic[T]):
_field_name: str
_tag_type: str
_frame: str
_value: T
def __init__(self, value: T, *,
field_name: str = '<unknown field>',
tag_type: str = '<unknown tag>') -> None:
self._value = value
self._frame = self._frame_ident(_getframe().f_back.f_back)
self._field_name = field_name
self._tag_type = tag_type
def _access_value(self) -> T:
if AllowedStack.top:
return self._value
current_frame = self._frame_ident(
_getframe().f_back.f_back.f_back)
import traceback
traceback.print_stack()
if current_frame == self._frame:
return self._value
else:
raise SecurityError(
f'Protected {self._tag_type} value from '
f'field {self._field_name} accessed outside origin frame.')
def __repr__(self) -> str:
val = self._value
return f'<Protected {type(val)}: {val!r}@{id(self):#x}>'
def _frame_ident(self, frame: FrameType) -> str:
return '::'.join(map(str, [
# cannot rely on id alone as memory addresses are reused
id(frame),
frame.f_code.co_filename,
frame.f_code.co_name,
]))
@property
def data(self) -> T: # type: ignore
return self._access_value()
[docs]class Personal(OpaqueTag[T]):
is_personal = True
FrameLocal: Type[_FrameLocal] = _FrameLocal
[docs] def get_value(self) -> T:
if AllowedStack.top:
return self._value
else:
return cast(T, self.FrameLocal(
self._value,
field_name=self.field or '<unknown field>',
tag_type=self._name.lower(),
))
@no_type_check
def __class_getitem__(self, params: Any) -> Any:
if not issubclass(params, (str, bytes)):
raise TypeError(f'Personal only supports str/bytes not {params!r}')
return super().__class_getitem__(params)
[docs]class Secret(TransparentTag[T]):
is_secret = True
mask: str = '***********'
def _prepare_value(self) -> T:
return cast(T, self.mask)
[docs]class Sensitive(OpaqueTag[T]):
is_sensitive = True
FrameLocal: Type[_FrameLocal] = _FrameLocal
[docs] def get_value(self) -> T:
return cast(T, self.FrameLocal(
self._value,
field_name=self.field or '<unknown field>',
tag_type=self._name.lower(),
))
def __class_getitem__(self, params: Any) -> Any:
if not issubclass(params, (str, bytes)):
raise TypeError(f'Personal only supports str/bytes not {params!r}')
# bypass mypy bug by using getattr
sup = getattr(super(), '__class_getitem__') # noqa: B009
return sup(params)
class _PIIstr(str):
...
class _PIIbytes(bytes):
...
class _PIIint(int):
...
class _PIIfloat(float):
...
class _PIIDecimal(Decimal):
...