11import base64
2+ import functools
23import json
34import linecache
45import logging
7778FALSY_ENV_VALUES = frozenset (("false" , "f" , "n" , "no" , "off" , "0" ))
7879TRUTHY_ENV_VALUES = frozenset (("true" , "t" , "y" , "yes" , "on" , "1" ))
7980
81+ MAX_STACK_FRAMES = 2000
82+ """Maximum number of stack frames to send to Sentry.
83+
84+ If we have more than this number of stack frames, we will stop processing
85+ the stacktrace to avoid getting stuck in a long-lasting loop. This value
86+ exceeds the default sys.getrecursionlimit() of 1000, so users will only
87+ be affected by this limit if they have a custom recursion limit.
88+ """
89+
8090
8191def env_to_bool (value , * , strict = False ):
8292 # type: (Any, Optional[bool]) -> bool | None
@@ -667,7 +677,7 @@ def single_exception_from_error_tuple(
667677 source = None , # type: Optional[str]
668678 full_stack = None , # type: Optional[list[dict[str, Any]]]
669679):
670- # type: (...) -> Dict[str, Any]
680+ # type: (...) -> Annotated[ Dict[str, Any] ]
671681 """
672682 Creates a dict that goes into the events `exception.values` list and is ingestible by Sentry.
673683
@@ -732,10 +742,15 @@ def single_exception_from_error_tuple(
732742 max_value_length = max_value_length ,
733743 custom_repr = custom_repr ,
734744 )
735- for tb in iter_stacks (tb )
745+ for tb , _ in zip ( iter_stacks (tb ), range ( MAX_STACK_FRAMES + 1 ) )
736746 ] # type: List[Dict[str, Any]]
737747
738- if frames :
748+ if len (frames ) > MAX_STACK_FRAMES :
749+ exception_value ["stacktrace" ] = AnnotatedValue .removed_because_over_size_limit (
750+ value = {}
751+ )
752+
753+ elif frames :
739754 if not full_stack :
740755 new_frames = frames
741756 else :
@@ -798,7 +813,7 @@ def exceptions_from_error(
798813 source = None , # type: Optional[str]
799814 full_stack = None , # type: Optional[list[dict[str, Any]]]
800815):
801- # type: (...) -> Tuple[int, List[Dict[str, Any]]]
816+ # type: (...) -> Tuple[int, List[Annotated[ Dict[str, Any] ]]]
802817 """
803818 Creates the list of exceptions.
804819 This can include chained exceptions and exceptions from an ExceptionGroup.
@@ -894,7 +909,7 @@ def exceptions_from_error_tuple(
894909 mechanism = None , # type: Optional[Dict[str, Any]]
895910 full_stack = None , # type: Optional[list[dict[str, Any]]]
896911):
897- # type: (...) -> List[Dict[str, Any]]
912+ # type: (...) -> List[Annotated[ Dict[str, Any] ]]
898913 exc_type , exc_value , tb = exc_info
899914
900915 is_exception_group = BaseExceptionGroup is not None and isinstance (
@@ -941,7 +956,7 @@ def to_string(value):
941956
942957
943958def iter_event_stacktraces (event ):
944- # type: (Event) -> Iterator[Dict[str, Any]]
959+ # type: (Event) -> Iterator[Annotated[ Dict[str, Any] ]]
945960 if "stacktrace" in event :
946961 yield event ["stacktrace" ]
947962 if "threads" in event :
@@ -950,20 +965,26 @@ def iter_event_stacktraces(event):
950965 yield thread ["stacktrace" ]
951966 if "exception" in event :
952967 for exception in event ["exception" ].get ("values" ) or ():
953- if "stacktrace" in exception :
968+ if isinstance ( exception , dict ) and "stacktrace" in exception :
954969 yield exception ["stacktrace" ]
955970
956971
957972def iter_event_frames (event ):
958973 # type: (Event) -> Iterator[Dict[str, Any]]
959974 for stacktrace in iter_event_stacktraces (event ):
975+ if isinstance (stacktrace , AnnotatedValue ):
976+ stacktrace = stacktrace .value or {}
977+
960978 for frame in stacktrace .get ("frames" ) or ():
961979 yield frame
962980
963981
964982def handle_in_app (event , in_app_exclude = None , in_app_include = None , project_root = None ):
965983 # type: (Event, Optional[List[str]], Optional[List[str]], Optional[str]) -> Event
966984 for stacktrace in iter_event_stacktraces (event ):
985+ if isinstance (stacktrace , AnnotatedValue ):
986+ stacktrace = stacktrace .value or {}
987+
967988 set_in_app_in_frames (
968989 stacktrace .get ("frames" ),
969990 in_app_exclude = in_app_exclude ,
0 commit comments