Skip to content

fix: Raise for missing DeepAttrDict keys instead of inserting them - #643

Merged
razor-x merged 2 commits into
mainfrom
claude/python-sdk-audit-o2iid9-10-deep-attr-dict
Aug 28, 2026
Merged

fix: Raise for missing DeepAttrDict keys instead of inserting them#643
razor-x merged 2 commits into
mainfrom
claude/python-sdk-audit-o2iid9-10-deep-attr-dict

Conversation

@razor-x

@razor-x razor-x commented Aug 27, 2026

Copy link
Copy Markdown
Member

What

DeepAttrDict — the type behind custom_metadata on devices, connected accounts, phones, connect webviews, and events, and the fallback for unknown event/action-attempt variants — silently auto-vivified on read: device.custom_metadata.reservaton_id returned {} and wrote the typo'd key into the resource, which then re-serialized back to the API on the next update (SDK audit finding M9, runtime-verified). MIGRATION.md claimed this exact v1 footgun was fixed.

  • Reading a missing key now raises like a plain dict: KeyError when indexing, AttributeError for attribute access — matching the typed resource dataclasses and what MIGRATION.md already promises. __getattr__ translates KeyError to AttributeError so hasattr, getattr(..., default), and copy/pickle protocol probes behave normally.
  • Nothing is ever inserted on read. Deep-wrapping on write is unchanged; .get(), in, and iteration remain the probes for optional keys.
  • MIGRATION.md's misleading "custom_metadata ... not affected" line now states the raising behavior and the .get() probe pattern.

⚠️ Behavior change: code that probed optional custom_metadata keys via bare attribute access and truthy-checked the {} must switch to .get("key") — the pattern the migration guide already prescribes for typed resources.

Testing

Extended test/deep_attr_dict_test.py (previously a single 7-line happy path): missing key raises KeyError / missing attribute raises AttributeError; reads never insert; hasattr/getattr-default/.get/in all behave; nested wrapping intact; end-to-end — a typo'd custom_metadata read on a hydrated device raises and leaves the metadata unmutated; unknown-event fallback fields stay readable.

Revert check: with seam/deep_attr_dict.py reverted to main, the tests fail with DID NOT RAISE AttributeError — the silent-vivification symptom.

Full suite: 192 passed; mypy, pylint (10.00), black clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y1RzepycXEYA3LStfjt8cY


Generated by Claude Code

@razor-x
razor-x requested a review from a team as a code owner August 27, 2026 21:50
@razor-x
razor-x force-pushed the claude/python-sdk-audit-o2iid9-10-deep-attr-dict branch from d27b83d to 1c3d417 Compare August 28, 2026 19:30
@razor-x
razor-x force-pushed the claude/python-sdk-audit-o2iid9-10-deep-attr-dict branch from 1c3d417 to 1e56f51 Compare August 28, 2026 20:43
@razor-x
razor-x merged commit d8dced4 into main Aug 28, 2026
23 checks passed
@razor-x
razor-x deleted the claude/python-sdk-audit-o2iid9-10-deep-attr-dict branch August 28, 2026 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants