Lib/fileinput.py
cpython 3.14 @ ab2d84fe1023/Lib/fileinput.py
fileinput presents a list of files (or sys.stdin) as a single flat stream of lines. The top-level input() function creates and returns a module-level FileInput instance; subsequent calls to filename(), lineno(), filelineno(), and isfirstline() operate on that shared instance. Callers that prefer explicit lifetime management can construct FileInput directly and use it as a context manager.
The inplace flag turns the module into a simple line-filter harness. When it is set, each input file is renamed to a backup, the original name is reopened as stdout, and the caller's loop writes transformed lines back. The backup parameter controls the backup suffix (default .bak). hook_compressed and hook_encoded are factory functions that return open-hook callables for transparent decompression and encoding, respectively.
FileInput is a lazy iterator. It opens at most one file at a time and advances to the next only when the current one is exhausted. The class tracks per-file line numbers (_filelineno) and a global line counter (_lineno) separately. The fileno() method returns the underlying file descriptor, or -1 when reading from stdin or between files.
Map
| Lines | Symbol | Role | gopy |
|---|---|---|---|
| 1-50 | module header | Docstring, imports, __all__ | - |
| 51-100 | input() + module-level helpers | Creates/returns _state, delegates filename, lineno, filelineno, fileno, isfirstline, isstdin, nextfile, close | - |
| 101-170 | FileInput.__init__ | Validates args, stores file list, mode, hook, inplace/backup settings | - |
| 171-230 | FileInput.__iter__ / __next__ | Drives the line-fetch loop, opens next file when current is exhausted | - |
| 231-290 | FileInput._open | Opens a single file: handles stdin, inplace rename, backup, openhook | - |
| 291-350 | FileInput property methods | filename, fileno, lineno, filelineno, isfirstline, isstdin | - |
| 351-395 | FileInput.nextfile / close | Skip rest of current file, restore stdout for inplace, close file handle | - |
| 396-430 | hook_compressed, hook_encoded | Open-hook factories for gzip/bz2/xz and encoding wrappers | - |
Reading
Module-level input() and helpers (lines 51 to 100)
cpython 3.14 @ ab2d84fe1023/Lib/fileinput.py#L51-100
input() stores the new FileInput in the module-level _state variable and returns it. All the module-level convenience functions (filename(), lineno(), etc.) just proxy to _state, raising RuntimeError if no instance exists. This design allows scripts to use for line in fileinput.input(...) without keeping an explicit reference.
def input(files=None, inplace=False, backup='', *, mode='r', openhook=None,
encoding=None, errors=None):
global _state
if _state and _state._file:
raise RuntimeError("input() already active")
_state = FileInput(files, inplace, backup, mode=mode, openhook=openhook,
encoding=encoding, errors=errors)
return _state
FileInput.__init__ (lines 101 to 170)
cpython 3.14 @ ab2d84fe1023/Lib/fileinput.py#L101-170
The constructor normalises the files argument: a bare string is wrapped in a tuple, None triggers a fallback to sys.argv[1:] or ('-',) for stdin. Incompatible option combinations (e.g. inplace with openhook) raise ValueError early.
def __init__(self, files=None, inplace=False, backup='', *,
mode='r', openhook=None, encoding=None, errors=None):
if isinstance(files, (str, bytes, os.PathLike)):
files = (files,)
elif files is None:
files = sys.argv[1:] or ('-',)
else:
files = tuple(files)
self._files = files
self._inplace = inplace
self._backup = backup
# ... stores mode, openhook, encoding, errors; resets counters
__next__ and file advancement (lines 171 to 230)
cpython 3.14 @ ab2d84fe1023/Lib/fileinput.py#L171-230
__next__ reads from self._file. When that file is exhausted (returns ''), it calls nextfile() to close it and sets self._file = None, then advances self._fileindex and opens the next entry via _open(). The loop continues until a non-empty line is returned or the file list runs out and StopIteration is raised.
def __next__(self):
while True:
line = self._readline()
if line:
self._lineno += 1
self._filelineno += 1
return line
if not self._file:
raise StopIteration
self.nextfile()
# opens next file on the following iteration