Skip to content
Python Basics: Track Line Origins Across Several Files

Python Basics: Track Line Origins Across Several Files

October 28, 2026

When combining small reports, keep both the overall row number and the original file location. FileInput streams an explicit file list while exposing those two coordinates.

Prepare a scratch directory

Tested on Linux with CPython 3.12.14. You need Bash, a text editor, the named runtime, and standard mktemp, rm, and rmdir utilities. Run Python without -O so assertions remain enabled. The examples use only controlled local data; generated fixture directories are cleaned automatically.

Keep the same terminal open. Stop if setup fails, before saving the source:

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

Run the example

Save this as demonstrate-fileinput.py:

import fileinput
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    first = Path(directory) / "east.txt"
    second = Path(directory) / "west.txt"
    first.write_text("apple\npear\n", encoding="utf-8")
    second.write_text("plum\n", encoding="utf-8")
    seen = []
    with fileinput.FileInput(files=[first, second], encoding="utf-8") as lines:
        for line in lines:
            name = Path(lines.filename()).name
            value = line.rstrip("\n")
            row = (lines.lineno(), name, lines.filelineno(), value)
            seen.append(row)
            print(*row, sep=" | ")
    assert seen == [(1, "east.txt", 1, "apple"),
                    (2, "east.txt", 2, "pear"),
                    (3, "west.txt", 1, "plum")]

Run it:

python3 -B demonstrate-fileinput.py

Observed output:

1 | east.txt | 1 | apple
2 | east.txt | 2 | pear
3 | west.txt | 1 | plum

Read the result

lineno continues across files, while filelineno starts again at one for the second file. Passing files explicitly avoids interpreting the program command line as an input list. The context manager closes the active stream.

Apply it carefully

This reads files without modifying them. Do not enable inplace merely to add labels: it redirects output into replacement files. A dash in the file list means standard input, so validate a user-supplied list if that behavior is unwanted. Removing only the newline preserves meaningful spaces.

The official reference describes the behavior and options. These checks cover the fixture above rather than every input or platform.

Clean up

Remove the saved source, return to the starting directory, then remove the empty practice directory:

rm -- demonstrate-fileinput.py
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

If the directory is not empty, inspect its remaining contents rather than deleting unknown files.

Acknowledgements

This post was created with assistance from AI.

Last updated on