{"record":{"id":"e83fe9fe6a63a50c","repo":"python/cpython","slug":"self-raw-should-implement-rawiobase-it-should-not","errorCode":null,"errorMessage":"self.raw should implement RawIOBase: it should not raise BlockingIOError","messagePattern":"self\\.raw should implement RawIOBase: it should not raise BlockingIOError","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"Lib/_pyio.py","lineNumber":1314,"sourceCode":"    def truncate(self, pos=None):\n        with self._write_lock:\n            self._flush_unlocked()\n            if pos is None:\n                pos = self.raw.tell()\n            return self.raw.truncate(pos)\n\n    def flush(self):\n        with self._write_lock:\n            self._flush_unlocked()\n\n    def _flush_unlocked(self):\n        if self.closed:\n            raise ValueError(\"flush on closed file\")\n        while self._write_buf:\n            try:\n                n = self.raw.write(self._write_buf)\n            except BlockingIOError:\n                raise RuntimeError(\"self.raw should implement RawIOBase: it \"\n                                   \"should not raise BlockingIOError\")\n            if n is None:\n                raise BlockingIOError(\n                    errno.EAGAIN,\n                    \"write could not complete without blocking\", 0)\n            if n > len(self._write_buf) or n < 0:\n                raise OSError(\"write() returned incorrect number of bytes\")\n            del self._write_buf[:n]\n\n    def tell(self):\n        return _BufferedIOMixin.tell(self) + len(self._write_buf)\n\n    def seek(self, pos, whence=0):\n        if whence not in valid_seek_flags:\n            raise ValueError(\"invalid whence value\")\n        with self._write_lock:\n            self._flush_unlocked()\n            return _BufferedIOMixin.seek(self, pos, whence)","sourceCodeStart":1296,"sourceCodeEnd":1332,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pyio.py#L1296-L1332","documentation":"Raised by BufferedWriter._flush_unlocked when the underlying raw stream's write() raises BlockingIOError. The buffered layer's contract (RawIOBase) is that raw.write either writes or returns None to indicate blocking — it must not raise BlockingIOError itself. A raw object violating that contract is a programming error in the raw class, so it is surfaced as RuntimeError with the quoted expectation.","triggerScenarios":"Supplying a custom raw stream whose write() raises BlockingIOError('EAGAIN') instead of returning None; wrapping a non-blocking object without honoring the RawIOBase return-None protocol.","commonSituations":"Hand-rolled raw wrappers around sockets/subprocess pipes that propagate BlockingIOError from os.write/send; porting code from a layer (e.g. socketserver) that expects raising semantics into io wrappers.","solutions":["In the custom raw class, catch the blocking condition in write() and return None instead of raising BlockingIOError.","Subclass io.RawIOBase (not IOBase) so the return-value contract is explicit.","If blocking writes are fine, make the fd blocking so os.write never signals EAGAIN."],"exampleFix":"# before\nclass MyRaw(io.RawIOBase):\n    def write(self, b):\n        return os.write(self.fd, b)  # may raise BlockingIOError -> RuntimeError upstream\n\n# after\nclass MyRaw(io.RawIOBase):\n    def write(self, b):\n        try:\n            return os.write(self.fd, b)\n        except BlockingIOError:\n            return None  # RawIOBase protocol: None means 'would block'","handlingStrategy":"type-guard","validationCode":"class ContractCheckingRaw(io.RawIOBase):\n    def write(self, b):\n        try:\n            return super().write(b)\n        except BlockingIOError:\n            return None  # satisfy RawIOBase protocol","typeGuard":"def honors_rawio_contract(raw) -> bool:\n    \"\"\"Can't fully verify statically; check the class contract by inspection.\"\"\"\n    import inspect\n    src = inspect.getsource(type(raw).write)\n    return \"BlockingIOError\" not in src or \"return None\" in src","tryCatchPattern":"try:\n    writer.flush()\nexcept RuntimeError as e:\n    if \"RawIOBase\" in str(e):\n        raise TypeError(\"custom raw stream violates RawIOBase: return None on would-block\") from e\n    raise","preventionTips":["Subclass io.RawIOBase for raw streams used with buffered wrappers.","On would-block, return None from write(), never raise BlockingIOError.","Unit-test custom raw streams against io.BufferedWriter to catch contract breaks."],"tags":["python","io","buffered-writer","runtimeerror","rawiobase","custom-stream","protocol-violation"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}