{"record":{"id":"793bfc43f888a086","repo":"PyO3/pyo3","slug":"frozenset-should-always-be-iterable","errorCode":null,"errorMessage":"frozenset should always be iterable","messagePattern":"frozenset should always be iterable","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"info","filePath":"src/types/frozenset.rs","lineNumber":227,"sourceCode":"    }\n}\n\nimpl<'py> IntoIterator for &Bound<'py, PyFrozenSet> {\n    type Item = Bound<'py, PyAny>;\n    type IntoIter = BoundFrozenSetIterator<'py>;\n\n    /// Returns an iterator of values in this set.\n    fn into_iter(self) -> Self::IntoIter {\n        self.iter()\n    }\n}\n\n/// PyO3 implementation of an iterator for a Python `frozenset` object.\npub struct BoundFrozenSetIterator<'py>(Bound<'py, PyIterator>);\n\nimpl<'py> BoundFrozenSetIterator<'py> {\n    pub(super) fn new(set: Bound<'py, PyFrozenSet>) -> Self {\n        Self(PyIterator::from_object(&set).expect(\"frozenset should always be iterable\"))\n    }\n}\n\nimpl<'py> Iterator for BoundFrozenSetIterator<'py> {\n    type Item = Bound<'py, super::PyAny>;\n\n    /// Advances the iterator and returns the next value.\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0\n            .next()\n            .map(|result| result.expect(\"frozenset iteration should be infallible\"))\n    }\n\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        let len = ExactSizeIterator::len(self);\n        (len, Some(len))\n    }\n","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/src/types/frozenset.rs#L209-L245","documentation":"PyO3 wraps a `frozenset` iterator as `BoundFrozenSetIterator`, constructing a `PyIterator` from the set. The `.expect(\"frozenset should always be iterable\")` asserts a CPython invariant: frozensets are always iterable, so `PyIterator::from_object` should never fail. Reaching this panic means an object masquerading as, or corrupting, a `frozenset` was passed, or the type object's `tp_iter` was patched.","triggerScenarios":"Passing an object whose type claims to be a frozenset (subclass or mocked `Py_TYPE` swap) but whose iterator cannot be created; monkeypatching `frozenset` type slots; memory corruption at interpreter teardown.","commonSituations":"Test doubles / mocks that impersonate frozensets; C-API-level tampering with builtin types (some exotic runtime patches); essentially unreachable in normal code — mostly encountered in corrupted environments.","solutions":["Verify with `isinstance(x, frozenset)` (not just a type-pointer match) before passing objects where the panic occurs","Remove any monkeypatching of builtin frozenset types/slots","Upgrade pyo3 and reproduce with a minimal snippet; report to PyO3 if hit in vanilla CPython","Check for C extension corruption or mixed Python runtimes in the process"],"exampleFix":"# before\nfake = Mock(spec=frozenset)  # spec is not a real frozenset\nrust_iter_fn(fake)\n# after\nreal = frozenset(fake)\nrust_iter_fn(real)","handlingStrategy":"type-guard","validationCode":"# Python: pass real frozensets only\nisinstance(value, frozenset) or raise TypeError('expected frozenset')","typeGuard":"def is_real_frozenset(value) -> bool:\n    return type(value) is frozenset","tryCatchPattern":null,"preventionTips":["Use type(x) is frozenset for strict checks into Rust APIs","Never mock/patch builtin frozenset types","Convert with frozenset(value) before crossing into Rust","Report any reproduction on vanilla CPython to PyO3"],"tags":["rust","pyo3","frozenset","iterator","invariant"],"backgroundTag":"frozenset-not-iterable-panic","analyzedSha":"ac9b6899d348be4d54614d060dea53a645a12e36","analyzedAt":"2026-09-05T09:20:35.319Z","contentChangedAt":"2026-09-05T09:20:35.319Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}