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

    Binary data representing internal serialization with Python marshal
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python marshal: Internal Serialization

    Learn Python marshal to serialize internal types, control format versions, and reject code objects when they are unnecessary.

    Ler mais

    Tempo de leitura: 6 minutos
    06/08/2026
    Monitor with binary code representing pickle customization with Python copyreg
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python copyreg: Customize Pickle

    Learn Python copyreg to register reduction functions, customize pickle, and preserve object compatibility across versions.

    Ler mais

    Tempo de leitura: 5 minutos
    06/08/2026
    Python code representing specialized functions with functools.partial
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python functools.partial: Practical Guide

    Learn Python functools.partial to bind arguments, adapt callbacks, and create clear specialized functions.

    Ler mais

    Tempo de leitura: 5 minutos
    06/08/2026
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python filecmp: Compare Files and Folders

    Learn Python filecmp to compare files and folders using shallow checks, dircmp, cmpfiles, cache handling, and integrity hashes.

    Ler mais

    Tempo de leitura: 5 minutos
    03/08/2026
    Command terminal representing safe parsing with Python shlex
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python shlex: Parse Commands Safely

    Learn Python shlex to split command lines, handle quotes, use quote and join, build mini-languages, and reduce shell injection risks.

    Ler mais

    Tempo de leitura: 6 minutos
    02/08/2026
    Local database representing persistence with Python shelve
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python shelve: Simple Persistence

    Learn Python shelve for simple object persistence, mutable updates, pickle security, concurrency limits, and SQLite migration.

    Ler mais

    Tempo de leitura: 5 minutos
    01/08/2026