Skip to content

Commit

Permalink
s/GroupingComponent/BaseGroupingComponent
Browse files Browse the repository at this point in the history
  • Loading branch information
lobsterkatie committed Nov 14, 2024
1 parent f6af21c commit 08d64ab
Show file tree
Hide file tree
Showing 13 changed files with 93 additions and 91 deletions.
2 changes: 1 addition & 1 deletion src/sentry/grouping/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
Here an example of how components can be used::
function_name = 'lambda$1234'
threads = GroupingComponent(
threads = BaseGroupingComponent(
id="function",
values=[function_name],
contributes=False,
Expand Down
8 changes: 4 additions & 4 deletions src/sentry/grouping/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import sentry_sdk

from sentry import options
from sentry.grouping.component import GroupingComponent
from sentry.grouping.component import BaseGroupingComponent
from sentry.grouping.enhancer import LATEST_VERSION, Enhancements
from sentry.grouping.enhancer.exceptions import InvalidEnhancerConfig
from sentry.grouping.strategies.base import DEFAULT_GROUPING_ENHANCEMENTS_BASE, GroupingContext
Expand Down Expand Up @@ -264,10 +264,10 @@ def apply_server_fingerprinting(event, config, allow_custom_title=True):

def _get_calculated_grouping_variants_for_event(
event: Event, context: GroupingContext
) -> dict[str, GroupingComponent]:
) -> dict[str, BaseGroupingComponent]:
winning_strategy: str | None = None
precedence_hint: str | None = None
per_variant_components: dict[str, list[GroupingComponent]] = {}
per_variant_components: dict[str, list[BaseGroupingComponent]] = {}

for strategy in context.config.iter_strategies():
# Defined in src/sentry/grouping/strategies/base.py
Expand All @@ -292,7 +292,7 @@ def _get_calculated_grouping_variants_for_event(

rv = {}
for variant, components in per_variant_components.items():
component = GroupingComponent(id=variant, values=components)
component = BaseGroupingComponent(id=variant, values=components)
if not component.contributes and precedence_hint:
component.update(hint=precedence_hint)
rv[variant] = component
Expand Down
32 changes: 17 additions & 15 deletions src/sentry/grouping/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,29 @@
}


def _calculate_contributes(values: Sequence[str | int | GroupingComponent]) -> bool:
def _calculate_contributes(values: Sequence[str | int | BaseGroupingComponent]) -> bool:
for value in values or ():
if not isinstance(value, GroupingComponent) or value.contributes:
if not isinstance(value, BaseGroupingComponent) or value.contributes:
return True
return False


class GroupingComponent:
class BaseGroupingComponent:
"""A grouping component is a recursive structure that is flattened
into components to make a hash for grouping purposes.
"""

id: str = "default"
hint: str | None = None
contributes: bool = False
values: Sequence[str | int | GroupingComponent]
values: Sequence[str | int | BaseGroupingComponent]

def __init__(
self,
id: str,
hint: str | None = None,
contributes: bool | None = None,
values: Sequence[str | int | GroupingComponent] | None = None,
values: Sequence[str | int | BaseGroupingComponent] | None = None,
variant_provider: bool = False,
):
self.id = id
Expand All @@ -72,13 +72,15 @@ def description(self) -> str:
# Keep track of the paths we walk so later we can pick the longest one
paths = []

def _walk_components(component: GroupingComponent, current_path: list[str | None]) -> None:
def _walk_components(
component: BaseGroupingComponent, current_path: list[str | None]
) -> None:
# Keep track of the names of the nodes from the root of the component tree to here
current_path.append(component.name)

# Walk the tree, looking for contributing components.
for value in component.values:
if isinstance(value, GroupingComponent) and value.contributes:
if isinstance(value, BaseGroupingComponent) and value.contributes:
_walk_components(value, current_path)

# Filter out the `None`s (which come from components not in `KNOWN_MAJOR_COMPONENT_NAMES`)
Expand All @@ -99,16 +101,16 @@ def _walk_components(component: GroupingComponent, current_path: list[str | None

def get_subcomponent(
self, id: str, only_contributing: bool = False
) -> str | int | GroupingComponent | None:
) -> str | int | BaseGroupingComponent | None:
"""Looks up a subcomponent by the id and returns the first or `None`."""
return next(self.iter_subcomponents(id=id, only_contributing=only_contributing), None)

def iter_subcomponents(
self, id: str, recursive: bool = False, only_contributing: bool = False
) -> Iterator[str | int | GroupingComponent | None]:
) -> Iterator[str | int | BaseGroupingComponent | None]:
"""Finds all subcomponents matching an id, optionally recursively."""
for value in self.values:
if isinstance(value, GroupingComponent):
if isinstance(value, BaseGroupingComponent):
if only_contributing and not value.contributes:
continue
if value.id == id:
Expand All @@ -123,7 +125,7 @@ def update(
self,
hint: str | None = None,
contributes: bool | None = None,
values: Sequence[str | int | GroupingComponent] | None = None,
values: Sequence[str | int | BaseGroupingComponent] | None = None,
) -> None:
"""Updates an already existing component with new values."""
if hint is not None:
Expand All @@ -135,20 +137,20 @@ def update(
if contributes is not None:
self.contributes = contributes

def shallow_copy(self) -> GroupingComponent:
def shallow_copy(self) -> BaseGroupingComponent:
"""Creates a shallow copy."""
rv = object.__new__(self.__class__)
rv.__dict__.update(self.__dict__)
rv.values = list(self.values)
return rv

def iter_values(self) -> Generator[str | int | GroupingComponent]:
def iter_values(self) -> Generator[str | int | BaseGroupingComponent]:
"""Recursively walks the component and flattens it into a list of
values.
"""
if self.contributes:
for value in self.values:
if isinstance(value, GroupingComponent):
if isinstance(value, BaseGroupingComponent):
yield from value.iter_values()
else:
yield value
Expand All @@ -170,7 +172,7 @@ def as_dict(self) -> dict[str, Any]:
"values": [],
}
for value in self.values:
if isinstance(value, GroupingComponent):
if isinstance(value, BaseGroupingComponent):
rv["values"].append(value.as_dict())
else:
# This basically assumes that a value is only a primitive
Expand Down
8 changes: 4 additions & 4 deletions src/sentry/grouping/enhancer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from sentry_ophio.enhancers import Enhancements as RustEnhancements

from sentry import projectoptions
from sentry.grouping.component import GroupingComponent
from sentry.grouping.component import BaseGroupingComponent
from sentry.stacktraces.functions import set_in_app
from sentry.utils.safe import get_path, set_path

Expand Down Expand Up @@ -164,11 +164,11 @@ def apply_modifications_to_frame(

def assemble_stacktrace_component(
self,
components: list[GroupingComponent],
components: list[BaseGroupingComponent],
frames: list[dict[str, Any]],
platform: str | None,
exception_data: dict[str, Any] | None = None,
) -> tuple[GroupingComponent, bool]:
) -> tuple[BaseGroupingComponent, bool]:
"""
This assembles a `stacktrace` grouping component out of the given
`frame` components and source frames.
Expand All @@ -186,7 +186,7 @@ def assemble_stacktrace_component(
for py_component, rust_component in zip(components, rust_components):
py_component.update(contributes=rust_component.contributes, hint=rust_component.hint)

component = GroupingComponent(
component = BaseGroupingComponent(
id="stacktrace",
values=components,
hint=rust_results.hint,
Expand Down
8 changes: 4 additions & 4 deletions src/sentry/grouping/strategies/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from sentry import projectoptions
from sentry.eventstore.models import Event
from sentry.grouping.component import GroupingComponent
from sentry.grouping.component import BaseGroupingComponent
from sentry.grouping.enhancer import Enhancements
from sentry.interfaces.base import Interface

Expand All @@ -24,7 +24,7 @@
DEFAULT_GROUPING_ENHANCEMENTS_BASE = "common:2019-03-23"
DEFAULT_GROUPING_FINGERPRINTING_BASES: list[str] = []

ReturnedVariants = dict[str, GroupingComponent]
ReturnedVariants = dict[str, BaseGroupingComponent]
ConcreteInterface = TypeVar("ConcreteInterface", bound=Interface, contravariant=True)


Expand Down Expand Up @@ -117,7 +117,7 @@ def get_grouping_component(

def get_single_grouping_component(
self, interface: Interface, *, event: Event, **kwargs: Any
) -> GroupingComponent:
) -> BaseGroupingComponent:
"""Invokes a delegate grouping strategy. If no such delegate is
configured a fallback grouping component is returned.
"""
Expand Down Expand Up @@ -193,7 +193,7 @@ def variant_processor(self, func: VariantProcessor) -> VariantProcessor:

def get_grouping_component(
self, event: Event, context: GroupingContext, variant: str | None = None
) -> None | GroupingComponent | ReturnedVariants:
) -> None | BaseGroupingComponent | ReturnedVariants:
"""Given a specific variant this calculates the grouping component."""
args = []
iface = event.interfaces.get(self.interface)
Expand Down
32 changes: 16 additions & 16 deletions src/sentry/grouping/strategies/legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Any

from sentry.eventstore.models import Event
from sentry.grouping.component import GroupingComponent
from sentry.grouping.component import BaseGroupingComponent
from sentry.grouping.strategies.base import (
GroupingContext,
ReturnedVariants,
Expand Down Expand Up @@ -164,17 +164,17 @@ def single_exception_legacy(
interface: SingleException, event: Event, context: GroupingContext, **meta: Any
) -> ReturnedVariants:

type_component = GroupingComponent(
type_component = BaseGroupingComponent(
id="type",
values=[interface.type] if interface.type else [],
contributes=False,
)
value_component = GroupingComponent(
value_component = BaseGroupingComponent(
id="value",
values=[interface.value] if interface.value else [],
contributes=False,
)
stacktrace_component = GroupingComponent(id="stacktrace")
stacktrace_component = BaseGroupingComponent(id="stacktrace")

if interface.stacktrace is not None:
stacktrace_component = context.get_single_grouping_component(
Expand All @@ -195,7 +195,7 @@ def single_exception_legacy(
value_component.update(contributes=True)

return {
context["variant"]: GroupingComponent(
context["variant"]: BaseGroupingComponent(
id="exception", values=[stacktrace_component, type_component, value_component]
)
}
Expand Down Expand Up @@ -231,7 +231,7 @@ def chained_exception_legacy(
if stacktrace_component is None or not stacktrace_component.contributes:
value.update(contributes=False, hint="exception has no stacktrace")

return {context["variant"]: GroupingComponent(id="chained-exception", values=values)}
return {context["variant"]: BaseGroupingComponent(id="chained-exception", values=values)}


@chained_exception_legacy.variant_processor
Expand Down Expand Up @@ -262,7 +262,7 @@ def frame_legacy(
# Safari throws [native code] frames in for calls like ``forEach``
# whereas Chrome ignores these. Let's remove it from the hashing algo
# so that they're more likely to group together
filename_component = GroupingComponent(id="filename")
filename_component = BaseGroupingComponent(id="filename")
if interface.filename == "<anonymous>":
filename_component.update(
contributes=False, values=[interface.filename], hint="anonymous filename discarded"
Expand Down Expand Up @@ -293,7 +293,7 @@ def frame_legacy(
# if we have a module we use that for grouping. This will always
# take precedence over the filename, even if the module is
# considered unhashable.
module_component = GroupingComponent(id="module")
module_component = BaseGroupingComponent(id="module")
if interface.module:
if is_unhashable_module_legacy(interface, platform):
module_component.update(values=["<module>"], hint="normalized generated module name")
Expand All @@ -306,7 +306,7 @@ def frame_legacy(
)

# Context line when available is the primary contributor
context_line_component = GroupingComponent(id="context-line")
context_line_component = BaseGroupingComponent(id="context-line")
if interface.context_line is not None:
if len(interface.context_line) > 120:
context_line_component.update(hint="discarded because line too long")
Expand All @@ -315,9 +315,9 @@ def frame_legacy(
else:
context_line_component.update(values=[interface.context_line])

symbol_component = GroupingComponent(id="symbol")
function_component = GroupingComponent(id="function")
lineno_component = GroupingComponent(id="lineno")
symbol_component = BaseGroupingComponent(id="symbol")
function_component = BaseGroupingComponent(id="function")
lineno_component = BaseGroupingComponent(id="lineno")

# The context line grouping information is the most reliable one.
# If we did not manage to find some information there, we want to
Expand Down Expand Up @@ -369,7 +369,7 @@ def frame_legacy(
)

return {
context["variant"]: GroupingComponent(
context["variant"]: BaseGroupingComponent(
id="frame",
values=[
module_component,
Expand Down Expand Up @@ -450,7 +450,7 @@ def threads_legacy(
thread_count = len(interface.values)
if thread_count != 1:
return {
context["variant"]: GroupingComponent(
context["variant"]: BaseGroupingComponent(
id="threads",
contributes=False,
hint="ignored because contains %d threads" % thread_count,
Expand All @@ -460,13 +460,13 @@ def threads_legacy(
stacktrace: Stacktrace = interface.values[0].get("stacktrace")
if not stacktrace:
return {
context["variant"]: GroupingComponent(
context["variant"]: BaseGroupingComponent(
id="threads", contributes=False, hint="thread has no stacktrace"
)
}

return {
context["variant"]: GroupingComponent(
context["variant"]: BaseGroupingComponent(
id="threads",
values=[context.get_single_grouping_component(stacktrace, event=event, **meta)],
)
Expand Down
6 changes: 3 additions & 3 deletions src/sentry/grouping/strategies/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from sentry import analytics
from sentry.eventstore.models import Event
from sentry.features.rollout import in_rollout_group
from sentry.grouping.component import GroupingComponent
from sentry.grouping.component import BaseGroupingComponent
from sentry.grouping.parameterization import Parameterizer, UniqueIdExperiment
from sentry.grouping.strategies.base import (
GroupingContext,
Expand Down Expand Up @@ -109,15 +109,15 @@ def message_v1(
normalized = normalize_message_for_grouping(raw, event)
hint = "stripped event-specific values" if raw != normalized else None
return {
context["variant"]: GroupingComponent(
context["variant"]: BaseGroupingComponent(
id="message",
values=[normalized],
hint=hint,
)
}
else:
return {
context["variant"]: GroupingComponent(
context["variant"]: BaseGroupingComponent(
id="message",
values=[interface.message or interface.formatted or ""],
)
Expand Down
Loading

0 comments on commit 08d64ab

Please sign in to comment.