Python linecache: Read Lines by Number

Published on: August 7, 2026
Reading time: 7 minutes
Code editor with numbered lines representing the Python linecache module

Debuggers, exception formatters, static analyzers, editors, and observability systems often need to retrieve one exact source line without repeatedly opening and parsing the same file. The Python linecache module provides this infrastructure. It accepts a filename and a one-based line number, returns the corresponding text, and keeps source data in an internal cache to make repeated lookups efficient.

This guide explains getline(), cache invalidation with checkcache(), memory cleanup with clearcache(), encoding detection, import loaders, frozen modules, and safe integration with diagnostic tools. It complements our guides to Python traceback, tokenize, inspect, pathlib, and filecmp.

What linecache solves

A direct implementation might open a file, read every line, and select one index:

from pathlib import Path

lines = Path("app.py").read_text(encoding="utf-8").splitlines(True)
line = lines[24]

This is reasonable for a single operation, but it repeats work when many requests target the same source. It also requires decisions about encodings, missing files, invalid line numbers, and modules whose source is provided by an import loader rather than a normal path. Linecache centralizes these behaviors and is used by the standard traceback module to display source code around exceptions.

Basic getline() usage

The main function is linecache.getline(filename, lineno).

import linecache

text = linecache.getline("app.py", 10)
print(text, end="")

Line numbers start at 1, matching editors and tracebacks. When a line is found, the returned string normally includes its terminating newline. Using end="" prevents print() from adding a second one.

Error behavior

getline() intentionally avoids raising common file-reading exceptions. A missing file, an out-of-range line, or an unreadable source normally produces an empty string.

line = linecache.getline("missing.py", 3)
if line == "":
    print("source line unavailable")

This behavior is ideal for diagnostics: failure to retrieve source should not replace the original exception. Applications that need to distinguish permission errors, missing paths, and invalid encodings should validate the path separately or use direct file access.

Blank line versus failure

A real blank line containing only a newline is returned as "\n". Failure is returned as "".

if line == "":
    print("not found")
elif line == "\n":
    print("blank source line")

This distinction is useful, but linecache is not a full filesystem auditing API. It is designed for tolerant source retrieval.

How caching helps

After source is loaded, additional lookups for that file can reuse cached data.

for number in range(100, 111):
    print(linecache.getline("large_module.py", number), end="")

This is efficient when showing several lines around an exception, producing code previews, or resolving many references to one module during a process.

Stale cache entries

If a file changes after the first read, cached lines may still describe the older version. Call checkcache() before a lookup when freshness matters.

linecache.checkcache("app.py")
current = linecache.getline("app.py", 10)

The function checks file metadata and discards entries that no longer appear valid. A later getline() call reloads the source.

Checking all cached files

Calling checkcache() without a filename validates all cache entries.

linecache.checkcache()

This can be appropriate in a long-running development tool that watches many files. In large servers, targeted checks are usually cheaper and more predictable.

Clearing the cache

clearcache() discards all stored source lines.

linecache.clearcache()

Use it after a large batch, between isolated tests, or when cached files are no longer useful. It does not delete files and does not unload imported modules.

Source encoding

Linecache opens Python source through tokenize.open(). That helper follows Python source-encoding rules and uses UTF-8 when no encoding declaration is present.

# -*- coding: latin-1 -*-
message = "hello"

The official linecache documentation states that encoding detection relies on tokenize.detect_encoding(). For arbitrary non-Python text, direct access with an application-defined encoding may be clearer.

Traceback integration

Traceback objects contain filenames and line numbers. Linecache supplies the source text included in formatted reports.

import traceback

try:
    result = 10 / 0
except ZeroDivisionError:
    print(traceback.format_exc())

Custom diagnostic systems can use the same mechanism to show nearby context.

def source_context(filename, lineno, radius=2):
    start = max(1, lineno - radius)
    end = lineno + radius
    return [
        (number, linecache.getline(filename, number))
        for number in range(start, end + 1)
    ]

Invalid line numbers

Zero, negative values, and numbers beyond the end of the file return an empty string.

assert linecache.getline("app.py", 0) == ""
assert linecache.getline("app.py", -1) == ""

Validate values received by an API so callers get a useful message rather than a silent empty result.

Relative paths

If a relative filename is not found directly, linecache may search entries from sys.path.

line = linecache.getline("my_package/module.py", 5)

Application code is usually more predictable with absolute paths. Resolve a known base directory before querying source outside the import system.

Loader-provided source

Some modules do not correspond to ordinary files. An import loader can expose get_source(). When module_globals is provided and contains a compatible loader, linecache can request source through that interface.

line = linecache.getline(
    virtual_name,
    12,
    module_globals=module.__dict__,
)

This supports custom importers, archive-based packages, generated modules, and specialized runtimes.

lazycache()

lazycache(filename, module_globals) stores enough loader information to retrieve source later without performing immediate I/O and without retaining the complete globals mapping.

linecache.lazycache(virtual_name, module.__dict__)
# Later:
text = linecache.getline(virtual_name, 20)

This is useful when one component knows the module context but another component will request source lines later.

Frozen modules in Python 3.14

Python 3.14 added support for names beginning with <frozen . When module globals provide __file__, linecache can attempt to locate the real source path.

line = linecache.getline(
    "<frozen my_module>",
    8,
    module_globals=module_globals,
)

This improves diagnostics in frozen and embedded environments where the traceback filename is not a conventional path.

Generated code

When code is compiled with a virtual filename, linecache does not automatically know the original text.

source = "def calculate():\n    return 42\n"
code = compile(source, "<generated>", "exec")

Frameworks that want complete tracebacks can provide a loader or carefully manage source entries. Modifying the private linecache.cache structure directly ties code to implementation details and should be isolated behind a tested adapter.

Security for user-supplied paths

Never expose arbitrary server files through a “show source line” endpoint. Source trees, configuration files, keys, and credentials may be readable by the process.

from pathlib import Path

BASE = Path("/srv/app/source").resolve()
requested = (BASE / name).resolve()
if requested != BASE and BASE not in requested.parents:
    raise ValueError("path outside allowed source directory")

Also enforce authentication, authorization, extension allowlists, and response redaction.

A lexical check for .. is insufficient when symlinks are involved. Resolve both the base directory and requested path before comparing them. In high-security environments, filesystem race conditions require stronger platform-specific controls.

Concurrency and consistency

Linecache does not provide a transaction covering file changes, validation, and retrieval. A source file may change between checkcache() and getline().

If exact consistency is required, read the file once and operate on a local snapshot. Linecache prioritizes convenient diagnostics rather than immutable views.

Memory use

A process that touches many large source files can retain more memory than expected. Call clearcache() after one-off analysis jobs. For a long-running service, consider whether direct streaming or a bounded application cache better matches the workload.

Testing invalidation

Temporary directories make cache behavior easy to test.

def test_updated_line(tmp_path):
    path = tmp_path / "example.py"
    path.write_text("a = 1\n", encoding="utf-8")
    assert linecache.getline(str(path), 1) == "a = 1\n"

    path.write_text("a = 2\n", encoding="utf-8")
    linecache.checkcache(str(path))
    assert linecache.getline(str(path), 1) == "a = 2\n"

    linecache.clearcache()

Always clean shared cache state so tests do not influence each other.

Linecache versus direct reading

Choose linecache for repeated random access by line number, especially in tracebacks and analysis tools. Choose open() or Path.read_text() when processing an entire file, reporting detailed I/O errors, applying locks, or using custom text-decoding rules.

Using it in editors and linters

A linter can store only filename, line number, and column for each diagnostic, then retrieve text when rendering the final report. This avoids attaching duplicate source strings to thousands of findings. After an editor saves a file, the integration should call checkcache(filename) before displaying updated diagnostics.

Using it in observability systems

Production error reports may include source snippets, but deployments often run code different from the developer workstation. Store version identifiers and avoid assuming that current disk contents match the code that produced an old exception. For durable reports, capture a controlled snippet at error time or link the stack trace to the exact source revision.

Common mistakes

  • Using a zero-based index instead of a one-based line number.
  • Treating an empty string as a real blank line.
  • Expecting automatic refresh after files change.
  • Keeping thousands of source files cached indefinitely.
  • Depending on the process working directory.
  • Exposing arbitrary paths through a web endpoint.
  • Assuming every module has a physical source file.
  • Depending directly on private cache representation.

Best practices

  • Use getline() for tolerant line retrieval.
  • Validate external paths and line numbers.
  • Call checkcache(filename) after known changes.
  • Clear the cache after large one-off jobs.
  • Pass module globals for loader-backed sources.
  • Handle the empty-string result explicitly.
  • Use snapshots when exact consistency matters.
  • Test virtual modules and supported Python versions.

Conclusion

The Python linecache module is a small but important part of the language’s diagnostic infrastructure. It retrieves source lines by number, follows Python encoding rules, reuses cached content, and cooperates with import loaders and frozen modules.

Used carefully, it simplifies tracebacks, code previews, linters, and debugging tools. Remember that its API is intentionally tolerant: invalidate stale entries, validate external input, manage memory in long-running processes, and switch to direct file access when detailed errors or strict consistency are required.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Document and inbox representing local email stores with Python mailbox
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python mailbox: Local Email Stores

    Learn Python mailbox to read, create, and migrate Maildir, mbox, and MH stores with locking, flags, message parsing, and safe

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026
    Text editor representing formatting with Python textwrap
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python textwrap: Format Text

    Learn Python textwrap to wrap, fill, shorten, indent, and dedent text with controlled width, whitespace, long words, and reusable settings.

    Ler mais

    Tempo de leitura: 4 minutos
    10/08/2026
    Folder and magnifying glass representing file-name filters with Python fnmatch
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python fnmatch: Filter File Names

    Learn Python fnmatch to filter file names with shell wildcards, control case sensitivity, exclude patterns, and distinguish glob from regex.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Monitor with binary data representing compact numeric arrays in Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python array: Compact Numeric Data

    Learn Python array for compact numeric storage, binary files, byte order, memory views, and safe buffer interoperability.

    Ler mais

    Tempo de leitura: 6 minutos
    10/08/2026
    Color wheel representing RGB, HSV, and HLS conversions with Python colorsys
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python colorsys: RGB, HSV, and HLS

    Learn Python colorsys to convert colors between RGB, HSV, HLS, and YIQ, generate palettes, and avoid scale and precision mistakes.

    Ler mais

    Tempo de leitura: 5 minutos
    09/08/2026
    Configuration icon representing plist files with Python plistlib
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    plistlib: Read and Write Apple Plist Files

    Learn Python plistlib to read and write XML and binary plist files, validate data, handle dates, bytes, and UIDs safely.

    Ler mais

    Tempo de leitura: 6 minutos
    08/08/2026