ref: Introduce inline type check for whether a span is StreamedSpan#6180
ref: Introduce inline type check for whether a span is StreamedSpan#6180ericapisani merged 5 commits intomasterfrom
Conversation
…ed check The `isinstance(span, StreamedSpan) and not isinstance(span, NoOpStreamedSpan)` pattern was scattered across the codebase and easy to get wrong — forgetting the `NoOpStreamedSpan` guard is a recurring code review catch. Extract it into a single `_is_sampled_streamed_span()` helper in `traces.py` with a `TypeGuard[StreamedSpan]` return type for proper type narrowing, and replace all instances of the combined check in `scope.py`, `starlette.py`, and `fastapi.py`.
|
bugbot run |
|
@sentry review |
Codecov Results 📊✅ 13 passed | Total: 13 | Pass Rate: 100% | Execution Time: 8.90s All tests are passing successfully. ❌ Patch coverage is 75.00%. Project has 15002 uncovered lines. Files with missing lines (4)
Generated by Codecov Action |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 10e96aa. Configure here.
alexander-alderman-webb
left a comment
There was a problem hiding this comment.
It looks like in blocks gated by not isinstance(current_span, NoOpStreamedSpan) we rely on private attributes and functions of StreamedSpan such as _segment and _is_segment().
If we're relying on private details that we expect to only exist on StreamedSpan (and not subclasses) it would be more appropriate to replace logic like
isinstance(span, StreamedSpan) and not isinstance(span, NoOpStreamedSpan)with
type(span) is StreamedSpanThere was a problem hiding this comment.
Wondering if it'd be better to replace
isinstance(span, StreamedSpan) and not isinstance(span, NoOpStreamedSpan)with
isinstance(span, StreamedSpan) and span.sampledBut I assume that might cause mypy to complain about nonexistent methods on NoOpStreamedSpans since it won't be able to realize the condition effectively means it can't be a NoOpStreamedSpan?
Edit: Only saw Alex's comment above now. That also sounds good.
|
Re: using the I think it depends on how strict we want to be with ensuring that the only spans that pass this are I wrote this check with Re: using the
We would achieve the same outcome using the |
The spans touched by the filtering logic are created by the SDK, so we know that they are of type |
|
@alexander-alderman-webb @sentrivana Made the change in 203c816 |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 203c816. Configure here.
_is_sampled_streamed_span() helper to replace repeated check_is_streamed_span() helper to replace repeated check
| if isinstance(current_span, StreamedSpan) and not isinstance( | ||
| current_span, NoOpStreamedSpan | ||
| ): | ||
| if _is_streamed_span(current_span): |
There was a problem hiding this comment.
Let's inline the method now since it's only one condition. Removes one indirection.
| if _is_streamed_span(current_span): | |
| if type(span) is StreamedSpan: |
| and isinstance(current_span, StreamedSpan) | ||
| and not isinstance(current_span, NoOpStreamedSpan) | ||
| ): | ||
| if info and "data" in info and _is_streamed_span(current_span): |
There was a problem hiding this comment.
| if info and "data" in info and _is_streamed_span(current_span): | |
| if info and "data" in info and type(current_span) is StreamedSpan: |
| if isinstance(current_span, StreamedSpan) and not isinstance( | ||
| current_span, NoOpStreamedSpan | ||
| ): | ||
| if _is_streamed_span(current_span): |
There was a problem hiding this comment.
| if _is_streamed_span(current_span): | |
| if type(current_span) is StreamedSpan: |
| and self.streamed_span is not None | ||
| and not isinstance(self.streamed_span, NoOpStreamedSpan) | ||
| ): | ||
| if span_streaming and _is_streamed_span(self.streamed_span): |
There was a problem hiding this comment.
| if span_streaming and _is_streamed_span(self.streamed_span): | |
| if type(self.streamed_span) is StreamedSpan: |
| and self.streamed_span is not None | ||
| and not isinstance(self.streamed_span, NoOpStreamedSpan) | ||
| ): | ||
| if span_streaming and _is_streamed_span(self.streamed_span): |
There was a problem hiding this comment.
| if span_streaming and _is_streamed_span(self.streamed_span): | |
| if type(self.streamed_span) is StreamedSpan: |
| and not isinstance(span, NoOpStreamedSpan) | ||
| and span._is_segment() | ||
| ): | ||
| if _is_streamed_span(span) and span._is_segment(): |
There was a problem hiding this comment.
| if _is_streamed_span(span) and span._is_segment(): | |
| if type(self.streamed_span) is StreamedSpan: |
…ry/sentry-python into create-sampled-span-helper-0doa2
alexander-alderman-webb
left a comment
There was a problem hiding this comment.
Code LGTM! Change the title when you merge 🙏
_is_streamed_span() helper to replace repeated check
The
isinstance(span, StreamedSpan) and not isinstance(span, NoOpStreamedSpan)pattern was repeated in multiple places. Omitting theNoOpStreamedSpanguard is a frequent code review catch — it silently treats unsampled spans as sampled.Introduce
_is_streamed_span()intraces.pywith aTypeGuard[StreamedSpan]return type for proper type narrowing, and replace all combined-check instances inscope.py,starlette.py, andfastapi.py.Locations where
_spancan also be a regularSpan(not justStreamedSpan) were intentionally left unchanged, as the semantics differ there.Fixes PY-2397
Fixes #6182