Skip to content

Next Python SDK major - #5005

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

sentrivana wants to merge 281 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 📊

64061 passed | ❌ 2 failed | ⏭️ 2899 skipped | Total: 66962 | Pass Rate: 95.67% | Execution Time: 186m 38s

📊 Comparison with Base Branch

Metric Change
Total Tests 📉 -69939
Passed Tests 📉 -65692
Failed Tests 📈 +2
Skipped Tests 📉 -4249

➕ New Tests (2)

View new tests
  • test_input_attributes_nonstreaming_chat[True-messages0-expected_system_instructions0-expected_input_messages0]
    • File: tests.integrations.mistral.test_mistral
    • Status: ❌ Failing
  • test_cache_spans_decorator
    • File: tests.integrations.django.test_cache_module
    • Status: ❌ Failing

❌ Failed Tests

test_input_attributes_nonstreaming_chat[True-messages0-expected_system_instructions0-expected_input_messages0]

File: tests.integrations.mistral.test_mistral
Suite: py3.10-mistral-v2.0.5
Error: KeyError: 140515602046976

Stack Trace
tests/integrations/mistral/test_mistral.py:380: in test_input_attributes_nonstreaming_chat
    sentry_init(
tests/conftest.py:349: in inner
    client = sentry_sdk.Client(*a, **kw)
sentry_sdk/client.py:278: in __init__
    self._init_impl()
sentry_sdk/client.py:428: in _init_impl
    self.integrations = setup_integrations(
sentry_sdk/integrations/__init__.py:216: in setup_integrations
    for integration_cls in iter_default_integrations(
sentry_sdk/integrations/__init__.py:44: in iter_default_integrations
    yield getattr(import_module(module), cls)
/usr/lib/python3.10/importlib/__init__.py:126: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
<frozen importlib._bootstrap>:1050: in _gcd_import
    ???
<frozen importlib._bootstrap>:1024: in _find_and_load
    ???
<frozen importlib._bootstrap>:171: in __enter__
    ???
<frozen importlib._bootstrap>:123: in acquire
    ???
E   KeyError: 140515602046976

test_cache_spans_decorator

File: tests.integrations.django.test_cache_module
Suite: py3.13-django-v6.1.1
Error: tests/integrations/django/test_cache_module.py:307: in test_cache_spans_decorator assert not spans[0]["attributes"]["cache.hit"] E assert not True

Stack Trace
tests/integrations/django/test_cache_module.py:307: in test_cache_spans_decorator
    assert not spans[0]["attributes"]["cache.hit"]
E   assert not True

✅ Patch coverage is 90.16%. Project has 2066 uncovered lines.
❌ Project coverage is 90.16%. Comparing base (d122d57) to head (a1913eb).

Coverage diff
@@            Coverage Diff             @@
##        master       #PR       +/-##
==========================================
- Coverage    90.22%    90.16%    -0.06%
==========================================
  Files          194       178       -16
  Lines        25997     20999     -4998
  Branches      9662      7582     -2080
==========================================
+ Hits         23454     18933     -4521
- Misses        2543      2066      -477
- Partials      1451      1193      -258

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
Comment thread sentry_sdk/integrations/otlp.py
Comment thread tests/integrations/django/asgi/test_asgi.py
Comment thread sentry_sdk/integrations/otlp.py
Comment thread tests/integrations/threading/test_threading.py Outdated
Comment thread sentry_sdk/integrations/launchdarkly.py Outdated
Comment thread sentry_sdk/scope.py Outdated
### Description
The LaunchDarkly client is a
[singleton](https://launchdarkly.com/docs/sdk/server-side/python#initialize-the-client).
As such, it doesn't make sense to provide a way to patch a specific
client only.

This allows us to get rid of the integation `__init__`, which is always
good: integrations are meant to be setup via `setup_once`.

#### Issues
Closes #7040

#### 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)
- drop old chalice from ci
- bump min version in `integrations/__init__.py`
We're now enforcing Tornado > 6, so compat code for older versions is
not needed anymore
Comment thread sentry_sdk/integrations/trytond.py
Comment thread sentry_sdk/integrations/chalice.py
Comment thread sentry_sdk/integrations/openai_agents/__init__.py
Comment thread sentry_sdk/integrations/pydantic_ai/__init__.py
Comment thread sentry_sdk/integrations/spark/spark_driver.py
Comment thread tests/integrations/launchdarkly/test_launchdarkly.py
Comment thread sentry_sdk/integrations/__init__.py Outdated
Comment thread sentry_sdk/integrations/openai_agents/__init__.py
Comment thread sentry_sdk/integrations/starlette.py
Comment thread sentry_sdk/api.py
Comment thread sentry_sdk/integrations/django/__init__.py
Comment thread sentry_sdk/integrations/langchain.py Outdated
Comment on lines 1182 to 1208
span = sentry_sdk.traces.start_span(
name=f"invoke_agent {run_name}" if run_name else "invoke_agent",
attributes={
"sentry.op": OP.GEN_AI_INVOKE_AGENT,
"sentry.origin": LangchainIntegration.origin,
SPANDATA.GEN_AI_OPERATION_NAME: "invoke_agent",
SPANDATA.GEN_AI_RESPONSE_STREAMING: True,
},
)

if run_name:
span.set_data(SPANDATA.GEN_AI_FUNCTION_ID, run_name)
if run_name:
span.set_attribute(SPANDATA.GEN_AI_FUNCTION_ID, run_name)

_set_tools_on_span(span, tools)

input = args[0].get("input") if len(args) >= 1 else None
if input is not None and record_inputs:
normalized_messages = normalize_message_roles([input])

scope = sentry_sdk.get_current_scope()
messages_data = (
truncate_and_annotate_messages(normalized_messages, span, scope)
if should_truncate_gen_ai_input(client.options)
else normalized_messages
set_data_normalized(
span,
SPANDATA.GEN_AI_REQUEST_MESSAGES,
normalized_messages,
unpack=False,
)
if messages_data is not None:
set_data_normalized(
span,
SPANDATA.GEN_AI_REQUEST_MESSAGES,
messages_data,
unpack=False,
)

# Run the agent
result = f(self, *args, **kwargs)

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.

AgentExecutor.stream leaves invoke_agent span active if setup raises

Wrap result = f(self, *args, **kwargs) in try/except and call span.__exit__(*exc_info) on failure so a setup error does not leave the active span on the scope (see google_genai stream wrappers).

Evidence
  • new_stream() calls sentry_sdk.traces.start_span(...), and Span._start() immediately sets the span as the current scope span when active=True.
  • result = f(self, *args, **kwargs) is unguarded; only new_iterator / new_iterator_async call span.__exit__.
  • If AgentExecutor.stream raises before returning an iterator, the span is never finished and remains the active span for later work.
  • google_genai stream wrappers catch exceptions around the underlying call and finish the span; this path does not.

Identified by Warden · code-review · GUS-JBC

Comment thread sentry_sdk/client.py
Comment on lines 777 to 800
@@ -1169,44 +794,7 @@ def capture_event(

envelope = Envelope(headers=headers)

if is_transaction and isinstance(profile, Profile):
envelope.add_profile(profile.to_json(event_opt, self.options))

if is_transaction and not span_recorder_has_gen_ai_span:
envelope.add_transaction(event_opt)
elif is_transaction:
split_spans = _split_gen_ai_spans(event_opt)
if split_spans is None or not split_spans[1]:
envelope.add_transaction(event_opt)
else:
non_gen_ai_spans, gen_ai_spans = split_spans

event_opt["spans"] = non_gen_ai_spans
envelope.add_transaction(event_opt)

converted_gen_ai_spans = [
_serialized_v1_span_to_serialized_v2_span(span, event_opt)
for span in gen_ai_spans
if isinstance(span, dict)
]

envelope.add_item(
Item(
type=SpanBatcher.TYPE,
content_type=SpanBatcher.CONTENT_TYPE,
headers={
"item_count": len(converted_gen_ai_spans),
},
payload=PayloadRef(
json={
"version": 2,
"items": converted_gen_ai_spans,
},
),
)
)

elif is_checkin:
if is_checkin:
envelope.add_checkin(event_opt)
else:
envelope.add_event(event_opt)

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.

Transaction events now sent as error events

After removing transaction handling, events with type "transaction" still pass through and are sampled as errors then envelope.add_event()'d; drop or reject them explicitly instead of mis-categorizing.

Evidence
  • Previously is_transaction skipped _should_sample_error and used envelope.add_transaction(event_opt).
  • That branch is gone; only is_checkin is special-cased, so type == "transaction" falls into the else path.
  • envelope.add_event() always sets item type to "event", so a transaction payload is emitted as an error event.
  • Call sites still exist that pass {"type": "transaction"} into capture_event (e.g. tests/test_transport.py), and Event typing still allows "transaction".

Identified by Warden · code-review · 3PJ-F9H

Comment thread sentry_sdk/consts.py
Comment on lines 1279 to 1281
ca_certs: "Optional[str]" = None,
propagate_traces: bool = True,
traces_sample_rate: "Optional[float]" = None,
trace_lifecycle: "Optional[Literal['static', 'stream']]" = None,
traces_sampler: "Optional[TracesSampler]" = None,

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.

trace_lifecycle removed without migration note

Removing trace_lifecycle will hard-fail existing init(trace_lifecycle=...) callers, and MIGRATION_GUIDE.md does not document the removal or that stream mode is now the only behavior.

Evidence
  • The hunk deletes trace_lifecycle: Optional[Literal['static', 'stream']] from the public constructor signature.
  • DEFAULT_OPTIONS is generated from ClientConstructor.__init__, so the option disappears from accepted config keys.
  • _get_options() rejects unknown keys with TypeError, so old configs break at init time.
  • MIGRATION_GUIDE.md has no entry for trace_lifecycle, while related removals like stream_gen_ai_spans are explicitly listed.

Identified by Warden · code-review · KAM-DSE

Comment thread sentry_sdk/integrations/boto3.py Outdated
Comment on lines +179 to +186
streaming_span = sentry_sdk.traces.start_span(
name=span.name,
parent_span=span,
attributes={
"sentry.op": OP.HTTP_CLIENT_STREAM,
"sentry.origin": Boto3Integration.origin,
},
)

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.

Boto3 streaming span remains active and hijacks subsequent span parenting

Create the streaming span with active=False (or explicitly scope it only around body reads). The current implementation leaves it as the active span for the entire lifetime of the returned StreamingBody, causing unrelated spans and trace propagation to use the HTTP streaming span as their parent and allowing delayed cleanup to overwrite the current span stack.

Evidence
  • _sentry_after_call() ends the request span at line 173, then starts streaming_span with the default active=True at lines 179-186.
  • Span._start() replaces scope.span with the streaming span, so spans created while the body remains open are parented to http.client.stream rather than the caller's span.
  • streaming_span.end() runs later from read() or close() and restores the span saved at creation; if cleanup occurs while another span is active, _end() unconditionally overwrites that current scope entry, corrupting the span stack.
  • Existing boto3 streaming tests assert emitted span metadata and parent IDs but do not verify the current span or nested spans while the body is open.

Identified by Warden · find-bugs · L6S-EH8

sentrivana and others added 25 commits September 23, 2026 09:07
Boost the likelihood that agents learn the correct API when exploring
our SDK code.
…voke Agent spans (#7520)

Remove the available tools from Invoke Agent spans because it is ambiguous for an agent that makes multiple model calls in the course of its execution.
Each model call can have different available tools.
…t spans (#7517)

Remove the available tools from Invoke Agent spans because it is ambiguous for an agent that makes multiple model calls in the course of its execution.
Each model call can have different available tools.
The parameter allowed users to opt out of collecting GenAI attributes while keeping `send_default_pii=True`.
It is ignored when data collection is used, and the major release drops `send_default_pii` in favor of data collection.
The parameter allowed users to opt out of collecting GenAI attributes while keeping `send_default_pii=True`.
It is ignored when data collection is used, and the major release drops `send_default_pii` in favor of data collection.
### Description
- drop `send_default_pii` support from aws-lambda integration.
- explicitly use `data_collection: {}` in `sentry_init(...)` to fallback
to default config.

#### Issues
Resolves #7585
The parameter allowed users to opt out of collecting GenAI attributes while keeping `send_default_pii=True`.
It is ignored when data collection is used, and the major release drops `send_default_pii` in favor of data collection.
The parameter allowed users to opt out of collecting GenAI attributes while keeping `send_default_pii=True`.
It is ignored when data collection is used, and the major release drops `send_default_pii` in favor of data collection.
The parameter allowed users to opt out of collecting GenAI attributes while keeping `send_default_pii=True`.
It is ignored when data collection is used, and the major release drops `send_default_pii` in favor of data collection.
The parameter allowed users to opt out of collecting GenAI attributes while keeping `send_default_pii=True`.
It is ignored when data collection is used, and the major release drops `send_default_pii` in favor of data collection.
The parameter allowed users to opt out of collecting GenAI attributes while keeping `send_default_pii=True`.
It is ignored when data collection is used, and the major release drops `send_default_pii` in favor of data collection.
The parameter allowed users to opt out of collecting GenAI attributes while keeping `send_default_pii=True`.
It is ignored when data collection is used, and the major release drops `send_default_pii` in favor of data collection.
The parameter allowed users to opt out of collecting GenAI attributes while keeping `send_default_pii=True`.
It is ignored when data collection is used, and the major release drops `send_default_pii` in favor of data collection.
The parameter allowed users to opt out of collecting GenAI attributes while keeping `send_default_pii=True`.
It is ignored when data collection is used, and the major release drops `send_default_pii` in favor of data collection.
…ent spans (#7521)

Remove the response streaming flag from Invoke Agent spans because it is ambiguous for an agent that makes multiple model calls in the course of its execution.
Each model call may be synchronous or streamed, independently.
There is no need for agents to be under a common parent in span-first, since agents that hand off to each other share the same trace ID in the streaming trace lifecycle (sibling agent invocation spans).
### Description
- remove `send_default_pii` branching in `tests_s3.py`.
- Remove `send_default_pii` fallback from
`tracing_utils.get_url_attributes()`

#### Issues
Resolves #7658

This branch has not been deployed

No deployments
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.

8 participants