Next Python SDK major - #5005
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #5005 +/- ##
===========================================
+ Coverage 70.55% 83.76% +13.21%
===========================================
Files 180 180
Lines 18077 18080 +3
Branches 3008 3009 +1
===========================================
+ Hits 12754 15145 +2391
+ Misses 4432 1943 -2489
- Partials 891 992 +101
|
Codecov Results 📊✅ 88805 passed | ⏭️ 4112 skipped | Total: 92917 | Pass Rate: 95.57% | Execution Time: 282m 37s 📊 Comparison with Base Branch
All tests are passing successfully. ✅ Patch coverage is 90.30%. Project has 2274 uncovered lines. Coverage diff@@ Coverage Diff @@
## master #PR +/-##
==========================================
+ Coverage 90.25% 90.30% +0.05%
==========================================
Files 193 185 -8
Lines 25724 23437 -2287
Branches 9504 8588 -916
==========================================
+ Hits 23214 21163 -2051
- Misses 2510 2274 -236
- Partials 1435 1361 -74Generated by Codecov Action |
Semver Impact of This PR⚪ None (no version bump detected) 📋 Changelog PreviewThis is how your changes will appear in the changelog. New Features ✨
Bug Fixes 🐛Anthropic
Documentation 📚
Internal Changes 🔧
Other
🤖 This preview updates automatically when you update the PR. |
Add `UnraisablehookIntegration` to the default integrations list.
Stop raising exceptions `from None` in the ASGI and asyncio integrations. Closes #5624
Remove everything hub related, including all sorts of compatibility shims around hubs/scopes. Also remove deprecated session methods. `configure_scope` and `push_scope` removal coming in a future PR. #### Issues Closes #5001
The integration requires additional configuration which should be intentional on the user's part. #### Issues Closes #4993
- Remove everything in `integrations/opentelemetry` (`SentrySpanProcessor`, `SentryPropagator`, etc.) - Remove associated test files and CI config - Move old propagator functions and consts that we were using in `OTLPIntegration` to the OTLP propagator directly - Remove `instrumenter` Note: `NoOpSpan` was not removed because it makes mypy blow up. Not worth the effort as we'll anyway get rid of it when dropping transaction based tracing. #### Issues Closes #6932
### Description The API is deprecated and slated for removal in 3.0. #### Issues Closes #5019 #### Reminders - Please add tests to validate your changes, and lint your code using `uv run ruff`. - Add GH Issue ID _&_ Linear ID (if applicable) - PR title should use [conventional commit](https://develop.sentry.dev/engineering-practices/commit-messages/#type) style (`feat:`, `fix:`, `ref:`, `meta:`) - For external contributors: [CONTRIBUTING.md](https://github.com/getsentry/sentry-python/blob/master/CONTRIBUTING.md), [Sentry SDK development docs](https://develop.sentry.dev/sdk/), [Discord community](https://discord.gg/Ww9hbqr)
### Description Remove the deprecated API. #### Issues Closes #5018
### Description Most of the entries in our extras list serve as a way to communicate/enforce the lower boundary of the respective framework that we support. This creates a parallel system to the version checks we already have in each integration. Some extras, however, define extra dependencies or specific extras that are required for an integration to work correctly (e.g. the Flask integration needs `blinker` to work properly). In that case, keep the extra. #### Issues Closes #6259
### Description Also converted tests that were assuming `trace_lifecycle="static"`, and dropped transaction-specific tests that are not transferable to span streaming. #### Issues Closes https://linear.app/getsentry/issue/PY-2692/remove-transaction-based-tracing-from-fastapi #### Reminders - Please add tests to validate your changes, and lint your code using `uv run ruff`. - Add GH Issue ID _&_ Linear ID (if applicable) - PR title should use [conventional commit](https://develop.sentry.dev/engineering-practices/commit-messages/#type) style (`feat:`, `fix:`, `ref:`, `meta:`) - For external contributors: [CONTRIBUTING.md](https://github.com/getsentry/sentry-python/blob/master/CONTRIBUTING.md), [Sentry SDK development docs](https://develop.sentry.dev/sdk/), [Discord community](https://discord.gg/Ww9hbqr)
### Description <!-- What changed and why? --> #### Issues Closes https://linear.app/getsentry/issue/PY-2719/remove-transaction-based-tracing-from-starlette #### Reminders - Please add tests to validate your changes, and lint your code using `uv run ruff`. - Add GH Issue ID _&_ Linear ID (if applicable) - PR title should use [conventional commit](https://develop.sentry.dev/engineering-practices/commit-messages/#type) style (`feat:`, `fix:`, `ref:`, `meta:`) - For external contributors: [CONTRIBUTING.md](https://github.com/getsentry/sentry-python/blob/master/CONTRIBUTING.md), [Sentry SDK development docs](https://develop.sentry.dev/sdk/), [Discord community](https://discord.gg/Ww9hbqr)
Closes https://linear.app/getsentry/issue/PY-2697/remove-transaction-based-tracing-from-httpx The diff is so huge because we were duplicating all tracing tests.
The threading integration itself doesn't have any streaming branching, which is probably why these two threading tests that have to do with tracing got overlooked. In streaming, the behavior in the `propagate_scope=False` case has changed -- all spans will be emitted. Previously, some of the spans wouldn't be emitted because they would be orphaned child spans.
Remove `SanicIntegration.unsampled_statuses` that was no-op in span streaming. Also remove another unused non-span-streaming branch that I missed before.
| scope = sentry_sdk.get_current_scope() | ||
| messages_data = ( | ||
| truncate_and_annotate_messages(role_normalized_messages, span, scope) | ||
| if should_truncate_gen_ai_input(client.options) | ||
| if not has_span_streaming_enabled(client.options) | ||
| else role_normalized_messages | ||
| ) |
There was a problem hiding this comment.
Span streaming skips blob redaction for Anthropic message inputs
When span streaming is enabled, this path skips truncate_and_annotate_messages and therefore never calls redact_blob_message_parts, so base64 image/document content is stored unredacted on the span. Redact blobs before setting gen_ai.request.messages even when truncation is disabled.
Evidence
- In
_set_common_input_data, messages are set viatruncate_and_annotate_messages(...)only whennot has_span_streaming_enabled(client.options); otherwise rawrole_normalized_messagesare used. _transform_anthropic_content_block/transform_anthropic_content_partconvert Anthropic base64 image/document blocks intotype: "blob"with the originalcontentdata intact.redact_blob_message_parts(which replaces blob content withBLOB_DATA_SUBSTITUTE) is only invoked insidetruncate_and_annotate_messages.- Anthropic base64 tests (e.g.
test_message_with_base64_image) cover the static path and expect redaction; there is no equivalent span-streaming coverage for blob redaction.
Also found at 1 additional location
sentry_sdk/integrations/openai.py:458-481
Identified by Warden · code-review · HUW-UHQ
| sentry_sdk.traces.continue_trace(_sentry_tracing or {}) | ||
|
|
||
| function_name = qualname_from_function(user_f) | ||
| with sentry_sdk.traces.start_span( | ||
| name="unknown Ray task" if function_name is None else function_name, | ||
| attributes={ | ||
| "sentry.op": OP.QUEUE_TASK_RAY, | ||
| "sentry.origin": RayIntegration.origin, | ||
| "sentry.segment.name.source": SegmentNameSource.TASK, | ||
| }, | ||
| parent_span=None, | ||
| ): | ||
| try: | ||
| result = user_f(*f_args, **f_kwargs) | ||
| except Exception: | ||
| exc_info = sys.exc_info() | ||
| _capture_exception(exc_info) | ||
| reraise(*exc_info) | ||
|
|
||
| return result |
There was a problem hiding this comment.
Ray task tracing only works with span streaming enabled
This path always uses traces.continue_trace/traces.start_span, so without trace_lifecycle="stream" Ray tasks become no-op spans and lose transaction tracing that Celery and similar integrations still keep.
Evidence
- The removed branch previously called
sentry_sdk.continue_trace(...)+start_transaction(...)whenhas_span_streaming_enabled(...)was false. - The new code unconditionally calls
sentry_sdk.traces.continue_trace(...)andsentry_sdk.traces.start_span(...). traces.start_span()returnsNoOpStreamedSpan()when the client is active and span streaming is disabled.has_span_streaming_enabled()is still false by default (trace_lifecycledefaults toNone), and Celery still retains both streaming and transaction paths.
Also found at 2 additional locations
sentry_sdk/integrations/rq.py:77-101sentry_sdk/integrations/wsgi.py:133-158
Identified by Warden · code-review · LJD-K6S
| - Direct assignment to `Scope.level` was removed. Use `Scope.set_level` instead. | ||
| - Direct assignment to `Scope.user` was removed. Use `Scope.set_user` instead. | ||
| - `Scope.iter_headers` was removed. | ||
| - The SDK won't set any tags on its own anymore. |
There was a problem hiding this comment.
Migration guide incorrectly claims SDK no longer sets tags
The guide says the SDK won't set tags on its own, but integrations (celery, huey, arq, spark) and tracing still set tags, including http.status_code/status marked for removal in this major.
Evidence
MIGRATION_GUIDE.mdline 150 claims: "The SDK won't set any tags on its own anymore."sentry_sdk/tracing.pyset_http_status()still callsself.set_tag("http.status_code", ...)with comment "TODO-neel remove in major".Span.to_json()still writesself._tags["status"] = self.statuswith "TODO-neel remove redundant tag in major".- Celery/Huey/ARQ/Spark integrations still write tags such as
celery_task_id,huey_task_id, and spark driver/worker tags onto events.
Identified by Warden · code-review · T5F-P9A
|
|
||
| collection_name = command.get(event.command_name) | ||
| operation_name = event.command_name |
There was a problem hiding this comment.
MongoDB session ID now leaks into query spans/breadcrumbs
Restore command.pop("lsid", None) with the other protocol fields so session UUIDs are not included in span names, db.query.text, and breadcrumbs when PII stripping is off.
Evidence
- Previous code always did
lsid = command.pop("lsid", None)before building the query string, then only usedlsidfor legacyoperation_ids.session. - The new path still pops
$db,$clusterTime, and$signature, but no longer removeslsid. query = json.dumps(command, ...)becomes the spanname,SPANDATA.DB_QUERY_TEXT, and breadcrumb message, solsidis emitted whenever PII stripping is disabled.
Identified by Warden · code-review · MGH-8K2
| value = await old_execute_command(self, name, *args, **kwargs) | ||
|
|
||
| db_span.__exit__(None, None, None) | ||
| db_span.end() | ||
|
|
||
| if cache_span: | ||
| _set_cache_data(cache_span, self, cache_properties, value) | ||
| cache_span.__exit__(None, None, None) | ||
| cache_span.end() |
There was a problem hiding this comment.
Async Redis command spans are not ended if execute_command raises
Wrap the await and span cleanup in try/finally (or use with on the spans) so db_span and cache_span always end; otherwise a Redis error leaves them open as the active span and unsent.
Evidence
db_spanand optionalcache_spanare created viasentry_sdk.traces.start_span()with defaultactive=True, so they replace the current scope span inStreamedSpan._start().await old_execute_command(...)runs between span creation and the.end()calls at lines 157/161.- On exception, neither
.end()runs, so_end()never restores_scope.streamed_spanor queues the spans. - The pipeline path in the same file correctly uses
with span:, which always ends the span.
Also found at 2 additional locations
sentry_sdk/integrations/redis/_sync_common.py:156-162sentry_sdk/integrations/strawberry.py:191-201
Identified by Warden · code-review · VMK-B9M
| return getattr(self._inner, name) | ||
|
|
||
|
|
||
| class _FunctionTransport(Transport): | ||
| """ | ||
| DEPRECATED: Users wishing to provide a custom transport should subclass | ||
| the Transport class, rather than providing a function. | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| func: "Callable[[Event], None]", | ||
| ) -> None: | ||
| Transport.__init__(self) | ||
| self._func = func | ||
|
|
||
| def capture_event( | ||
| self, | ||
| event: "Event", | ||
| ) -> None: | ||
| self._func(event) | ||
| return None | ||
|
|
||
| def capture_envelope(self, envelope: "Envelope") -> None: | ||
| # Since function transports expect to be called with an event, we need | ||
| # to iterate over the envelope and call the function for each event, via | ||
| # the deprecated capture_event method. | ||
| event = envelope.get_event() | ||
| if event is not None: | ||
| self.capture_event(event) | ||
|
|
||
|
|
||
| def make_transport(options: "Dict[str, Any]") -> "Optional[Transport]": | ||
| ref_transport = options["transport"] | ||
|
|
There was a problem hiding this comment.
Callable transport now silently falls back to HttpTransport
After removing _FunctionTransport, a callable transport= value is ignored and make_transport still constructs the default HttpTransport when a DSN is set; consider rejecting non-Transport values so events are not sent unexpectedly.
Evidence
- This hunk deletes
_FunctionTransport, which previously wrappedCallable[[Event], None]custom transports. make_transport()only special-casesTransportinstances andTransportsubclasses; a callable falls through both branches.- When
options["dsn"]is set, it still doestransport = transport_cls(options), so the default HTTP transport is used and events are sent. MIGRATION_GUIDE.mddocuments removal, but there is no runtime error or warning for the old callable form.
Identified by Warden · code-review · F3D-F9W
We're preparing our next major on this branch.
The project is tracked in Linear. If you don't have access, we'll try to tag issues belonging to the project with the
SDK3.0 label on GitHub so that you can follow along.Notable changes
Context
You might have read this announcement about us discontinuing work on a 3.0. This is referring to the work done on the
potel-basebranch, which included two types of changes: a huge refactor of our tracing code on the one hand, and various unrelated changes, improvements and fixes on the other. We're dropping the huge refactor part, and only porting the rest, to a new branch and eventually a new 3.0 release.