Coverage for src/objectstate/edit_session.py: 40%

40 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-17 07:22 +0000

1"""Generic edit-session boundary for ObjectState-backed objects.""" 

2 

3from __future__ import annotations 

4 

5from dataclasses import dataclass 

6from typing import TYPE_CHECKING, Callable, Generic, TypeVar, cast 

7 

8if TYPE_CHECKING: 

9 from objectstate.object_state import ObjectState 

10 

11 

12EditedObjectT = TypeVar("EditedObjectT") 

13 

14 

15@dataclass(slots=True) 

16class ObjectStateEditSession(Generic[EditedObjectT]): 

17 """Reconstruct and inspect an edited object through ObjectState. 

18 

19 ObjectState owns parameter storage, dirty state, lazy dataclass 

20 reconstruction, and save/cancel baselines. This session is a small 

21 fail-loud boundary for windows or other callers that need the current 

22 edited object without reimplementing ObjectState semantics. 

23 """ 

24 

25 state_provider: Callable[[], ObjectState | None] 

26 fallback_object: EditedObjectT | None = None 

27 expected_type: type[EditedObjectT] | None = None 

28 

29 @property 

30 def state(self) -> ObjectState | None: 

31 return self.state_provider() 

32 

33 def require_state(self, parameter_name: str) -> ObjectState: 

34 state = self.state 

35 if state is None: 

36 raise KeyError( 

37 f"ObjectStateEditSession has no state for parameter {parameter_name!r}" 

38 ) 

39 return state 

40 

41 def to_object(self, *, update_delegate: bool = False) -> EditedObjectT: 

42 state = self.state 

43 if state is None: 

44 value = self.fallback_object 

45 else: 

46 value = state.to_object(update_delegate=update_delegate) 

47 

48 if value is None: 

49 raise RuntimeError("ObjectStateEditSession has no object to reconstruct") 

50 if self.expected_type is not None and not isinstance(value, self.expected_type): 

51 raise TypeError( 

52 "ObjectStateEditSession reconstructed " 

53 f"{type(value).__name__}; expected {self.expected_type.__name__}" 

54 ) 

55 return cast(EditedObjectT, value) 

56 

57 def has_parameter(self, parameter_name: str) -> bool: 

58 state = self.state 

59 return state is not None and parameter_name in state.parameters 

60 

61 def parameter_value(self, parameter_name: str): 

62 state = self.require_state(parameter_name) 

63 if parameter_name not in state.parameters: 

64 raise KeyError(f"ObjectState parameter does not exist: {parameter_name!r}") 

65 return state.parameters[parameter_name] 

66 

67 def update_parameter(self, parameter_name: str, value) -> None: 

68 state = self.require_state(parameter_name) 

69 if parameter_name not in state.parameters: 

70 raise KeyError(f"ObjectState parameter does not exist: {parameter_name!r}") 

71 state.update_parameter(parameter_name, value)