Coverage for flogin/flow/base.py: 100%

28 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-03 22:51 +0000

1from __future__ import annotations 

2 

3from collections.abc import Callable 

4from inspect import getmembers 

5from typing import Any 

6 

7from ..utils import MISSING 

8 

9ValidCls = Callable[[Any], Any] 

10 

11 

12def _convert_cls(orig: ValidCls, is_list: bool) -> ValidCls: 

13 if orig is MISSING: 

14 return lambda x: x 

15 if orig is not MISSING and is_list is True: 

16 return lambda item: [orig(x) for x in item] 

17 return orig 

18 

19 

20def _get_prop_func( 

21 cls: ValidCls, name: str, *, default: Any = MISSING 

22) -> Callable[[Any], Any]: 

23 if default is MISSING: 

24 

25 def func(self: Any) -> Any: 

26 return cls(self._data[name]) 

27 

28 else: 

29 

30 def func(self: Any) -> Any: 

31 return cls(self._data.get(name, default)) 

32 

33 return func 

34 

35 

36def add_prop( 

37 name: str, 

38 *, 

39 default: Any = MISSING, 

40 cls: ValidCls = MISSING, 

41 is_list: bool = False, 

42) -> Any: 

43 cls = _convert_cls(cls, is_list) 

44 func = _get_prop_func(cls, name, default=default) 

45 

46 return property(func) 

47 

48 

49class Base: 

50 __slots__ = ("__repr_attributes__", "_data") 

51 

52 def __init__(self, data: dict[str, Any]) -> None: 

53 self._data = data 

54 self.__repr_attributes__ = [ 

55 entry[0] 

56 for entry in getmembers( 

57 self.__class__, lambda other: isinstance(other, property) 

58 ) 

59 ] 

60 

61 def __repr__(self) -> str: 

62 args = [f"{item}={getattr(self, item)!r}" for item in self.__repr_attributes__] 

63 return f"<{self.__class__.__name__} {' '.join(args)}>"