Skip to content

Support matrix

Source recovery depends on the function's source existing somewhere inspect can find it. That is a property of the execution context, and no library can change it. This page is the contract: eighteen contexts, measured across CPython 3.10 through 3.14, with what redef does in each.

Contexts that work

Context Notes
module imported from a file the baseline
python script.py
python -OO script.py future flags and optimization inherited
module imported from a zip
python -c "..." 3.13+ only; earlier versions have no source
exec + a hand-installed linecache entry how notebooks stay readable
linecache._register_code + a real module 3.13+, the supported registration path
IPython / Jupyter cell
nested function dedent + offset repairs applied
method in a class

Contexts that raise, and what the error says

Every failure raises SourceUnavailable with a reason and, where one exists, a remedy:

Context reason Remedy
sourceless .pyc (frozen apps: PyInstaller, Nuitka) frozen-or-sourceless none; state it in your tool's docs
python < script.py stdin none
plain REPL (code.InteractiveConsole) repl none
plain exec into a bare dict bare-exec see below
linecache._register_code into a bare namespace bare-exec see below

The bare-exec remedy, verbatim from the error message: exec into a namespace registered in sys.modules and register the source with linecache. Both halves are required, because inspect.findsource consults the registry only when the function's globals belong to a real module:

import linecache, sys, types

mod = types.ModuleType("generated")
sys.modules["generated"] = mod
code = compile(src, "<generated>", "exec")
linecache.cache["<generated>"] = (len(src), None, src.splitlines(True), "<generated>")
exec(code, mod.__dict__)

Refusals that are not about availability

Situation Error Why
lambda NotAFunction getsourcelines returns the physical line; two lambdas on one line are indistinguishable
closure, at rewrite time ClosureUnsupported recompiling a lone def turns free variables into global lookups that fail at call time
function carrying __wrapped__ InnermostViolation a wrapper below the rewrite means the source no longer describes the object
stale source (wrong name) SourceMismatch a linecache entry outlived its cell; the text defines a different function
stale source (same name, changed body) SourceMismatch caught by verification, invisible to a name check

recover() alone accepts closures (it reports freevars in its metadata and verification compiles them in a synthetic enclosing scope); only recompilation refuses them. Acquisition-only tools, ones that analyze the tree without re-defining the function, can therefore use recover on closures freely, self-recursive nested functions included.

Reading the matrix honestly

The envelope is 10 of 18 on CPython 3.14 and smaller on 3.10 through 3.12 (python -c and the registration contexts arrive at 3.13). If your tool decorates functions, this is your tool's envelope too, whoever implements the pipeline. What redef adds at the edges is that every cell outside the envelope is a named error instead of an OSError, a wrong function, or a NameError three calls later.