Skip to content

Commit 87ac5cd

Browse files
authoredMar 19, 2025··
ref(grouping): Tweak enhancement naming and documentation (#87363)
This is a bunch of renaming and comment-adding that I've done as part of my travels through the enhancements code, both recently and last fall. No behavior changes.
1 parent 6a4c1ff commit 87ac5cd

File tree

4 files changed

+81
-38
lines changed

4 files changed

+81
-38
lines changed
 

‎src/sentry/grouping/enhancer/__init__.py

+14-14
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,14 @@ def assemble_stacktrace_component(
190190
"""
191191
match_frames = [create_match_frame(frame, platform) for frame in frames]
192192

193-
rust_components = [RustComponent(contributes=c.contributes) for c in frame_components]
193+
rust_frame_components = [RustComponent(contributes=c.contributes) for c in frame_components]
194194

195195
# Modify the rust components by applying +group/-group rules and getting hints for both
196196
# those changes and the `in_app` changes applied by earlier in the ingestion process by
197197
# `apply_category_and_updated_in_app_to_frames`. Also, get `hint` and `contributes` values
198198
# for the overall stacktrace (returned in `rust_results`).
199199
rust_results = self.rust_enhancements.assemble_stacktrace_component(
200-
match_frames, make_rust_exception_data(exception_data), rust_components
200+
match_frames, make_rust_exception_data(exception_data), rust_frame_components
201201
)
202202

203203
# Tally the number of each type of frame in the stacktrace. Later on, this will allow us to
@@ -206,7 +206,7 @@ def assemble_stacktrace_component(
206206
frame_counts: Counter[str] = Counter()
207207

208208
# Update frame components with results from rust
209-
for py_component, rust_component in zip(frame_components, rust_components):
209+
for py_component, rust_component in zip(frame_components, rust_frame_components):
210210
# TODO: Remove the first condition once we get rid of the legacy config
211211
if (
212212
not (self.bases and self.bases[0].startswith("legacy"))
@@ -293,7 +293,7 @@ def _to_config_structure(self):
293293
return [
294294
self.version,
295295
self.bases,
296-
[x._to_config_structure(self.version) for x in self.rules],
296+
[rule._to_config_structure(self.version) for rule in self.rules],
297297
]
298298

299299
def dumps(self) -> str:
@@ -307,7 +307,7 @@ def _from_config_structure(cls, data, rust_enhancements: RustEnhancements) -> En
307307
if version not in VERSIONS:
308308
raise ValueError("Unknown version")
309309
return cls(
310-
rules=[EnhancementRule._from_config_structure(x, version=version) for x in rules],
310+
rules=[EnhancementRule._from_config_structure(rule, version=version) for rule in rules],
311311
rust_enhancements=rust_enhancements,
312312
version=version,
313313
bases=bases,
@@ -348,17 +348,17 @@ def from_config_string(cls, s, bases=None, id=None) -> Enhancements:
348348

349349

350350
def _load_configs() -> dict[str, Enhancements]:
351-
rv = {}
352-
base = os.path.join(os.path.abspath(os.path.dirname(__file__)), "enhancement-configs")
353-
for fn in os.listdir(base):
354-
if fn.endswith(".txt"):
355-
with open(os.path.join(base, fn), encoding="utf-8") as f:
351+
enhancement_bases = {}
352+
configs_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "enhancement-configs")
353+
for filename in os.listdir(configs_dir):
354+
if filename.endswith(".txt"):
355+
with open(os.path.join(configs_dir, filename), encoding="utf-8") as f:
356356
# We cannot use `:` in filenames on Windows but we already have ids with
357357
# `:` in their names hence this trickery.
358-
fn = fn.replace("@", ":")
359-
enhancements = Enhancements.from_config_string(f.read(), id=fn[:-4])
360-
rv[fn[:-4]] = enhancements
361-
return rv
358+
filename = filename.replace("@", ":")
359+
enhancements = Enhancements.from_config_string(f.read(), id=filename[:-4])
360+
enhancement_bases[filename[:-4]] = enhancements
361+
return enhancement_bases
362362

363363

364364
ENHANCEMENT_BASES = _load_configs()

‎src/sentry/grouping/enhancer/actions.py

+23-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99

1010
ACTIONS = ["group", "app"]
1111
ACTION_BITSIZE = 8
12-
assert len(ACTIONS) < 1 << ACTION_BITSIZE
12+
# Ensure that the number of possible actions is smaller than the number of numbers which can be
13+
# represented with `ACTION_BITSIZE` bits
14+
assert len(ACTIONS) < 1 << ACTION_BITSIZE # This is 2^ACTION_BITSIZE
1315
ACTION_FLAGS = {
1416
(True, None): 0,
1517
(True, "up"): 1,
@@ -54,19 +56,26 @@ def is_updater(self) -> bool:
5456

5557
@classmethod
5658
def _from_config_structure(cls, val, version: int):
57-
if isinstance(val, list):
59+
if isinstance(val, list): # This is a `VarAction`
5860
return VarAction(val[0], val[1])
61+
# Otherwise, assume it's a `FlagAction`, since those are the only two types we currently have
5962
flag, range_direction = REVERSE_ACTION_FLAGS[val >> ACTION_BITSIZE]
6063
return FlagAction(ACTIONS[val & 0xF], flag, range_direction)
6164

6265

6366
class FlagAction(EnhancementAction):
67+
"""
68+
An action which sets either a frame's `contributes` value or its `in_app` value.
69+
70+
May optionally set the value for all frames above or below it in the stacktrace as well.
71+
"""
72+
6473
def __init__(self, key: str, flag: bool, range: str | None) -> None:
65-
self.key = key
74+
self.key = key # The type of change (`app` or `group`)
6675
self._is_updater = key in {"group", "app"}
6776
self._is_modifier = key == "app"
68-
self.flag = flag
69-
self.range = range # e.g. None, "up", "down"
77+
self.flag = flag # True for `+app/+group` rules, False for `-app/-group` rules
78+
self.range = range # None (apply the action to this frame), "up", or "down"
7079

7180
def __str__(self) -> str:
7281
return "{}{}{}".format(
@@ -76,6 +85,15 @@ def __str__(self) -> str:
7685
)
7786

7887
def _to_config_structure(self, version: int):
88+
"""
89+
Convert the action into an integer by
90+
- converting the combination of its boolean value (if it's a `+app/+group` rule or a
91+
`-app/-group` rule) and its range (if it applies to this frame, frames above, or
92+
frames below) into a number (see `ACTION_FLAGS`) and then multiplying that number by
93+
2^ACTION_BITSIZE
94+
- converting its type (app or group) into a number (using the index in `ACTIONS`)
95+
- bitwise or-ing those two numbers
96+
"""
7997
return ACTIONS.index(self.key) | (ACTION_FLAGS[self.flag, self.range] << ACTION_BITSIZE)
8098

8199
def _slice_to_range(self, seq, idx):

‎src/sentry/grouping/enhancer/matchers.py

+30-12
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def _cached(cache, function, *args, **kwargs):
6464
"value": "value",
6565
"mechanism": "mechanism",
6666
"category": "category",
67-
# fingerprinting specific fields
67+
# fingerprinting-specific fields
6868
"family": "family",
6969
"app": "app",
7070
}
@@ -112,8 +112,8 @@ def _to_config_structure(self, version):
112112
raise NotImplementedError()
113113

114114
@staticmethod
115-
def _from_config_structure(obj, version):
116-
val = obj
115+
def _from_config_structure(config_structure, version):
116+
val = config_structure
117117
if val.startswith("|[") and val.endswith("]"):
118118
return CalleeMatch(EnhancementMatch._from_config_structure(val[2:-1], version))
119119
if val.startswith("[") and val.endswith("]|"):
@@ -195,18 +195,36 @@ def matches_frame(self, frames, idx, exception_data, cache):
195195
return rv
196196

197197
def _positive_frame_match(self, match_frame, exception_data, cache):
198-
# Implement is subclasses
198+
# Implement in subclasses
199199
raise NotImplementedError
200200

201201
def _to_config_structure(self, version):
202+
"""
203+
Convert the matcher into a string of the form
204+
<match_type><match_pattern>
205+
where
206+
match_type is a single letter code for the match type (see MATCH_KEYS)
207+
match_pattern is the value to match against
208+
209+
This will be preceded by a `!` if the match is negated. Families against which to match are
210+
also converted to single-letter abbreviations, and in-app booleans are converted to 0 or 1.
211+
"""
212+
# Convert the families to match into a string of single letter abbreviations (so
213+
# `javascript,native` becomes `JN`, for example)
202214
if self.key == "family":
203-
arg = "".join(_f for _f in [FAMILIES.get(x) for x in self.pattern.split(",")] if _f)
215+
value_to_match = "".join(
216+
abbreviation
217+
for abbreviation in [FAMILIES.get(family) for family in self.pattern.split(",")]
218+
if abbreviation
219+
)
204220
elif self.key == "app":
205221
boolified_pattern = bool_from_string(self.pattern)
206-
arg = "1" if boolified_pattern is True else "0" if boolified_pattern is False else ""
222+
value_to_match = (
223+
"1" if boolified_pattern is True else "0" if boolified_pattern is False else ""
224+
)
207225
else:
208-
arg = self.pattern
209-
return ("!" if self.negated else "") + MATCH_KEYS[self.key] + arg
226+
value_to_match = self.pattern
227+
return ("!" if self.negated else "") + MATCH_KEYS[self.key] + value_to_match
210228

211229

212230
def path_like_match(pattern, value):
@@ -268,13 +286,13 @@ def _positive_frame_match(self, match_frame, exception_data, cache):
268286

269287
class FrameFieldMatch(FrameMatch):
270288
def _positive_frame_match(self, match_frame, exception_data, cache):
271-
field = match_frame[self.field]
272-
if field is None:
289+
value = match_frame[self.field]
290+
if value is None:
273291
return False
274-
if field == self._encoded_pattern:
292+
if value == self._encoded_pattern:
275293
return True
276294

277-
return _cached(cache, glob_match, field, self._encoded_pattern)
295+
return _cached(cache, glob_match, value, self._encoded_pattern)
278296

279297

280298
class FunctionMatch(FrameFieldMatch):

‎src/sentry/grouping/enhancer/rules.py

+14-7
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def __init__(self, matchers, actions):
2424

2525
@property
2626
def matcher_description(self):
27-
rv = " ".join(x.description for x in self.matchers)
27+
rv = " ".join(matcher.description for matcher in self.matchers)
2828
for action in self.actions:
2929
rv = f"{rv} {action}"
3030
return rv
@@ -47,7 +47,7 @@ def as_dict(self):
4747
matchers = {}
4848
for matcher in self.matchers:
4949
matchers[matcher.key] = matcher.pattern
50-
return {"match": matchers, "actions": [str(x) for x in self.actions]}
50+
return {"match": matchers, "actions": [str(action) for action in self.actions]}
5151

5252
def get_matching_frame_actions(
5353
self,
@@ -81,13 +81,20 @@ def get_matching_frame_actions(
8181

8282
def _to_config_structure(self, version):
8383
return [
84-
[x._to_config_structure(version) for x in self.matchers],
85-
[x._to_config_structure(version) for x in self.actions],
84+
[matcher._to_config_structure(version) for matcher in self.matchers],
85+
[action._to_config_structure(version) for action in self.actions],
8686
]
8787

8888
@classmethod
89-
def _from_config_structure(cls, tuple, version):
89+
def _from_config_structure(cls, config_structure, version):
90+
matcher_abbreviations, encoded_actions = config_structure
9091
return EnhancementRule(
91-
[EnhancementMatch._from_config_structure(x, version) for x in tuple[0]],
92-
[EnhancementAction._from_config_structure(x, version) for x in tuple[1]],
92+
[
93+
EnhancementMatch._from_config_structure(matcher, version)
94+
for matcher in matcher_abbreviations
95+
],
96+
[
97+
EnhancementAction._from_config_structure(action, version)
98+
for action in encoded_actions
99+
],
93100
)

0 commit comments

Comments
 (0)
Please sign in to comment.