Skip to content

feat(data-collection): create DataCollection option in client#6702

Draft
ericapisani wants to merge 20 commits into
masterfrom
ep/db-spec-experiement-foundation-dict
Draft

feat(data-collection): create DataCollection option in client#6702
ericapisani wants to merge 20 commits into
masterfrom
ep/db-spec-experiement-foundation-dict

Conversation

@ericapisani

@ericapisani ericapisani commented Jun 30, 2026

Copy link
Copy Markdown
Member

Support more granular PII controls via the data_collection option as seen in the spec.

These changes:

  • introduce the option
  • normalizes the user-provided options into a DataCollection dict
  • handles populating the data_collection option based on what the user provided, and whether or not send_default_pii (which will eventually be legacy) was set.

They don't currently hook up to anything in order to keep the size of this pull request somewhat sane.

Some high-level design decisions to call out:

  1. Introducing 2 types for the different config options - one that's more flexible for user input, the other more strict after we've received the input.

This was done so that, once we've received the config from the user and have set defaults for values that weren't provided, we can avoid needing to constantly check if the value is None within the SDK.

  1. Including a provided_by_user property within the DataCollection type

This is mainly to deal with spotlight-related logic within the client, which you can see here.

Fixes PY-2570
Fixes #6700

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Codecov Results 📊

90424 passed | ⏭️ 6302 skipped | Total: 96726 | Pass Rate: 93.48% | Execution Time: 312m 38s

📊 Comparison with Base Branch

Metric Change
Total Tests 📈 +390
Passed Tests 📈 +390
Failed Tests
Skipped Tests

All tests are passing successfully.

✅ Patch coverage is 96.49%. Project has 2447 uncovered lines.
❌ Project coverage is 89.75%. Comparing base (base) to head (head).

Files with missing lines (2)
File Patch % Lines
sentry_sdk/data_collection.py 98.04% ⚠️ 1 Missing and 2 partials
sentry_sdk/scope.py 50.00% ⚠️ 1 Missing
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
- Coverage    89.76%    89.75%    -0.01%
==========================================
  Files          192       193        +1
  Lines        23823     23880       +57
  Branches      8226      8244       +18
==========================================
+ Hits         21384     21433       +49
- Misses        2439      2447        +8
- Partials      1351      1351         —

Generated by Codecov Action

@ericapisani ericapisani changed the base branch from ep/db-spec-experiment-foundation to master July 2, 2026 19:43
@ericapisani ericapisani changed the title ref(data-collection): Rework DataCollection as a TypedDict, drop accessors feat(data-collection): create DataCollection option in client Jul 2, 2026
…ssors

Replace the class-based DataCollection/KeyValueCollectionBehavior/
GenAICollection/HttpHeadersCollection API with plain TypedDicts
(DataCollection, DataCollectionUserOptions, and per-category
*CollectionBehaviour/*UserOptions types) defined in _types.py.

Collection mode strings move from camelCase (denyList/allowList) to
snake_case (deny_list/allow_list) to match Python convention, since
this is a Python-only deviation from the spec that is never
serialized to Sentry.

Drop the should_collect_user_info/should_collect_gen_ai_inputs/
should_collect_gen_ai_outputs accessor methods and their module-level
shortcuts in favor of reading data_collection fields directly, and
drop the public sentry_sdk.DataCollection/GenAICollection/
HttpHeadersCollection/KeyValueCollectionBehavior exports.

Revert the Unreleased CHANGELOG entry and README example for the
prior class-based API.
@ericapisani ericapisani force-pushed the ep/db-spec-experiement-foundation-dict branch from 1d30c98 to 8b9dedb Compare July 2, 2026 19:47
Comment thread tests/test_data_collection.py
Comment thread tests/test_data_collection.py
Comment thread sentry_sdk/client.py
Comment thread sentry_sdk/data_collection.py
Comment thread sentry_sdk/data_collection.py Outdated
@ericapisani

Copy link
Copy Markdown
Member Author

bugbot run

Comment thread sentry_sdk/client.py Outdated
Comment thread sentry_sdk/client.py
@ericapisani ericapisani marked this pull request as ready for review July 3, 2026 13:04
@ericapisani ericapisani requested a review from a team as a code owner July 3, 2026 13:04
@linear-code

linear-code Bot commented Jul 3, 2026

Copy link
Copy Markdown

PY-2570

Comment thread sentry_sdk/data_collection.py Outdated

@alexander-alderman-webb alexander-alderman-webb left a comment

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.

Just did a quick pass, not a proper review.
Can we add underscores to everything we don't want users to access? 🙏

Comment thread sentry_sdk/data_collection.py Outdated
Comment thread sentry_sdk/data_collection.py Outdated
Comment thread sentry_sdk/data_collection.py Outdated
Comment thread sentry_sdk/data_collection.py Outdated
Comment thread sentry_sdk/client.py Outdated
Comment on lines +757 to +763
@property
def data_collection(self) -> "DataCollection":
"""
Returns the resolved :class:`~sentry_sdk.data_collection.DataCollection`
config for this client.
"""
return self.options["data_collection"]

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.

Do we need to add this accessor?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Probably not, but had added it to be consistent with dsn and should_send_default_pii. While I can kind of see why we have something for should_send_default_pii, I'm not sure why we have dsn in the first place - any context?

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.

There's also client options for which we do not provide accessors. We can always add the accessor later on if there's popular demand, but IMO it'd be good to avoid public API bloat.
If it's convenient for us in the integrations we can also keep a private _data_collection() accessor.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Sounds good, will remove

Comment thread sentry_sdk/client.py Outdated
Comment on lines +441 to +443
@property
def data_collection(self) -> "DataCollection":
return _DISABLED_DATA_COLLECTION_CONFIG

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.

Do we need to add this accessor?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We might not depending on our convo in the other comment that's related.

This was added for consistency with dsn and should_send_default_pii, but otherwise I don't think we need this.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 15c21c0. Configure here.

Comment thread sentry_sdk/client.py
include_local_variables=self.options["include_local_variables"]
is not False,
include_source_context=self.options["include_source_context"]
is not False,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Spotlight frame flags disagree

Medium Severity

In DSN-less Spotlight mode, re-derived data_collection uses include_local_variables is not False (and the same for include_source_context), while _resolve_data_collection treats those options with bool(value) when they are not None. Falsy non-boolean values such as 0 resolve to disabled frame settings initially but become enabled after the Spotlight override.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 15c21c0. Configure here.

Comment thread sentry_sdk/client.py Outdated
@property
def data_collection(self) -> "DataCollection":
return _DISABLED_DATA_COLLECTION_CONFIG

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unpickled client missing data_collection

Low Severity

_Client.__setstate__ restores options from pickled state and calls _init_impl without ensuring a resolved data_collection entry exists. Clients pickled before this option was added have no data_collection key, so DSN-less Spotlight setup or the new data_collection property can raise KeyError.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 15c21c0. Configure here.

# http_bodies: omitted means "all valid types"; [] is the explicit opt-out.
http_bodies = d.get("http_bodies")
http_bodies = (
list(http_bodies) if http_bodies is not None else list(_ALL_HTTP_BODY_TYPES)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

String http_bodies becomes char list

Medium Severity

In _resolve_explicit, a user-provided http_bodies value is passed through list(http_bodies) whenever it is not None. A string is iterable, so a mistaken string body type becomes a list of single-character strings instead of rejecting the value or treating it as one body type.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 15c21c0. Configure here.

…s - we don't currently support gating on that today within the Python SDK
Comment on lines +182 to +191
def _http_headers_from_value(
val: "dict[str, Any]",
) -> "HttpHeadersCollectionBehaviour":
return {
"request": (
_kvcb_from_value(val["request"])
if "request" in val
else _kvcb_from_value({"mode": "deny_list"})
),
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The _http_headers_from_value function silently misinterprets string inputs, leading to an incorrect configuration that is the opposite of the intended effect.
Severity: MEDIUM

Suggested Fix

Add input validation to the _http_headers_from_value function to ensure the val argument is a dictionary. If it is not a dictionary, raise a TypeError to prevent the silent misinterpretation of string values and ensure consistent error handling with other similar functions in the module.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: sentry_sdk/data_collection.py#L182-L191

Potential issue: The function `_http_headers_from_value` in
`sentry_sdk/data_collection.py` incorrectly processes non-dictionary inputs. If a truthy
string like `"off"` is passed for the `http_headers` option, the code performs a
substring check (`"request" in val`) instead of a dictionary key lookup. This check
evaluates to false, causing the function to return a default value that is the opposite
of the user's likely intent. Unlike other similar functions in the file which would
raise an `AttributeError` on invalid input, this function fails silently and produces an
incorrect configuration, potentially leading to unintended data collection behavior.

Comment thread sentry_sdk/data_collection.py
@ericapisani ericapisani marked this pull request as draft July 6, 2026 13:38
@ericapisani

Copy link
Copy Markdown
Member Author

Moving back to draft while I investigate the bot comments

Comment thread sentry_sdk/scope.py

def get_data_collection() -> "DataCollection":
"""Return the resolved DataCollection config of the active client."""
return Scope.get_client().data_collection

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_data_collection() raises AttributeError — no .data_collection attribute exists on any client class

get_data_collection() in scope.py accesses Scope.get_client().data_collection, but no data_collection property or attribute is defined on BaseClient, NonRecordingClient, or _Client; the resolved value lives at self.options["data_collection"]. There is no __getattr__ fallback, so any call to this helper will raise AttributeError. The helper is currently unused (the PR notes the option is not yet hooked up), so this is a latent bug that will surface on first use.

Evidence
  • get_data_collection() returns Scope.get_client().data_collection (scope.py:2184).
  • No client class defines a data_collection property; only dsn, parsed_dsn, and _batchers properties exist (client.py:424, 428, 747, 752, 1329), and NonRecordingClient is a pass-only subclass.
  • The resolved value is stored/read internally as self.options["data_collection"] (client.py:353, 629), and there is no __getattr__ fallback on client.py to proxy attribute access.
  • get_data_collection has no callers anywhere in the repo, so the AttributeError is latent until the helper is wired up.

Identified by Warden code-review · 3TS-36F

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create DataCollection option in client

2 participants