Coverage for src/loman/exception.py: 100%

26 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-09-07 00:32 +0000

1"""Exception classes for the loman computation engine.""" 

2 

3 

4class ComputationError(Exception): 

5 """Base exception for computation-related errors.""" 

6 

7 

8class MapError(ComputationError): 

9 """Exception raised during map operations with partial results.""" 

10 

11 def __init__(self, message: str, results: list[object]) -> None: 

12 """Initialize MapError with message and partial results.""" 

13 super().__init__(message) 

14 self.results = results 

15 

16 

17class LoopDetectedError(ComputationError): 

18 """Exception raised when a dependency loop is detected.""" 

19 

20 

21class NonExistentNodeError(ComputationError): 

22 """Exception raised when trying to access a non-existent node.""" 

23 

24 

25class NodeAlreadyExistsError(ComputationError): 

26 """Exception raised when trying to create a node that already exists.""" 

27 

28 

29class CannotInsertToPlaceholderNodeError(ComputationError): 

30 """Exception raised when trying to insert into a placeholder node.""" 

31 

32 

33class InvalidBlockTypeError(TypeError, ComputationError): 

34 """Exception raised when a block is not callable or a Computation.""" 

35 

36 

37class FittingError(ComputationError): 

38 """Exception raised when curve fitting exceeds error tolerance.""" 

39 

40 

41class ValidationError(ComputationError): 

42 """Exception raised during computation validation.""" 

43 

44 

45class SerializationError(ComputationError): 

46 """Exception raised during serialization/deserialization.""" 

47 

48 

49class DeserializedError(ComputationError): 

50 """Stand-in for an exception whose original class could not be rebuilt. 

51 

52 A saved ERROR node records the name of the exception that produced it. 

53 Rebuilding an arbitrary one would mean importing whatever module the file 

54 names, which is executing code chosen by the file, so only builtin exception 

55 types are reconstructed. Everything else becomes one of these, carrying the 

56 original identity as data for post-mortem reading. 

57 

58 :ivar exception_type: Name of the exception class that was originally raised. 

59 :ivar exception_module: Module that class came from, when it was recorded. 

60 """ 

61 

62 def __init__(self, message: str, exception_type: str, exception_module: str | None = None) -> None: 

63 """Record the original exception's identity alongside its message.""" 

64 super().__init__(message) 

65 self.exception_type = exception_type 

66 self.exception_module = exception_module 

67 

68 def __repr__(self) -> str: 

69 """Show the original exception's type so it is not mistaken for this one.""" 

70 qualified = f"{self.exception_module}.{self.exception_type}" if self.exception_module else self.exception_type 

71 return f"DeserializedError({qualified}: {super().__str__()!r})" 

72 

73 

74# Backward compatibility aliases 

75MapException = MapError 

76LoopDetectedException = LoopDetectedError 

77NonExistentNodeException = NonExistentNodeError 

78NodeAlreadyExistsException = NodeAlreadyExistsError 

79CannotInsertToPlaceholderNodeException = CannotInsertToPlaceholderNodeError