File tree 2 files changed +56
-6
lines changed
2 files changed +56
-6
lines changed Original file line number Diff line number Diff line change 1
- """ osparc ERROR CODES (OEC)
1
+ """osparc ERROR CODES (OEC)
2
2
Unique identifier of an exception instance
3
3
Intended to report a user about unexpected errors.
4
4
Unexpected exceptions can be traced by matching the
7
7
SEE test_error_codes for some use cases
8
8
"""
9
9
10
+ import hashlib
10
11
import re
12
+ import traceback
11
13
from typing import TYPE_CHECKING , Annotated
12
14
13
15
from pydantic import StringConstraints , TypeAdapter
14
16
15
17
_LABEL = "OEC:{}"
16
- _PATTERN = r"OEC:\d +"
18
+ _PATTERN = r"OEC:[a-zA-Z0-9] +"
17
19
18
20
if TYPE_CHECKING :
19
21
ErrorCodeStr = str
22
24
str , StringConstraints (strip_whitespace = True , pattern = _PATTERN )
23
25
]
24
26
27
+ _LEN = 12 # chars (~48 bits)
28
+
29
+
30
+ def _generate_error_fingerprint (exc : BaseException ) -> str :
31
+ """
32
+ Unique error fingerprint for deduplication purposes
33
+ """
34
+ tb = traceback .extract_tb (exc .__traceback__ )
35
+ frame_sigs = [f"{ frame .name } :{ frame .lineno } " for frame in tb ]
36
+ fingerprint = f"{ type (exc ).__name__ } |" + "|" .join (frame_sigs )
37
+ # E.g. ZeroDivisionError|foo:23|main:10
38
+ return hashlib .sha256 (fingerprint .encode ()).hexdigest ()[:_LEN ]
39
+
25
40
26
41
def create_error_code (exception : BaseException ) -> ErrorCodeStr :
27
- return TypeAdapter (ErrorCodeStr ).validate_python (_LABEL .format (id (exception )))
42
+ return TypeAdapter (ErrorCodeStr ).validate_python (
43
+ _LABEL .format (_generate_error_fingerprint (exception ))
44
+ )
28
45
29
46
30
47
def parse_error_code (obj ) -> set [ErrorCodeStr ]:
Original file line number Diff line number Diff line change 11
11
logger = logging .getLogger (__name__ )
12
12
13
13
14
- def test_error_code_use_case (caplog : pytest .LogCaptureFixture ):
15
- """use case for error-codes"""
14
+ def _level_three (v ):
15
+ msg = f"An error occurred in level three with { v } "
16
+ raise RuntimeError (msg )
17
+
18
+
19
+ def _level_two (v ):
20
+ _level_three (v )
21
+
22
+
23
+ def _level_one (v = None ):
24
+ _level_two (v )
25
+
26
+
27
+ def test_exception_fingerprint_consistency ():
28
+ error_codes = []
29
+
30
+ for v in range (2 ):
31
+ # emulates different runs of the same function (e.g. different sessions)
32
+ try :
33
+ _level_one (v ) # same even if different value!
34
+ except Exception as err :
35
+ error_code = create_error_code (err )
36
+ error_codes .append (error_code )
37
+
38
+ assert error_codes == [error_codes [0 ]] * len (error_codes )
39
+
40
+ try :
41
+ # Same function but different location
42
+ _level_one (0 )
43
+ except Exception as e2 :
44
+ error_code_2 = create_error_code (e2 )
45
+ assert error_code_2 != error_code [0 ]
46
+
47
+
48
+ def test_create_log_and_parse_error_code (caplog : pytest .LogCaptureFixture ):
16
49
with pytest .raises (RuntimeError ) as exc_info :
17
- raise RuntimeError ( "Something unexpected went wrong" )
50
+ _level_one ( )
18
51
19
52
# 1. Unexpected ERROR
20
53
err = exc_info .value
You can’t perform that action at this time.
0 commit comments