Skip to content

Fix Attachment.__getattr__ recursion and copy/pickle support - #329

Open
Flerpharos wants to merge 1 commit into
mainfrom
fix/320-attachment-getattr-recursion
Open

Fix Attachment.__getattr__ recursion and copy/pickle support#329
Flerpharos wants to merge 1 commit into
mainfrom
fix/320-attachment-getattr-recursion

Conversation

@Flerpharos

Copy link
Copy Markdown
Collaborator

Fixes #320

Summary

  • Attachment.__getattr__ read self._backing via getattr, which re-entered __getattr__ and recursed to RecursionError whenever _backing wasn't yet set (partially-constructed instance, or an instance created by copy/deepcopy/pickle bypassing __init__).
  • Additionally, once the primary recursion guard was in place, __getattr__ was still delegating dunder lookups (e.g. __getstate__, __reduce_ex__) to the backing stream. That hijacked copy/pickle's own reduction protocol with the backing BytesIO's state instead of Attachment's, so copy.copy/copy.deepcopy/pickle still failed (with a ValueError, not a RecursionError). Excluding dunder names from delegation fixes this; nothing in the codebase relies on dunder delegation through Attachment.

Test plan

  • uv run pytest --ignore=tests/test_integration.py -q (434 passed)
  • uv run ruff check .
  • New tests cover: AttributeError on missing _backing, and successful copy.copy, copy.deepcopy, and pickle.loads(pickle.dumps(...)) round-trips.

…tion

_backing is looked up via getattr() itself, so __getattr__ recursed
infinitely whenever _backing wasn't yet in __dict__ (copy, deepcopy,
pickle, or a partially-constructed instance). Delegating dunder names
(e.g. __getstate__) to the backing stream also hijacked copy/pickle's
own reduction protocol. Guard both cases and raise AttributeError.
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.

Attachment cannot be copied or pickled (__getattr__ recurses on missing _backing)

1 participant