Instrumenting imported modules
By default, only code you explicitly hand to pyccolo.exec() /
pyccolo.eval() is instrumented. Instrumentation is opt-in for modules
imported within a tracing context, and a tracer decides which files to
instrument by overriding should_instrument_file():
import pyccolo as pyc
class MyTracer(pyc.BaseTracer):
def should_instrument_file(self, filename: str) -> bool:
return filename.endswith("foo.py")
# handlers, etc. defined below
...
with MyTracer:
import foo # contents of `foo` get instrumented
import bar # contents of `bar` do not
The method is called with the candidate module’s filename and returns whether it
should be rewritten. (To instrument everything imported inside the context,
set the class attribute instrument_all_files = True instead of overriding the
method.)
How it works
Imports are instrumented by registering a custom finder / loader on
sys.meta_path. This loader:
ignores cached bytecode, which may be uninstrumented (an already-compiled
.pycwould otherwise be used verbatim, skipping the rewrite); andavoids generating new cached bytecode, which would be instrumented — writing it to disk could cause confusion later, when instrumentation is not desired.
The finder/loader is installed and removed with the tracing context, so ordinary
imports outside the with block behave exactly as they always do.
A complete example — lazy imports that resolve on first use — ships as pyccolo/examples/lazy_imports.py (see the Example gallery).