BaseTracer

Everything you build with Pyccolo is a subclass of BaseTracer. Thanks to its metaclass, the class itself acts as the tracer for most purposes: it is a singleton, it doubles as a context manager, and class-level calls are forwarded to the instance.

class pyccolo.BaseTracer(*args, **kwargs)[source]

Activation & singleton

A tracer class is a traitlets singleton. You rarely instantiate it yourself:

  • MyTracer.instance() — the singleton instance (also pyc.instance() for the active tracer).

  • with MyTracer: — activate for a dynamic scope (the class is a context manager). Equivalent to with MyTracer.instance().tracing_context():.

  • MyTracer.enable() / MyTracer.disable() — activate/deactivate imperatively.

Context managers

BaseTracer.tracing_context(disabled: bool = False, tracing_enabled_file: str | None = None) Generator[None, None, None]
BaseTracer.tracing_enabled(**kwargs) Generator[None, None, None]
BaseTracer.tracing_disabled(**kwargs) Generator[None, None, None]
BaseTracer.tracing_non_context(disabled: bool = False, tracing_enabled_file: str | None = None, do_patch_meta_path: bool | None = None) Callable

Running & compiling code

See Why pyc.exec, and how scoping works for why code defined in the same file as the tracer must be run through these entry points.

BaseTracer.exec(code: str | Module | stmt | Expression, global_env: dict | None = None, local_env: dict | None = None, *, instrument: bool = True, filename: str | None = None, num_extra_lookback_frames: int = 0) Dict[str, Any]
BaseTracer.eval(code: str | expr | Expression, global_env: dict | None = None, local_env: dict | None = None, *, instrument: bool = True, filename: str | None = None, num_extra_lookback_frames: int = 0) Any
BaseTracer.execute(*args, **kwargs)
BaseTracer.parse(code: str, mode='exec', filename: str | None = None, tracers: List[BaseTracer] | None = None, instrument: bool = True) Module | Expression
BaseTracer.transform(code: str, tracers: List[BaseTracer] | None = None, positions: List[Tuple[int, int]] | None = None, pure: bool = False) str | Tuple[str, List[Position]]
BaseTracer.untransform(tree: AST, tracers: List[BaseTracer] | None = None, positions: List[Tuple[int, int]] | None = None, pure: bool = False) str | Tuple[str, List[Position]]
BaseTracer.instrumented(f: Callable, mutate: bool = False) Callable
BaseTracer.trace_lambda(lam: Callable[[...], Any]) Callable[[...], Any]

Re-entrant fragments

For instrumenting code from inside a handler without disturbing tracer state (used by macro-style tracers):

BaseTracer.parse_fragment(code: str, filename: str | None = None, tracers: List[BaseTracer] | None = None) Module | Expression

Parse + instrument a code fragment from within an already active trace (e.g. inside an event handler) without disturbing the enclosing rewrite’s transient state. Returns the instrumented AST; the caller may edit it before running it with exec_raw(..., instrument=False).

Use this (rather than parse/exec) when you need to compile a fragment mid-handler: it neither re-enters the tracing context (which would reset tracer state) nor clobbers an in-flight parse, so nested instrumented constructs in the fragment still dispatch to handlers.

tracers scopes the instrumentation to a subset of the active stack; pass it when a foreign co-tracer should not weave its events into the fragment (e.g. it would not recognize the fragment’s synthetic nodes).

BaseTracer.exec_fragment(code: str | Module, global_env: dict, local_env: dict, filename: str | None = None) dict

Parse, instrument, and run a code fragment from within an active trace, reusing the current tracing context. local_env is mutated with whatever the fragment defines and returned. See parse_fragment().

Overridable hooks

Override these on your subclass to customize behavior. All have safe defaults.

BaseTracer.should_instrument_file(filename: str) bool
BaseTracer.file_passes_filter_for_event(evt: str, filename: str) bool
BaseTracer.enter_tracing_hook() None
BaseTracer.exit_tracing_hook() None
BaseTracer.static_init_module(node: Module) None
BaseTracer.should_propagate_handler_exception(evt: TraceEvent, exc: Exception) bool

Guards

Guards let you switch instrumentation off at runtime for a function or loop once you no longer need it — the key to amortizing overhead.

classmethod BaseTracer.activate_guard(guard: str | int | AST) None
classmethod BaseTracer.deactivate_guard(guard: str | int | AST) None
classmethod BaseTracer.register_local_guard(guard: str) None

Augmentation helpers

Used when defining new syntax.

classmethod BaseTracer.syntax_augmentation_specs() List[AugmentationSpec]
classmethod BaseTracer.get_augmentations(node_id: AST | int) FrozenSet[AugmentationSpec]
BaseTracer.make_ast_rewriter(path: str, module_id: int | None = None, tracers: List[BaseTracer] | None = None) AstRewriter
BaseTracer.make_syntax_augmenter(ast_rewriter: AstRewriter | None) Callable[[CodeLines], CodeLines]
classmethod BaseTracer.make_sandbox_fname() str

AST ancestry

Classmethods for asking where a node sits in the tree — handy inside when= predicates and handlers.

classmethod BaseTracer.is_outer_stmt(node_or_id, exclude_outer_stmt_types=None)[source]
classmethod BaseTracer.is_initial_frame_stmt(node_or_id: int | AST)[source]
classmethod BaseTracer.stmt_only_has_ancestor_types(node_or_id: int | AST, ancestor_types: Tuple[Type[AST], ...])[source]

State

BaseTracer.make_stack()
BaseTracer.reset()

NoopTracer

class pyccolo.NoopTracer(*args, **kwargs)[source]

The default tracer used when the stack is empty: it patches nothing, enables no guards, and instruments no files.