{"record":{"id":"d6573254d6dfa459","repo":"pytest-dev/pytest","slug":"redirected-stdin-is-pseudofile-has-no-fileno","errorCode":null,"errorMessage":"redirected stdin is pseudofile, has no fileno()","messagePattern":"redirected stdin is pseudofile, has no fileno\\(\\)","errorType":"exception","errorClass":"UnsupportedOperation","httpStatus":null,"severity":"error","filePath":"src/_pytest/capture.py","lineNumber":247,"sourceCode":"        raise OSError(\n            \"pytest: reading from stdin while output is captured!  Consider using `-s`.\"\n        )\n\n    readline = read\n\n    def __next__(self) -> str:\n        return self.readline()\n\n    def readlines(self, hint: int | None = -1) -> list[str]:\n        raise OSError(\n            \"pytest: reading from stdin while output is captured!  Consider using `-s`.\"\n        )\n\n    def __iter__(self) -> Iterator[str]:\n        return self\n\n    def fileno(self) -> int:\n        raise UnsupportedOperation(\"redirected stdin is pseudofile, has no fileno()\")\n\n    def flush(self) -> None:\n        raise UnsupportedOperation(\"redirected stdin is pseudofile, has no flush()\")\n\n    def isatty(self) -> bool:\n        return False\n\n    def close(self) -> None:\n        pass\n\n    def readable(self) -> bool:\n        return False\n\n    def seek(self, offset: int, whence: int = 0) -> int:\n        raise UnsupportedOperation(\"redirected stdin is pseudofile, has no seek(int)\")\n\n    def seekable(self) -> bool:\n        return False","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/capture.py#L229-L265","documentation":"Raised by DontReadFromInput.fileno() when code attempts to obtain the underlying OS file descriptor of the captured stdin. The captured stdin is an in-memory pseudofile with no real fd, so fileno() raises UnsupportedOperation. This commonly surfaces inside libraries that probe fileno() to decide whether to use low-level I/O (e.g. selectors, readline).","triggerScenarios":"Code calls sys.stdin.fileno(); a dependency (e.g. prompt_toolkit, readline, selectors) probes for a real fd; passing captured stdin to a function expecting a raw fd.","commonSituations":"Libraries that require a TTY/fd for interactivity; CI runs where capture is on and the library still probes fileno().","solutions":["Run pytest with -s so sys.stdin is the real stream.","Monkeypatch sys.stdin to a real file-like object that has a fileno() (e.g. open('/dev/null')).","Guard code under test with hasattr(sys.stdin, 'fileno') and a try/except."],"exampleFix":"// before\nfd = sys.stdin.fileno()  # raises under capture\n// after\nmonkeypatch.setattr('sys.stdin', open(os.devnull))\nfd = sys.stdin.fileno()","handlingStrategy":"try-catch","validationCode":"def safe_fileno(stream):\n    try:\n        return stream.fileno()\n    except (OSError, ValueError):\n        return None","typeGuard":"def has_real_fileno(stream) -> bool:\n    fn = getattr(stream, 'fileno', None)\n    if fn is None:\n        return False\n    try:\n        fn(); return True\n    except Exception:\n        return False","tryCatchPattern":"try:\n    fd = sys.stdin.fileno()\nexcept (OSError, ValueError) as e:\n    if 'pseudofile' in str(e):\n        fd = None  # fall back to non-fd path","preventionTips":["Probe fileno() defensively when libraries may run under pytest capture.","Run with -s for tests that genuinely need a real fd."],"tags":["capture","stdin","fileno","io"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}