Skip to content

Next Python SDK major - #5005

Draft
sentrivana wants to merge 176 commits into
masterfrom
major/3.0
Draft

Next Python SDK major#5005
sentrivana wants to merge 176 commits into
masterfrom
major/3.0

Conversation

@sentrivana

@sentrivana sentrivana commented Oct 24, 2025

Copy link
Copy Markdown
Contributor

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 SDK 3.0 label on GitHub so that you can follow along.

Notable changes

  • Transaction-based tracing will be removed. Span streaming will be the default tracing model.
  • Python 3.6 support will be removed.

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-base branch, 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.

@codecov

codecov Bot commented Oct 24, 2025

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.76%. Comparing base (14aff96) to head (d864ba0).
⚠️ Report is 4 commits behind head on master.
✅ All tests successful. No failed tests found.

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     
Files with missing lines Coverage Δ
sentry_sdk/integrations/__init__.py 88.42% <100.00%> (+0.37%) ⬆️

... and 61 files with indirect coverage changes

@github-actions

github-actions Bot commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Codecov Results 📊

88805 passed | ⏭️ 4112 skipped | Total: 92917 | Pass Rate: 95.57% | Execution Time: 282m 37s

📊 Comparison with Base Branch

Metric Change
Total Tests 📉 -44789
Passed Tests 📉 -41791
Failed Tests
Skipped Tests 📉 -2998

All tests are passing successfully.

✅ Patch coverage is 90.30%. Project has 2274 uncovered lines.
✅ Project coverage is 90.3%. Comparing base (692cca2) to head (c3c44b1).

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       -74

Generated by Codecov Action

@github-actions

github-actions Bot commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

None (no version bump detected)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (logging) Separate ignore lists for events/breadcrumbs and sentry logs by sl0thentr0py in #5698

Bug Fixes 🐛

Anthropic

  • Set exception info on streaming span when applicable by alexander-alderman-webb in #5683
  • Patch AsyncStream.close() and AsyncMessageStream.close() to finish spans by alexander-alderman-webb in #5675
  • Patch Stream.close() and MessageStream.close() to finish spans by alexander-alderman-webb in #5674

Documentation 📚

  • Add note on AI PRs to CONTRIBUTING.md by sentrivana in #5696

Internal Changes 🔧

  • Add -latest alias for each integration test suite by sentrivana in #5706
  • Use date-based branch names for toxgen PRs by sentrivana in #5704
  • 🤖 Update test matrix with new releases (03/19) by github-actions in #5703
  • Add client report tests for span streaming by sentrivana in #5677

Other

  • Next Python SDK major by sentrivana in #5005
  • Update CHANGELOG.md by sentrivana in #5685

🤖 This preview updates automatically when you update the PR.

Comment thread sentry_sdk/consts.py
sentrivana and others added 4 commits July 28, 2026 11:47
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
Comment thread sentry_sdk/integrations/otlp.py
### 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
…new major (#6948)

### Description
When `failed_request_status_codes` was first introduced, it accepted a
different format. The format was then changed, while the old format was
deprecated. Drop support for the old format now.

#### Issues
Closes #5017
### 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
Comment thread tests/integrations/django/asgi/test_asgi.py
sentrivana and others added 29 commits September 2, 2026 15:09
### 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)
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.
Comment on lines 557 to 562
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
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 via truncate_and_annotate_messages(...) only when not has_span_streaming_enabled(client.options); otherwise raw role_normalized_messages are used.
  • _transform_anthropic_content_block / transform_anthropic_content_part convert Anthropic base64 image/document blocks into type: "blob" with the original content data intact.
  • redact_blob_message_parts (which replaces blob content with BLOB_DATA_SUBSTITUTE) is only invoked inside truncate_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

Comment on lines +92 to +111
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(...) when has_span_streaming_enabled(...) was false.
  • The new code unconditionally calls sentry_sdk.traces.continue_trace(...) and sentry_sdk.traces.start_span(...).
  • traces.start_span() returns NoOpStreamedSpan() when the client is active and span streaming is disabled.
  • has_span_streaming_enabled() is still false by default (trace_lifecycle defaults to None), and Celery still retains both streaming and transaction paths.
Also found at 2 additional locations
  • sentry_sdk/integrations/rq.py:77-101
  • sentry_sdk/integrations/wsgi.py:133-158

Identified by Warden · code-review · LJD-K6S

Comment thread MIGRATION_GUIDE.md
- 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.md line 150 claims: "The SDK won't set any tags on its own anymore."
  • sentry_sdk/tracing.py set_http_status() still calls self.set_tag("http.status_code", ...) with comment "TODO-neel remove in major".
  • Span.to_json() still writes self._tags["status"] = self.status with "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

Comment on lines 139 to 141

collection_name = command.get(event.command_name)
operation_name = event.command_name

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 used lsid for legacy operation_ids.session.
  • The new path still pops $db, $clusterTime, and $signature, but no longer removes lsid.
  • query = json.dumps(command, ...) becomes the span name, SPANDATA.DB_QUERY_TEXT, and breadcrumb message, so lsid is emitted whenever PII stripping is disabled.

Identified by Warden · code-review · MGH-8K2

Comment on lines 155 to +161
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()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_span and optional cache_span are created via sentry_sdk.traces.start_span() with default active=True, so they replace the current scope span in StreamedSpan._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_span or 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-162
  • sentry_sdk/integrations/strawberry.py:191-201

Identified by Warden · code-review · VMK-B9M

Comment thread sentry_sdk/transport.py
Comment on lines 1117 to 1122
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"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 wrapped Callable[[Event], None] custom transports.
  • make_transport() only special-cases Transport instances and Transport subclasses; a callable falls through both branches.
  • When options["dsn"] is set, it still does transport = transport_cls(options), so the default HTTP transport is used and events are sent.
  • MIGRATION_GUIDE.md documents removal, but there is no runtime error or warning for the old callable form.

Identified by Warden · code-review · F3D-F9W

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants