Skip to content

Next Python SDK major - #5005

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

sentrivana wants to merge 463 commits into
masterfrom
major/3.0

Conversation

@sentrivana

@sentrivana sentrivana commented Oct 24, 2025 •

Copy link
Copy Markdown
Collaborator

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

Coverage Results 📊

✅ 57764 passed | ❌ 1 failed | ⏭️ 3008 skipped | Total: 60773 | Pass Rate: 95.05% | Execution Time: 160m 48s

📊 Comparison with Base Branch

Metric Change
Total Tests 📉 -83897
Passed Tests 📉 -79450
Failed Tests 📈 +1
Skipped Tests 📉 -4448

➕ New Tests (1)

View new tests
  • test_cache_spans_item_size
    • File: tests.integrations.django.test_cache_module
    • Status: ❌ Failing

❌ Failed Tests

test_cache_spans_item_size

File: tests.integrations.django.test_cache_module
Suite: py3.12-django-v6.1.3
Error: tests/integrations/django/test_cache_module.py:546: in test_cache_spans_item_size assert not spans[0]["attributes"]["cache.hit"] E assert not True

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

✅ Patch coverage is 88.77% (2847 of 3207 changed executable lines covered; target 80%).
Project statement coverage is 90.35% (up 0.02 percentage points from base (dbc50bd) to head (1c0d88c)).

Changed files with executable lines (143)
File Patch coverage Changed executable lines
sentry_sdk/integrations/aws_lambda/_handler.py 19.10% 17/89 covered; missed: 43, 45, 46, 59, 60, 62, 63, 65, 66, 67, 69, 70, 72, 76, 78, 79, 80, 81, 82, 83, 91, 95, 99, 107, 109, 112, 113, 115, 116, 117, 118, 119, 120, 121, 125, 126, 127, 131, 132, 133, 134, 135, 139, 140, 144, 145, 146, 147, 149, 154, 156, 168, 169, 170, 171, 172, 173, 174, 176, 190, 191, 192, 197, 198, 199, 200, 201, 205, 206, 208, 209, 211
sentry_sdk/integrations/aws_lambda/_runtime.py 22.00% 11/50 covered; missed: 23, 25, 26, 28, 29, 31, 32, 33, 37, 41, 42, 43, 47, 68, 70, 72, 76, 77, 78, 79, 82, 101, 102, 103, 104, 106, 109, 110, 112, 115, 117, 121, 122, 123, 124, 125, 126, 127, 138
sentry_sdk/integrations/aws_lambda/_request.py 69.86% 51/73 covered; missed: 19, 25, 27, 28, 29, 31, 32, 33, 35, 68, 76, 77, 78, 82, 83, 91, 92, 93, 97, 98, 101, 120; partial branches: 18, 22, 67, 75, 88, 90, 95, 96, 119
sentry_sdk/integrations/langchain.py 90.26% 139/154 covered; missed: 237, 274, 388, 405, 414, 492, 501, 507, 522, 528, 548, 591, 592, 598, 1244; partial branches: 236, 273, 285, 387, 404, 413, 491, 500, 506, 521, 527, 547
sentry_sdk/integrations/ray.py 33.33% 11/33 covered; missed: 92, 94, 95, 104, 105, 106, 107, 108, 109, 111, 130, 134, 135, 138, 139, 140, 141, 143, 160, 161, 162, 163; partial branches: 129
sentry_sdk/integrations/aws_lambda/init.py 36.36% 12/33 covered; missed: 24, 28, 29, 30, 34, 36, 37, 41, 43, 47, 49, 52, 53, 57, 62, 63, 64, 65, 67, 69, 74
sentry_sdk/integrations/mcp.py 93.55% 87/93 covered; missed: 279, 283, 355, 359, 403, 405; partial branches: 278, 282, 288, 354, 358, 366, 371, 386, 389, 393, 395, 398, 447, 449
sentry_sdk/integrations/anthropic.py 76.92% 30/39 covered; missed: 501, 503, 505, 509, 514, 740, 839, 1057, 1159; partial branches: 513, 739, 838, 1056, 1158
sentry_sdk/integrations/google_genai/init.py 80.65% 50/62 covered; missed: 76, 106, 139, 140, 142, 144, 170, 243, 262, 263, 264, 265; partial branches: 75, 242
sentry_sdk/integrations/socket.py 59.09% 13/22 covered; missed: 59, 96, 107, 108, 114, 115, 116, 117, 118; partial branches: 58, 68, 95, 105, 112
sentry_sdk/integrations/gcp.py 7.14% 1/14 covered; missed: 88, 89, 100, 104, 105, 131, 132, 134, 225, 226, 230, 231, 237
sentry_sdk/integrations/openai_agents/patches/runner.py 80.95% 34/42 covered; missed: 152, 173, 178, 179, 196, 201, 202, 203; partial branches: 151, 164, 169, 193
sentry_sdk/integrations/litestar.py 85.00% 34/40 covered; missed: 169, 184, 186, 204, 206, 272; partial branches: 168, 183, 185, 203, 205
sentry_sdk/integrations/asyncpg.py 86.21% 25/29 covered; missed: 216, 217, 244, 245; partial branches: 113, 212, 240, 247, 250
sentry_sdk/integrations/pydantic_ai/spans/ai_client.py 86.11% 31/36 covered; missed: 330, 331, 332, 333, 334; partial branches: 295, 300, 305, 323
sentry_sdk/integrations/mistral.py 92.31% 48/52 covered; missed: 321, 325, 441, 445; partial branches: 320, 324, 440, 444
sentry_sdk/integrations/openai_agents/spans/ai_client.py 87.10% 27/31 covered; missed: 227, 228, 239, 255; partial branches: 225, 230, 238, 254
sentry_sdk/integrations/otlp.py 87.10% 27/31 covered; missed: 154, 158, 180, 219; partial branches: 149, 153, 157, 174
sentry_sdk/integrations/pydantic_ai/spans/invoke_agent.py 100.00% 41/41 covered; partial branches: 65, 69, 77, 95, 103, 111, 113, 121
sentry_sdk/integrations/openai_agents/patches/agent_run.py 58.33% 7/12 covered; missed: 60, 225, 232, 254, 263; partial branches: 104, 182
sentry_sdk/integrations/quart.py 86.84% 33/38 covered; missed: 105, 107, 108, 148, 149; partial branches: 190, 200
sentry_sdk/integrations/aiohttp.py 95.45% 63/66 covered; missed: 331, 332, 333; partial branches: 169, 263, 330
sentry_sdk/integrations/asgi.py 86.49% 32/37 covered; missed: 244, 246, 248, 249, 254; partial branches: 230
sentry_sdk/integrations/django/init.py 94.92% 56/59 covered; missed: 805, 824, 900; partial branches: 590, 804, 823
sentry_sdk/integrations/openai.py 96.84% 92/95 covered; missed: 413, 524, 794; partial branches: 585, 604, 793
sentry_sdk/integrations/starlette.py 93.48% 43/46 covered; missed: 109, 165, 801; partial branches: 164, 540, 738
sentry_sdk/integrations/typesafe.py 90.00% 18/20 covered; missed: 289, 375; partial branches: 253, 288, 339, 374
sentry_sdk/integrations/sqlalchemy.py 86.67% 13/15 covered; missed: 169, 173; partial branches: 82, 150, 164
sentry_sdk/_init_implementation.py 89.47% 17/19 covered; missed: 21, 33; partial branches: 20, 32
sentry_sdk/integrations/aiomysql.py 100.00% 21/21 covered; partial branches: 98, 102, 146, 150
sentry_sdk/integrations/chalice.py 94.29% 33/35 covered; missed: 97, 141; partial branches: 96, 100
sentry_sdk/integrations/cohere.py 81.82% 18/22 covered; missed: 144, 149, 158, 260
sentry_sdk/integrations/django/caching.py 97.37% 37/38 covered; missed: 62; partial branches: 61, 81, 108
sentry_sdk/integrations/django/templates.py 77.78% 7/9 covered; missed: 58, 92; partial branches: 57, 91
sentry_sdk/integrations/django/views.py 84.62% 11/13 covered; missed: 25, 75; partial branches: 24, 74
sentry_sdk/integrations/langgraph.py 97.37% 37/38 covered; missed: 275; partial branches: 118, 137, 272
sentry_sdk/integrations/openai_agents/init.py 96.77% 30/31 covered; missed: 133; partial branches: 90, 116, 126
sentry_sdk/client.py 92.31% 24/26 covered; missed: 420, 421; partial branches: 838
sentry_sdk/integrations/boto3/_utils.py 95.00% 19/20 covered; missed: 33; partial branches: 32, 36
sentry_sdk/integrations/flask.py 66.67% 6/9 covered; missed: 48, 142, 143
sentry_sdk/integrations/google_genai/utils.py 88.46% 23/26 covered; missed: 711, 720, 909
sentry_sdk/integrations/grpc/aio/client.py 87.50% 14/16 covered; missed: 90, 93; partial branches: 89
sentry_sdk/integrations/grpc/client.py 85.71% 12/14 covered; missed: 64, 67; partial branches: 63
sentry_sdk/integrations/starlite.py 87.50% 14/16 covered; missed: 155, 247; partial branches: 154
sentry_sdk/scope.py 100.00% 66/66 covered; partial branches: 745, 1475, 1517
sentry_sdk/data_collection.py 83.33% 5/6 covered; missed: 260; partial branches: 259
sentry_sdk/integrations/celery/init.py 100.00% 37/37 covered; partial branches: 105, 365
sentry_sdk/integrations/django/asgi.py 90.00% 9/10 covered; missed: 181; partial branches: 180
sentry_sdk/integrations/django/middleware.py 80.00% 4/5 covered; missed: 80; partial branches: 79
sentry_sdk/integrations/django/transactions.py 71.43% 5/7 covered; missed: 16, 17
sentry_sdk/integrations/google_genai/streaming.py 84.62% 11/13 covered; missed: 115, 119
sentry_sdk/integrations/grpc/server.py 66.67% 4/6 covered; missed: 62, 63
sentry_sdk/integrations/huggingface_hub.py 93.33% 28/30 covered; missed: 232, 427
sentry_sdk/integrations/pure_eval.py 33.33% 1/3 covered; missed: 24, 30
sentry_sdk/integrations/pyramid.py 86.67% 13/15 covered; missed: 78, 79
sentry_sdk/integrations/pyreqwest.py 100.00% 19/19 covered; partial branches: 139, 163
sentry_sdk/integrations/strawberry.py 97.67% 42/43 covered; missed: 271; partial branches: 270
sentry_sdk/integrations/tornado.py 100.00% 46/46 covered; partial branches: 134, 136
sentry_sdk/traces.py 96.67% 58/60 covered; missed: 293, 678
sentry_sdk/tracing_utils.py 100.00% 29/29 covered; partial branches: 140, 292
sentry_sdk/utils.py 95.12% 39/41 covered; missed: 1401, 1970
sentry_sdk/api.py 91.67% 11/12 covered; missed: 178
sentry_sdk/integrations/arq.py 100.00% 14/14 covered; partial branches: 187
sentry_sdk/integrations/asyncio.py 87.50% 7/8 covered; missed: 164
sentry_sdk/integrations/boto3/_services/kinesis.py 100.00% 20/20 covered; partial branches: 21
sentry_sdk/integrations/boto3/_services/sqs.py 100.00% 20/20 covered; partial branches: 35
sentry_sdk/integrations/boto3/_services/stepfunctions.py 100.00% 38/38 covered; partial branches: 57
sentry_sdk/integrations/bottle.py 100.00% 11/11 covered; partial branches: 118
sentry_sdk/integrations/dramatiq.py 100.00% 17/17 covered; partial branches: 170
sentry_sdk/integrations/falcon.py 100.00% 15/15 covered; partial branches: 97
sentry_sdk/integrations/httpx.py 100.00% 40/40 covered; partial branches: 177
sentry_sdk/integrations/httpx2.py 100.00% 40/40 covered; partial branches: 177
sentry_sdk/integrations/litellm.py 100.00% 14/14 covered; partial branches: 124
sentry_sdk/integrations/logging.py 100.00% 29/29 covered; partial branches: 148
sentry_sdk/integrations/loguru.py 100.00% 14/14 covered; partial branches: 154
sentry_sdk/integrations/openai_agents/patches/error_tracing.py 100.00% 3/3 covered; partial branches: 58
sentry_sdk/integrations/pymongo.py 100.00% 24/24 covered; partial branches: 108
sentry_sdk/integrations/redis/modules/queries.py 100.00% 10/10 covered; partial branches: 55
sentry_sdk/integrations/redis/utils.py 100.00% 7/7 covered; partial branches: 107
sentry_sdk/integrations/rq.py 100.00% 15/15 covered; partial branches: 124
sentry_sdk/integrations/sanic.py 100.00% 32/32 covered; partial branches: 186
sentry_sdk/integrations/threading.py 50.00% 1/2 covered; missed: 65
sentry_sdk/integrations/wsgi.py 100.00% 38/38 covered; partial branches: 425
sentry_sdk/profiler/continuous_profiler.py 50.00% 1/2 covered; missed: 84
sentry_sdk/_compat.py 100.00% 4/4 covered
sentry_sdk/_span_batcher.py 100.00% 11/11 covered
sentry_sdk/_types.py 100.00% 1/1 covered
sentry_sdk/ai/utils.py 100.00% 2/2 covered
sentry_sdk/consts.py 100.00% 76/76 covered
sentry_sdk/envelope.py 100.00% 1/1 covered
sentry_sdk/feature_flags.py 100.00% 3/3 covered
sentry_sdk/integrations/init.py 100.00% 3/3 covered
sentry_sdk/integrations/_asgi_common.py 100.00% 20/20 covered
sentry_sdk/integrations/_wsgi_common.py 100.00% 11/11 covered
sentry_sdk/integrations/ariadne.py 100.00% 9/9 covered
sentry_sdk/integrations/aws_lambda/consts.py 100.00% 5/5 covered
sentry_sdk/integrations/beam.py 100.00% 7/7 covered
sentry_sdk/integrations/boto3/_client.py 100.00% 12/12 covered
sentry_sdk/integrations/boto3/_context.py 100.00% 1/1 covered
sentry_sdk/integrations/boto3/_instrumentation.py 100.00% 28/28 covered
sentry_sdk/integrations/boto3/_services/dynamodb.py 100.00% 10/10 covered
sentry_sdk/integrations/boto3/services/lambda.py 100.00% 10/10 covered
sentry_sdk/integrations/boto3/_services/registry.py 100.00% 9/9 covered
sentry_sdk/integrations/boto3/_services/s3.py 100.00% 45/45 covered
sentry_sdk/integrations/boto3/_services/secretsmanager.py 100.00% 26/26 covered
sentry_sdk/integrations/boto3/_services/sns.py 100.00% 10/10 covered
sentry_sdk/integrations/boto3/consts.py 100.00% 2/2 covered
sentry_sdk/integrations/clickhouse_driver.py 100.00% 19/19 covered
sentry_sdk/integrations/dedupe.py 100.00% 5/5 covered
sentry_sdk/integrations/django/signals_handlers.py 100.00% 4/4 covered
sentry_sdk/integrations/django/tasks.py 100.00% 4/4 covered
sentry_sdk/integrations/executing.py 100.00% 1/1 covered
sentry_sdk/integrations/fastapi.py 100.00% 14/14 covered
sentry_sdk/integrations/gql.py 100.00% 3/3 covered
sentry_sdk/integrations/graphene.py 100.00% 12/12 covered
sentry_sdk/integrations/grpc/init.py 100.00% 2/2 covered
sentry_sdk/integrations/grpc/aio/server.py 100.00% 10/10 covered
sentry_sdk/integrations/huey.py 100.00% 16/16 covered
sentry_sdk/integrations/launchdarkly.py 100.00% 9/9 covered
sentry_sdk/integrations/openai_agents/patches/models.py 100.00% 9/9 covered
sentry_sdk/integrations/openai_agents/spans/init.py 100.00% 1/1 covered
sentry_sdk/integrations/openai_agents/spans/execute_tool.py 100.00% 8/8 covered
sentry_sdk/integrations/openai_agents/spans/invoke_agent.py 100.00% 8/8 covered
sentry_sdk/integrations/openai_agents/utils.py 100.00% 15/15 covered
sentry_sdk/integrations/openfeature.py 100.00% 5/5 covered
sentry_sdk/integrations/pydantic_ai/init.py 100.00% 46/46 covered
sentry_sdk/integrations/pydantic_ai/spans/init.py 100.00% 1/1 covered
sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py 100.00% 10/10 covered
sentry_sdk/integrations/pydantic_ai/spans/utils.py 100.00% 5/5 covered
sentry_sdk/integrations/pydantic_ai/utils.py 100.00% 10/10 covered
sentry_sdk/integrations/redis/init.py 100.00% 7/7 covered
sentry_sdk/integrations/redis/_async_common.py 100.00% 15/15 covered
sentry_sdk/integrations/redis/_sync_common.py 100.00% 9/9 covered
sentry_sdk/integrations/redis/modules/caches.py 100.00% 7/7 covered
sentry_sdk/integrations/redis/redis.py 100.00% 3/3 covered
sentry_sdk/integrations/rust_tracing.py 100.00% 12/12 covered
sentry_sdk/integrations/spark/spark_driver.py 100.00% 8/8 covered
sentry_sdk/integrations/statsig.py 100.00% 1/1 covered
sentry_sdk/integrations/stdlib.py 100.00% 32/32 covered
sentry_sdk/integrations/trytond.py 100.00% 11/11 covered
sentry_sdk/integrations/typer.py 100.00% 5/5 covered
sentry_sdk/integrations/unleash.py 100.00% 5/5 covered
sentry_sdk/transport.py 100.00% 3/3 covered
Coverage diff
@@            Coverage Diff             @@
##        master     #5005       +/-##
==========================================
+ Coverage    90.33%    90.35%    +0.02%
==========================================
  Files          203       199        -4
  Tracked lines     26905     21299     -5606
  Branches     10048      7364     -2684
==========================================
- Hits         24302     19243     -5059
+ Misses        2603      2056      -547
+ Partials      1516      1233      -283

Generated by Coverage 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 Resolved
Comment thread sentry_sdk/integrations/otlp.py Resolved
Comment thread sentry_sdk/integrations/otlp.py Resolved
Comment thread sentry_sdk/integrations/launchdarkly.py Resolved
Comment thread sentry_sdk/scope.py Resolved
Comment thread sentry_sdk/integrations/trytond.py Resolved
Comment thread sentry_sdk/integrations/chalice.py Resolved
Comment thread sentry_sdk/integrations/__init__.py Resolved
Comment thread sentry_sdk/integrations/starlette.py Resolved
sentrivana added a commit that referenced this pull request Aug 6, 2026
Fixes for things that the bots
[surfaced](#5005) on the
major branch:
- some version checks were too late (after patching)
- fix TrytondWSGI integration name/`_MIN_VERSIONS` entry mismatch

Also, changed the warning of the `DidNotEnable` message from "X not
installed" to "X not installed or incompatible".
Comment thread sentry_sdk/integrations/langchain.py Resolved
Comment thread sentry_sdk/spotlight.py Resolved
Comment thread sentry_sdk/spotlight.py Resolved
Comment thread sentry_sdk/integrations/otlp.py Resolved
Comment thread sentry_sdk/integrations/aiomysql.py Resolved
Comment thread sentry_sdk/integrations/aiomysql.py Resolved
Comment thread MIGRATION_GUIDE.md Resolved
Comment thread sentry_sdk/integrations/otlp.py Resolved
Comment thread sentry_sdk/integrations/pyramid.py Resolved
Comment thread sentry_sdk/integrations/strawberry.py Resolved
Comment thread sentry_sdk/integrations/stdlib.py Resolved
Comment thread sentry_sdk/integrations/stdlib.py Resolved
Comment thread sentry_sdk/integrations/__init__.py Resolved
Comment thread sentry_sdk/integrations/threading.py Resolved
Comment thread tests/integrations/httpx/test_httpx.py Resolved
sentrivana added a commit that referenced this pull request Aug 26, 2026
Originally raised by a bot
[here](#5005 (comment)):
the `parse_version` function parses version strings as is (e.g. 3.1
becomes `(3, 1)`). We use these parsed version tuples in integrations to
compare the installed version against the minimum (defined in
`integrations/__init__.py`). The minimum versions are often three-part,
e.g. `(3, 1, 0)`.

This means that we can mistakenly consider a valid version to be below
the minimum, because in pure tuple terms, `(3, 1) < (3, 1, 0)` is true.

This can also happen in reverse (package version has three parts, while
our min version boundary has two).

In this PR, we make the internal version comparison work as expected
regardless of mismatches in the length of the version strings/tuples.
Comment thread sentry_sdk/integrations/typer.py Resolved
Comment thread sentry_sdk/_init_implementation.py Resolved
Comment thread sentry_sdk/integrations/arq.py Resolved
Comment thread sentry_sdk/integrations/asgi.py Resolved
Comment thread sentry_sdk/integrations/strawberry.py Resolved
Comment thread sentry_sdk/integrations/redis/redis.py Resolved
Comment thread sentry_sdk/integrations/stdlib.py Resolved
Comment thread MIGRATION_GUIDE.md Resolved
Comment thread MIGRATION_GUIDE.md Resolved
Comment thread sentry_sdk/consts.py Resolved
Comment thread sentry_sdk/integrations/anthropic.py Resolved
Comment thread sentry_sdk/integrations/ray.py Resolved
Comment thread MIGRATION_GUIDE.md Resolved
Comment thread sentry_sdk/integrations/pymongo.py Resolved
Comment thread sentry_sdk/transport.py Resolved
pabloDeputter and others added 3 commits October 23, 2026 13:13
## Description

Adds span links as is specified in the [specs](https://develop.sentry.dev/sdk/telemetry/spans/#span-links): a span can reference other spans, e.g. in other traces, without making them its parent.

- `sentry_sdk.start_span(..., links=[...])`, `span.add_link(link)` and `span.add_links(links)`; with links being of the following format: `{"trace_id", "span_id", "sampled"?, "attributes"?}`
- span without links omits `links`
- `NoOpSpan` ignores links
- added to the migration guide as well
- [example](https://sentry-sdks.sentry.io/explore/traces/trace/8194b70609574447aa8732f24e1350a0/?mode=samples&node=span-bad54d1bc8cb9604&project=4511733153660928&source=traces&statsPeriod=24h&table=trace&timestamp=1792484175.113) in sentry-test

Related docs update can be found [here](getsentry/sentry-docs#20103).

## Why
An SQS/SNS consumer can receive a batch of records sent by several producers, each in its own trace. A span can only have one parent, so without links there's no way to connect the consumer each producer. With links the consumer span can point to each producer's trace.
Comment thread MIGRATION_GUIDE.md Resolved
Comment on lines +21 to +33
identity = aws_event.get("requestContext", {}).get("identity")
if identity is None:
return {}

user_info: "dict[str, Any]" = {}

user_arn = identity.get("userArn")
if user_arn is not None:
user_info["id"] = user_arn

ip = identity.get("sourceIp")
if ip is not None:
user_info["ip_address"] = ip

@sentry-warden sentry-warden Bot Oct 23, 2026 •

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.

_get_user_from_event crashes on non-dict requestContext/identity

Use isinstance checks for requestContext and identity before calling .get — a null or non-dict value raises AttributeError; the handler invokes this outside capture_internal_exceptions and can fail the Lambda before user code runs, and the event processor path can drop the error event.

Evidence
  • _get_user_from_event does aws_event.get("requestContext", {}).get("identity"); if the requestContext key is present with value None (or another non-dict), the default {} is skipped and .get raises AttributeError.
  • After the identity is None guard, identity.get("userArn") / identity.get("sourceIp") still assume a mapping, so a non-dict identity also raises.
  • _handler.py calls _get_user_from_event(request_data) outside capture_internal_exceptions when data_collection["user_info"] is true (the default), so the exception aborts sentry_handler before the user handler runs.
  • The same helper runs inside the request event processor; Scope.run_event_processors treats a processor exception as a dropped event.
  • The same file already guards headers with isinstance(..., dict) (e.g. _get_url), but not requestContext/identity.

Identified by Warden · code-review, find-bugs · RSZ-TZN

from sentry_sdk.consts import SPANDATA
from sentry_sdk.traces import StreamedSpan
from sentry_sdk.tracing_utils import has_span_streaming_enabled
from sentry_sdk.traces import Span

@sentry-warden sentry-warden Bot Oct 23, 2026 •

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.

Closing a Cohere chat stream leaves its Span active

If a consumer closes the returned chat_stream iterator before it is exhausted, GeneratorExit bypasses the except Exception handler and span.end() is skipped. Because the span was activated when chat_stream was called, the current scope can retain that unfinished span and use it as the parent for later spans.

Evidence
  • _wrap_chat starts an active span before returning the wrapped stream iterator.
  • new_iterator calls span.end() only after normal exhaustion; its cleanup handler catches Exception, not GeneratorExit.
  • There is no finally block, so closing a suspended iterator skips span.end().
  • Span._end() restores the previous scope span; without it, the scope can retain the unfinished span.
Also found at 2 additional locations
  • sentry_sdk/integrations/cohere.py:224-246
  • sentry_sdk/integrations/cohere.py:197-260

Identified by Warden · find-bugs, code-review · AZ7-GHE

Comment thread sentry_sdk/integrations/gql.py Resolved
Comment thread sentry_sdk/integrations/gql.py Resolved
Comment thread sentry_sdk/consts.py Resolved
pabloDeputter and others added 10 commits October 27, 2026 17:16
### Description
S3 object operations now override default `rpc` op with `object.*`.
Other S3 operations, e.g. `ListBuckets`, stay on `rpc`.
…into-3.0

# Conflicts:
#	.github/workflows/test-integrations-agents.yml
#	.github/workflows/test-integrations-cloud.yml
#	scripts/populate_tox/config.py
#	scripts/populate_tox/package_dependencies.jsonl
#	scripts/populate_tox/releases.jsonl
#	scripts/populate_tox/tox.jinja
#	sentry_sdk/integrations/falcon.py
#	tests/integrations/falcon/test_falcon.py
#	tox.ini
### Description

Removes `with_metaclass()` from `sentry_sdk/_compat.py`. Nothing on
`major/3.0` uses it anymore.

It's a Python 2 / `six`-style helper for declaring a metaclass across
Python versions; on Python 3 you just write `class Foo(metaclass=Meta)`.
Its only caller was `class Hub(with_metaclass(HubMeta))` in
`sentry_sdk/hub.py`. #6919 ("Drop hub related code in new major")
deleted `Hub` and the import, but left the helper behind.

With the helper gone, the `TYPE_CHECKING` block in `_compat.py` (`Any`,
`TypeVar`, `T`) is unused too, so it's removed as well. `T` wasn't used
before this change either.

**Evidence**
- On `major/3.0`, `git grep with_metaclass` matches only the definition.
There are no callers in `sentry_sdk`, `tests`, `scripts` or `docs`.
- Nothing imports `T` or `Any` from `sentry_sdk._compat`.
- #6091 and #7421 (against `master`) proposed the same removal as part
of larger Python 2 cleanups. Both were closed without being merged, one
for inactivity. Neither raised an objection to this change. On `master`,
`Hub` still uses the helper, so this only applies to `major/3.0`.

`_compat` is a private module, so this doesn't touch the public API.

#### Issues
n/a

<!-- junior-request-attribution:start -->
via **Ivana Kellyer**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:slack%3AC0BTREZTS2Z%3A1793270559.842119 -->

--

[View Junior
Session](https://junior-prod.sentry.dev/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119)
[[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Ivana Kellyer <ivana.kellyer@sentry.io>
…8154)

### Description

Removes the `PY37` flag from `sentry_sdk/_compat.py`, along with the
code paths that only ran when it was `False`. It also removes the
`nanosecond_time()` wrapper, which existed only to choose between those
paths.

`setup.py` sets `python_requires=">=3.7"`, so `PY37` is always `True` on
every supported interpreter, and the `tox.ini` matrix starts at py3.7.
That makes these branches unreachable:

- `nanosecond_time()` in `sentry_sdk/utils.py`: the
`int(time.perf_counter() * 1e9)` fallback is gone. Once only
`time.perf_counter_ns()` was left, the wrapper added nothing, so it's
removed as well. Its two call sites in `sentry_sdk/traces.py` (`Span`
start and end) now call `time.perf_counter_ns()` directly.
- `tests/test_gevent.py`: `compression_algo` always includes `"br"` now,
as it already did in practice.
- `tests/test_transport.py`: `elif PY37:` becomes `else:`, and the
pre-3.7 case list after it is removed. The cases that run on 3.7 are
unchanged.

`PY38`, `PY310` and `PY311` are still used, so they stay. This doesn't
depend on #8153; the two only touch different lines of `_compat.py` and
merge cleanly together.

`_compat` and `utils` are private modules, and nothing else in the repo
(including tests) references `nanosecond_time`. Runtime behavior doesn't
change on any supported Python version.

#### Issues
n/a

<!-- junior-request-attribution:start -->
via **Ivana Kellyer**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:slack%3AC0BTREZTS2Z%3A1793270559.842119 -->

--

[View Junior
Session](https://junior-prod.sentry.dev/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119)
[[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Ivana Kellyer <ivana.kellyer@sentry.io>
### Description

Removes the private `_get_usage_attributes()` helper from
`sentry_sdk/tracing_utils.py`. Nothing on `major/3.0` calls it anymore.

It pulled `gen_ai.usage.*` token counts out of return values for the AI
Agents `@trace` templates added in #4676. #7457 ("Remove trace template,
trace_ignore_status_codes") deleted the template code that called it,
but left the helper behind.

**Evidence**
- On `major/3.0`, `git grep -w _get_usage_attributes` matches only the
definition. There are no callers in `sentry_sdk` or `tests`.
- No open PR touches it.

`_get_value()` is still imported by the LangChain integration, so it
stays.

On `master` the helper is still used, because the trace templates still
exist there. This change only applies to `major/3.0`.

#### Issues
n/a

<!-- junior-request-attribution:start -->
via **Ivana Kellyer**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:slack%3AC0BTREZTS2Z%3A1793270559.842119 -->

--

[View Junior
Session](https://junior-prod.sentry.dev/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119)
[[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Ivana Kellyer <ivana.kellyer@sentry.io>
### Description

Removes the private `_ScopeManager` class from `sentry_sdk/scope.py`.
Nothing on `major/3.0` uses it anymore.

It was the context manager behind `push_scope()` and the `Hub` scope
helpers. #6919 ("Drop hub related code in new major") and #6944 ("Remove
`push_scope`, `configure_scope` in new major") removed those callers and
their imports, but left the class behind.

**Evidence**
- On `major/3.0`, `git grep -w _ScopeManager` matches only the class
definition in `sentry_sdk/` and `tests/`. The only other hits are
historical `CHANGELOG.md` and `MIGRATION_GUIDE.md` entries from 2.0
about it moving out of `sentry_sdk.hub`.
- No open PR touches it.

It's underscore-private and isn't exported, so this doesn't touch the
public API.

#### Issues
n/a

<!-- junior-request-attribution:start -->
via **Ivana Kellyer**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:slack%3AC0BTREZTS2Z%3A1793270559.842119 -->

--

[View Junior
Session](https://junior-prod.sentry.dev/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119)
[[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Ivana Kellyer <ivana.kellyer@sentry.io>
### Description

Removes two helpers that only `EventScrubber` used:

- `iter_event_frames()` in `sentry_sdk/utils.py`
- `AnnotatedValue.substituted_because_contains_sensitive_data()` in
`sentry_sdk/_types.py`

#7772 ("Remove `EventScrubber` and the `event_scrubber` option") deleted
`sentry_sdk/scrubber.py`, which held the only call to each of them:
frame-vars scrubbing called `iter_event_frames(event)`, and denylisted
keys were replaced with
`AnnotatedValue.substituted_because_contains_sensitive_data()`. Both
helpers were left behind.

**Evidence**
- On `major/3.0`, `git grep -w` for each name matches only its
definition. There are no callers in `sentry_sdk`, `tests`, `scripts` or
`docs`.
- No open PR touches either one.

`iter_event_stacktraces()` (used by `handle_in_app`) and
`SENSITIVE_DATA_SUBSTITUTE` (used by the data-collection filtering and
several integrations) are still used, so they stay.

On `master`, `EventScrubber` still uses both helpers, so this only
applies to `major/3.0`. `iter_event_frames` could be imported from
`sentry_sdk.utils`, but it isn't documented anywhere, and `major/3.0` is
already a breaking release.

#### Issues
n/a

<!-- junior-request-attribution:start -->
via **Ivana Kellyer**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:slack%3AC0BTREZTS2Z%3A1793270559.842119 -->

--

[View Junior
Session](https://junior-prod.sentry.dev/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119)
[[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
### Description

Removes `AwsCallContext.service_id_hyphenized` from
`sentry_sdk/integrations/boto3/_context.py`. It's still computed on
every call (`service_id.hyphenize()`), but on `major/3.0` nothing reads
it.

It had two readers. One was the `aws.service_id` span tag, which #7261
("Drop tags auto-set by the SDK") removed. The other was the span name:
#7800 ("align span names with OTel conventions") switched that to
`ctx.service_id`, so the hyphenized value lost its last reader. This
removes the slot, the attribute and the `hyphenize()` call.

**Evidence**
- On `major/3.0`, `git grep -w service_id_hyphenized` matches only the
three lines in `_context.py` that set it up.
- The open boto3 extension PRs against `major/3.0` (#8117 SQS, #8137
Lambda) don't reference it.

On `master`, `service_id_hyphenized` is still used for the span name and
the tag, so this only applies to `major/3.0`.

#### Issues
n/a

<!-- junior-request-attribution:start -->
via **Ivana Kellyer**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:slack%3AC0BTREZTS2Z%3A1793270559.842119 -->

--

[View Junior
Session](https://junior-prod.sentry.dev/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119)
[[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Ivana Kellyer <ivana.kellyer@sentry.io>
### Description

Removes `get_url_attributes_legacy()` from
`sentry_sdk/tracing_utils.py`. Nothing on `major/3.0` calls it anymore.

Its callers were the outgoing HTTP client integrations (pyreqwest,
httpx, httpx2). It handled their old `send_default_pii` behavior. When
#7698, #7819 and #7830 dropped `send_default_pii` support from those
integrations, each one removed its calls and switched to
`get_url_attributes()`. The legacy helper was left behind.

**Evidence**
- On `major/3.0`, `git grep -w get_url_attributes_legacy` matches only
the definition. There are no callers in `sentry_sdk`, `tests`, `scripts`
or `docs`.
- No open PR mentions it. The function doesn't exist on `master`, so a
future `master` → `major/3.0` merge can't bring it back.

`get_url_attributes()` and
`_apply_data_collection_filtering_to_query_string` are still used, so
they stay.

#### Issues
n/a

<!-- junior-request-attribution:start -->
via **Ivana Kellyer**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:slack%3AC0BTREZTS2Z%3A1793270559.842119 -->

--

[View Junior
Session](https://junior-prod.sentry.dev/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119)
[[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC0BTREZTS2Z%3A1793270559.842119/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Ivana Kellyer <ivana.kellyer@sentry.io>
Comment on lines 326 to +374
@@ -409,10 +348,7 @@ def _set_messaging_destination_name(
if delivery_info.get("exchange") == "" and routing_key is not None:
# Empty exchange indicates the default exchange, meaning the tasks
# are sent to the queue with the same name as the routing key.
if isinstance(span, StreamedSpan):
span.set_attribute(SPANDATA.MESSAGING_DESTINATION_NAME, routing_key)
else:
span.set_data(SPANDATA.MESSAGING_DESTINATION_NAME, routing_key)
span.set_attribute(SPANDATA.MESSAGING_DESTINATION_NAME, routing_key)


def _wrap_task_call(task: "Any", f: "F") -> "F":
@@ -425,34 +361,17 @@ def _inner(*args: "Any", **kwargs: "Any") -> "Any":
if client.get_integration(CeleryIntegration) is None:
return f(*args, **kwargs)

span_streaming = has_span_streaming_enabled(client.options)

try:
if span_streaming and get_current_span() is None:
if sentry_sdk.get_current_span() is None:
return f(*args, **kwargs)

span: "Union[Span, StreamedSpan]"
if span_streaming:
span = sentry_sdk.traces.start_span(
name=task.name,
attributes={
"sentry.op": OP.QUEUE_PROCESS,
"sentry.origin": CeleryIntegration.origin,
},
)
else:
span = sentry_sdk.start_span(
op=OP.QUEUE_PROCESS,
name=task.name,
origin=CeleryIntegration.origin,
)

with span:
if isinstance(span, StreamedSpan):
set_on_span = span.set_attribute
else:
set_on_span = span.set_data

with sentry_sdk.start_span(
name=task.name,
attributes={
"sentry.op": OP.QUEUE_PROCESS,
"sentry.origin": CeleryIntegration.origin,
},
) as span:

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.

Celery emits two nested queue.process spans per task

After op consolidation, both the segment in _wrap_tracer and the child in _wrap_task_call use OP.QUEUE_PROCESS, so each task yields two identically-op'd spans; other queue integrations keep a single queue.process segment with messaging attributes on it. Consider moving messaging attributes onto the segment and dropping the inner span, or giving the segment a distinct op, to avoid double-counting in queue insights.

Evidence
  • _wrap_tracer starts a segment with "sentry.op": OP.QUEUE_PROCESS and parent_span=None.
  • _wrap_task_call starts a child span that also sets "sentry.op": OP.QUEUE_PROCESS, plus messaging attributes.
  • RQ/Arq/Huey/Dramatiq each emit one queue.process segment with messaging data on that span.
  • Pre-change span-streaming used OP.QUEUE_TASK_CELERY for the outer span and OP.QUEUE_PROCESS only for the inner one.

Identified by Warden · code-review · X2K-WWE

sentrivana and others added 4 commits October 30, 2026 12:10
….1 (#8168)

### Description

Partially reverts #7058 so projects pinned to redis-py 3.x and
redis-py-cluster 2.x keep their Redis instrumentation in 3.0. With the
4.2 minimum, an explicitly configured `RedisIntegration()` raises
`DidNotEnable` and `sentry_sdk.init()` fails.

This came up while dogfooding 3.0 in getsentry/sentry, which is pinned
to redis-py 3.4.1 because it still uses redis-py-cluster (which requires
`redis<4`). It also can't move past redis-py 4.x yet because `rb` breaks
on redis-py 5+.

Only what's needed for those versions is restored:
- Lower the redis-py minimum from 4.2 to 3.4.1.
- Patch the `redis.asyncio` client and pipeline only if `redis.asyncio`
exists (added in redis-py 4.2). This import was guarded in 2.x and #7058
made it unconditional, which is the actual reason 3.x stopped working.
- Restore the redis-py-cluster instrumentation, limited to 2.x. 1.x
requires redis-py 2.x, which is below the new minimum, so the
`StrictRedisCluster`/`StrictClusterPipeline` branches aren't brought
back.
- Re-add the `redis_py_cluster_legacy` test suite (rewritten for span
streaming and the 3.0 attribute names) and its CI entry.

Kept from #7058: the `max_data_size` removal and the `StrictRedis` ->
`Redis` switch.

`tox.ini` was regenerated with `populate_tox.py --only redis
redis_py_cluster_legacy` to avoid unrelated matrix churn. This adds
redis 3.4.1/3.5.3 and drops 4.2.2 as the lowest 4.x env. CI only runs
redis 3.x on py3.7/3.8 because of its classifiers. I also checked 3.4.1
+ rb + redis-py-cluster 2.1.0 on Python 3.13 manually against
getsentry/sentry.

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.

4 participants