File tree Expand file tree Collapse file tree 2 files changed +56
-6
lines changed Expand file tree Collapse file tree 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)
22 Unique identifier of an exception instance
33 Intended to report a user about unexpected errors.
44 Unexpected exceptions can be traced by matching the
77SEE test_error_codes for some use cases
88"""
99
10+ import hashlib
1011import re
12+ import traceback
1113from typing import TYPE_CHECKING , Annotated
1214
1315from pydantic import StringConstraints , TypeAdapter
1416
1517_LABEL = "OEC:{}"
16- _PATTERN = r"OEC:\d +"
18+ _PATTERN = r"OEC:[a-zA-Z0-9] +"
1719
1820if TYPE_CHECKING :
1921 ErrorCodeStr = str
2224 str , StringConstraints (strip_whitespace = True , pattern = _PATTERN )
2325 ]
2426
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+
2540
2641def 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+ )
2845
2946
3047def parse_error_code (obj ) -> set [ErrorCodeStr ]:
Original file line number Diff line number Diff line change 1111logger = logging .getLogger (__name__ )
1212
1313
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 ):
1649 with pytest .raises (RuntimeError ) as exc_info :
17- raise RuntimeError ( "Something unexpected went wrong" )
50+ _level_one ( )
1851
1952 # 1. Unexpected ERROR
2053 err = exc_info .value
You can’t perform that action at this time.
0 commit comments