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.
Symlinks and resolved paths
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.






