Skip to content

Commit f0949c7

Browse files
authored
ref(grouping): Simplify handling of variant name and exception data (#97458)
During grouping, we pass data from one component strategy to another by storing it in the grouping context. In that context, two values - the variant name and a blob of exception data - are only sometimes defined. Until recently, the `GroupingContext` class supported key-lookup using subscript notation (`context[some_key]`) the same way a dictionary would, but lacked the safer `.get()` method. We therefore currently initialize both values to `None`, to prevent raising a `KeyError` if the values haven't been set. Now that `.get()` has been implemented, though, we don't need to do that. This PR therefore switches the way we access them to use `.get()`, and removes the default initialization. In order to enforce when `variant_name` is or isn't expected to be defined, it also extends the practice already followed in some strategies of asserting on its existence (or lack thereof). This ensures that if we ever make changes to the grouping code, we'll know right away if our assumptions have been violated.
1 parent 281f58c commit f0949c7

File tree

6 files changed

+31
-19
lines changed

6 files changed

+31
-19
lines changed

src/sentry/grouping/strategies/base.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ def __init__(self, strategy_config: StrategyConfiguration, event: Event):
117117
self.config = strategy_config
118118
self.event = event
119119
self._push_context_layer()
120-
self["variant_name"] = None
121120

122121
def __setitem__(self, key: str, value: ContextValue) -> None:
123122
# Add the key-value pair to the context layer at the top of the stack
@@ -182,12 +181,15 @@ def get_single_grouping_component(
182181
Invoke the delegate grouping strategy corresponding to the given interface, returning the
183182
grouping component for the variant set on the context.
184183
"""
184+
variant_name = self["variant_name"]
185+
assert variant_name is not None
186+
185187
components_by_variant = self._get_grouping_components_for_interface(
186188
interface, event=event, **kwargs
187189
)
188190

189191
assert len(components_by_variant) == 1
190-
return components_by_variant[self["variant_name"]]
192+
return components_by_variant[variant_name]
191193

192194
def _get_grouping_components_for_interface(
193195
self, interface: Interface, *, event: Event, **kwargs: Any
@@ -484,7 +486,7 @@ def call_with_variants(
484486
**kwargs: Any,
485487
) -> ReturnedVariants:
486488
context = kwargs["context"]
487-
incoming_variant_name = context["variant_name"]
489+
incoming_variant_name = context.get("variant_name")
488490

489491
if incoming_variant_name is not None:
490492
# For the case where the variant is already determined, we act as a delegate strategy. To

src/sentry/grouping/strategies/configurations.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@
2727
],
2828
delegates=["frame:v1", "stacktrace:v1", "single-exception:v1"],
2929
initial_context={
30-
# This key in the context tells the system which variant should
31-
# be produced. TODO: phase this out.
32-
"variant_name": None,
3330
# This is a flag that can be used by any delegate to respond to
3431
# a detected recursion. This is currently used by the frame
3532
# strategy to disable itself. Recursion is detected by the outer
@@ -40,8 +37,6 @@
4037
"normalize_message": True,
4138
# Platforms for which context line should be taken into account when grouping.
4239
"contextline_platforms": ("javascript", "node", "python", "php", "ruby"),
43-
# Stacktrace is produced in the context of this exception
44-
"exception_data": None,
4540
},
4641
)
4742

src/sentry/grouping/strategies/message.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,17 @@ def normalize_message_for_grouping(message: str, event: Event) -> str:
7272
def message_v1(
7373
interface: Message, event: Event, context: GroupingContext, **kwargs: Any
7474
) -> ReturnedVariants:
75+
variant_name = context["variant_name"]
76+
7577
# This is true for all but our test config
7678
if context["normalize_message"]:
7779
raw_message = interface.message or interface.formatted or ""
7880
normalized = normalize_message_for_grouping(raw_message, event)
7981
hint = "stripped event-specific values" if raw_message != normalized else None
80-
return {context["variant_name"]: MessageGroupingComponent(values=[normalized], hint=hint)}
82+
return {variant_name: MessageGroupingComponent(values=[normalized], hint=hint)}
8183
else:
8284
return {
83-
context["variant_name"]: MessageGroupingComponent(
85+
variant_name: MessageGroupingComponent(
8486
values=[interface.message or interface.formatted or ""],
8587
)
8688
}

src/sentry/grouping/strategies/newstyle.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ def frame(
300300
) -> ReturnedVariants:
301301
frame = interface
302302
platform = frame.platform or event.platform
303+
variant_name = context["variant_name"]
304+
assert variant_name is not None
303305

304306
# Safari throws [native code] frames in for calls like ``forEach``
305307
# whereas Chrome ignores these. Let's remove it from the hashing algo
@@ -371,7 +373,7 @@ def frame(
371373
if context["is_recursion"]:
372374
frame_component.update(contributes=False, hint="ignored due to recursion")
373375

374-
return {context["variant_name"]: frame_component}
376+
return {variant_name: frame_component}
375377

376378

377379
def get_contextline_component(
@@ -409,7 +411,7 @@ def get_contextline_component(
409411
def stacktrace(
410412
interface: Stacktrace, event: Event, context: GroupingContext, **kwargs: Any
411413
) -> ReturnedVariants:
412-
assert context["variant_name"] is None
414+
assert context.get("variant_name") is None
413415

414416
return call_with_variants(
415417
_single_stacktrace_variant,
@@ -425,6 +427,7 @@ def _single_stacktrace_variant(
425427
stacktrace: Stacktrace, event: Event, context: GroupingContext, kwargs: dict[str, Any]
426428
) -> ReturnedVariants:
427429
variant_name = context["variant_name"]
430+
assert variant_name is not None
428431

429432
frames = stacktrace.frames
430433

@@ -467,7 +470,7 @@ def _single_stacktrace_variant(
467470
frame_components,
468471
raw_frames,
469472
event.platform,
470-
exception_data=context["exception_data"],
473+
exception_data=context.get("exception_data"),
471474
)
472475

473476
# This context value is set by the grouping info endpoint, so that the frame order of the

src/sentry/grouping/strategies/security.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@
2929
def expect_ct_v1(
3030
interface: ExpectCT, event: Event, context: GroupingContext, **kwargs: Any
3131
) -> ReturnedVariants:
32+
variant_name = context["variant_name"]
33+
3234
return {
33-
context["variant_name"]: ExpectCTGroupingComponent(
35+
variant_name: ExpectCTGroupingComponent(
3436
values=[
3537
SaltGroupingComponent(values=["expect-ct"]),
3638
HostnameGroupingComponent(values=[interface.hostname]),
@@ -44,8 +46,10 @@ def expect_ct_v1(
4446
def expect_staple_v1(
4547
interface: ExpectStaple, event: Event, context: GroupingContext, **kwargs: Any
4648
) -> ReturnedVariants:
49+
variant_name = context["variant_name"]
50+
4751
return {
48-
context["variant_name"]: ExpectStapleGroupingComponent(
52+
variant_name: ExpectStapleGroupingComponent(
4953
values=[
5054
SaltGroupingComponent(values=["expect-staple"]),
5155
HostnameGroupingComponent(values=[interface.hostname]),
@@ -59,8 +63,10 @@ def expect_staple_v1(
5963
def hpkp_v1(
6064
interface: Hpkp, event: Event, context: GroupingContext, **kwargs: Any
6165
) -> ReturnedVariants:
66+
variant_name = context["variant_name"]
67+
6268
return {
63-
context["variant_name"]: HPKPGroupingComponent(
69+
variant_name: HPKPGroupingComponent(
6470
values=[
6571
SaltGroupingComponent(values=["hpkp"]),
6672
HostnameGroupingComponent(values=[interface.hostname]),
@@ -74,6 +80,8 @@ def hpkp_v1(
7480
def csp_v1(
7581
interface: Csp, event: Event, context: GroupingContext, **kwargs: Any
7682
) -> ReturnedVariants:
83+
variant_name = context["variant_name"]
84+
7785
violation_component = ViolationGroupingComponent()
7886
uri_component = URIGroupingComponent()
7987

@@ -89,7 +97,7 @@ def csp_v1(
8997
uri_component.update(values=[interface.normalized_blocked_uri])
9098

9199
return {
92-
context["variant_name"]: CSPGroupingComponent(
100+
variant_name: CSPGroupingComponent(
93101
values=[
94102
SaltGroupingComponent(values=[interface.effective_directive]),
95103
violation_component,

src/sentry/grouping/strategies/template.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
def template_v1(
2525
interface: Template, event: Event, context: GroupingContext, **kwargs: Any
2626
) -> ReturnedVariants:
27+
variant_name = context["variant_name"]
28+
2729
filename_component = FilenameGroupingComponent()
2830
if interface.filename is not None:
2931
filename_component.update(values=[interface.filename])
@@ -33,7 +35,7 @@ def template_v1(
3335
context_line_component.update(values=[interface.context_line])
3436

3537
return {
36-
context["variant_name"]: TemplateGroupingComponent(
37-
values=[filename_component, context_line_component]
38+
variant_name: TemplateGroupingComponent(
39+
values=[filename_component, context_line_component],
3840
)
3941
}

0 commit comments

Comments
 (0)