Repository navigation
Next Python SDK major - #5005
sentrivana wants to merge 456 commits into
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
|
Coverage Results 📊✅ 57523 passed | ⏭️ 2979 skipped | Total: 60502 | Pass Rate: 95.08% | Execution Time: 159m 31s All tests are passing successfully. ✅ Patch coverage is 88.78% (2849 of 3209 changed executable lines covered; target 80%). Changed files with executable lines (143)
Coverage diff@@ Coverage Diff @@
## master #5005 +/-##
==========================================
+ Coverage 90.32% 90.37% +0.05%
==========================================
Files 203 198 -5
Tracked lines 26872 21251 -5621
Branches 10044 7360 -2684
==========================================
- Hits 24272 19205 -5067
+ Misses 2600 2046 -554
+ Partials 1520 1237 -283Generated by Coverage 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. |
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".
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.
### Description - use of f-strings instead `”...”.format()`. - move constants into `consts.py`. - add new attributes to `SPANDATA` (will be used in the next PR).
### Description Expand aws-lambda instrumentation with extra resource identity and runtime metadata following OTel (https://opentelemetry.io/docs/specs/semconv/faas/aws-lambda/). Changes: - add `sentry.kind=server`. - add `cloud.account.id`, extracted from the invoked function ARN. - `cloud.resource_id` is now normalized from the invoked function ARN; keep the original value in `aws.lambda.invoked_arn` and replace an alias with the resolved published version when the function is not `$LATEST`. - `cloud.region` is now extracted from `AWS_REGION` env. var; `faas.name` from `AWS_LAMBDA_FUNCTION_NAME`; `faas.version` from `AWS_LAMBDA_FUNCTION_VERSION` following OTel (https://opentelemetry.io/docs/specs/semconv/faas/aws-lambda/#resource-detector). - refresh `sentry_sdk` on every test setup (for easier local testing so runs don’t reuse old behavior from earlier implementation). - add `capture_internal_exceptions()` around attribute extraction so that a single failure does not affect the other instrumentation. #### Issues Resolves #7931
### Description Adds the following attributes: - `aws.secretsmanager.secret.arn` (OTel): from an ARN `SecretID`, replaced by the response `ARN` (if available). - `aws.secretsmanager.secret.name` (getsentry/sentry-conventions#716): from response `Name` or the request `Name` or `SecretId`, else the ARN without its random `-XXXXXX` suffix (in case of failed calls). The actual secret values are never read. #### Issues Resolves #7574
## Description Adds the following attributes: - `aws.step_functions.state_machine.arn`, `*.activity.arn` (OTel) and `*.execution.arn` (getsentry/sentry-conventions#672), from the request and response. - `aws.step_functions.state_machine.name` and `*.activity.name` (getsentry/sentry-conventions#716), from `name` on create calls or from the ARN without its version/alias qualifier. Whenever `StartSyncExecution` returns a failed call (`FAILED` or `TIMED_OUT`), `error.type` will be set to the respective value. ### Issues Resolves #7478
### Description Adds the following attribute: - `aws.kinesis.stream_name` (OTel): from `StreamName`, else from the stream in `StreamARN`, `ConsumerARN` or `ResourceARN` (`…:stream/<name>[/consumer/…]`). #### Issues Resolves #7575
Updated the changelog for the upcoming 3.0.0b1 release, highlighting notable changes, new features, and migration resources.
## 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×tamp=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.
| 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 |
There was a problem hiding this comment.
_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_eventdoesaws_event.get("requestContext", {}).get("identity"); if therequestContextkey is present with valueNone(or another non-dict), the default{}is skipped and.getraises AttributeError.- After the
identity is Noneguard,identity.get("userArn")/identity.get("sourceIp")still assume a mapping, so a non-dict identity also raises. _handler.pycalls_get_user_from_event(request_data)outsidecapture_internal_exceptionswhendata_collection["user_info"]is true (the default), so the exception abortssentry_handlerbefore the user handler runs.- The same helper runs inside the request event processor;
Scope.run_event_processorstreats a processor exception as a dropped event. - The same file already guards headers with
isinstance(..., dict)(e.g._get_url), but notrequestContext/identity.
Identified by Warden · code-review, find-bugs · RSZ-TZN
| if "queryStringParameters" in request_data: | ||
| qs = request_data["queryStringParameters"] | ||
| if qs: | ||
| filtered_qs = _apply_key_value_collection_filtering( | ||
| items=qs, | ||
| behaviour=data_collection["url_query_params"], | ||
| ) | ||
| if filtered_qs: | ||
| additional_attributes[SPANDATA.URL_QUERY] = urlencode( | ||
| filtered_qs | ||
| ) |
There was a problem hiding this comment.
queryStringParameters assumed to be a Mapping
Check isinstance(qs, Mapping) (or wrap in capture_internal_exceptions) before filtering: a truthy non-dict value makes _apply_key_value_collection_filtering call .items() and crash the invocation.
Evidence
- The branch only checks
if qs:then passesqsto_apply_key_value_collection_filtering(items=qs, ...). - That helper always does
items.items()with no type guard. - Unlike
headers(explicitly coerced to{}when not a dict for EventBridge),queryStringParametershas noisinstancecheck. - This path is outside
capture_internal_exceptions, so a bad value abortssentry_handlerbefore the user handler runs.
Identified by Warden · find-bugs, code-review · FLN-MQ8
| 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 |
There was a problem hiding this comment.
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_chatstarts an active span before returning the wrapped stream iterator.new_iteratorcallsspan.end()only after normal exhaustion; its cleanup handler catchesException, notGeneratorExit.- There is no
finallyblock, so closing a suspended iterator skipsspan.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-246sentry_sdk/integrations/cohere.py:197-260
Identified by Warden · find-bugs · AZ7-GHE
| sample_rate: float = 1.0, | ||
| send_default_pii: "Optional[bool]" = None, | ||
| data_collection: "Optional[DataCollectionUserOptions]" = None, | ||
| data_collection: "Optional[DataCollectionUserOptions]" = {}, |
There was a problem hiding this comment.
data_collection typed Optional but None raises TypeError
Drop Optional from the annotation (or accept None in _resolve_data_collection); type checkers allow data_collection=None, but init will raise TypeError.
Evidence
ClientConstructor.__init__typesdata_collectionasOptional[DataCollectionUserOptions]with default{}.DEFAULT_OPTIONSis built from these defaults via_get_default_options()/inspect.getfullargspec._resolve_data_collection()doesisinstance(user_dc, dict)and raisesTypeErrorfor any non-dict, includingNone._get_options()always calls_resolve_data_collection(rv), sosentry_sdk.init(data_collection=None)fails at runtime despite the Optional type.
Identified by Warden · code-review, find-bugs · BW5-RFW
### 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>
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.