source: utilitiespy/tudelft/utilities/exception/ExceptionContainer.py@ 1451

Last change on this file since 1451 was 1451, checked in by wouter, 6 hours ago

fix hashcode issue in ExceptionContainer

File size: 4.8 KB
Line 
1from __future__ import annotations
2from typing import Optional, List
3from tudelft.utilities.tools.safehash import safehash
4
5class ExceptionContainer:
6 '''
7 An exception container contains the contents of an exception.
8 It's a standard frmat shared with Java so that we can exchange exceptions.
9 @param classname the class name of the exception, eg eg
10 "java.lang.NullPointerException", "AttributeError"
11 @param message short human-readable message explaining what happened.
12 null is used as "".
13 @param stacktrace a list of strings, each a stack point. May contain
14 filename, class name, line number, column number. Kept
15 general for now as currently this is intended for human
16 use only
17 @param cause another {@link ExceptionContainer} holding the
18 exception that caused this, or None if there was no
19 other cause.
20 '''
21 def __init__(self, classname:str, message:Optional[str],
22 stacktrace:List[str],cause: Optional[ExceptionContainer] ):
23 if classname == None or stacktrace == None:
24 raise AttributeError("classname and stacktrace must be not None")
25 self.__classname = classname
26 self.__message:str = "" if message == None else message
27 self.__stacktrace = stacktrace
28 self.__cause = cause
29
30
31 @staticmethod
32 def create( trace:str) -> ExceptionContainer:
33 '''
34 This creates a ExceptionContainer from a trace message.
35 The trace message is found in the errors and failures fields
36 of a TestResult object. The error field contains a list of tuples
37 and the 2nd tuple is the trace message.
38 It looks like a general error message, eg
39 Traceback (...):
40 File "blabla.py", line 22 in class1:
41 method(...)
42 File "blablabla.py" line 11 in class2:
43 raise blabla(....)
44 SomeErorr: errormessageblabla
45
46 The above exception was the direct cause .....
47
48 Traceback (....):
49 File ....
50
51
52 This trace message needs to be decyphered into a ExceptionContainer format
53 '''
54 return ExceptionContainer.create1(trace.split("\nThe above exception was the direct cause of the following exception:\n\n"),None)
55
56 @staticmethod
57 def create1(traces:List[str], cause:Optional[ExceptionContainer] ):
58 if len(traces)==0:
59 return cause
60 # there are more causes. process the first
61 trace = traces[0].split("\n")
62 err = trace[-1].split(":",1)
63 clazz = err[0]
64 message=err[1]
65 stacklist=trace[:-1]
66
67 # pre-pend this new cause before the existing cause
68 cause = ExceptionContainer(clazz, message, stacklist, cause)
69 # and continue with rest of the traces
70 return ExceptionContainer.create1(traces[1:],cause)
71
72 @staticmethod
73 def fromException(t:Exception) -> Optional[ExceptionContainer]:
74 '''
75 creates container holding given exxception.
76 Note that python exceptions do not contain a stacktrace.
77 NOTE you may want to use create(stacktrace).
78 '''
79 msg:str = t.args[0] if t.args else ""
80 name:str = type(t).__name__;
81 cause:Optional[ExceptionContainer] = None if not t.__cause__ else \
82 ExceptionContainer.fromException(t.__cause__)
83 return ExceptionContainer(name, msg, [],cause);
84
85 def getClassName(self) -> str:
86 return self.__classname
87
88 def getMessage(self)->str :
89 return self.__message
90
91 def getStackTrace(self) -> List[str]:
92 return self.__stacktrace
93
94 def getCause(self) ->Optional[ExceptionContainer]:
95 return self.__cause
96
97 def toJson(self):
98 '''
99 bit hacky, avoid use of pyson here to keep the libraries independent
100 '''
101 return {'classname':self.__classname, 'message':self.__message,
102 'stacktrace':self.__stacktrace, 'cause':self.__cause}
103
104 def __repr__(self):
105 mesg:str = self.__classname + ":" + self.__message + "\n"\
106 + "\n".join(self.__stacktrace)
107 if self.__cause != None:
108 mesg = mesg + "\n caused by:\n" + str(self.__cause)
109 return mesg;
110
111 def __hash__(self):
112 return safehash((self.__cause, self.__classname, self.__message, self.__stacktrace))
113
114 def __eq__(self, obj):
115 if self == obj:
116 return True
117 if obj == None:
118 return False
119 if type(self) != type(obj):
120 return False
121 return self.__cause==obj.cause \
122 and self.__classname == obj.classname \
123 and self.__message == obj.message \
124 and self.__stacktrace == obj.stacktrace
Note: See TracBrowser for help on using the repository browser.