FunctionWatcher? Like PyCode_AddWatcher, but for functions

The AddWatcher API for code objects is nice but there are two things I’m trying to do that currently can only be done with real function objects:

  1. Get source code, e.g. inspect.getsource(f) only works on actual function objects. Code objects store a filename and line number, but only the starting line number, so reconstructing it would involve hackiness like trying to parse indentation levels from that starting line number to try to find where the scope ends, and there are lambdas to worry about.
  2. Patch the func.__globals__ dictionary (code objects don’t have one) to monitor global accesses by select functions.

There are potentially multiple ways to deal with these.

  1. FunctionWatcher API that would be just like the code one, but for function objects.
  2. Patching the interpreter to have code objects keep a strong reference to the first function object that ever uses them (probably fine for my purposes but I think this is less likely to make it upstream).
  3. Changing the code objects to also store the ending line number, and adding a sys.monitoring API for global accesses.

Curious if the community has any input on what approach would be best.

Edit: updated the title to refer to exact API function

What exactly is this CodeWatcher API?

1 Like

I misremembered that camelcase, the functions are AddWatcher not AddCodeWatcher, but it’s an API for being notified of the creation of code objects, snippet from code.h:

/*
 * A callback that is invoked for different events in a code object's lifecycle.
 *
 * The callback is invoked with a borrowed reference to co, after it is
 * created and before it is destroyed.
 *
 * If the callback sets an exception, it must return -1. Otherwise
 * it should return 0.
 */
typedef int (*PyCode_WatchCallback)(
  PyCodeEvent event,
  PyCodeObject* co);

/*
 * Register a per-interpreter callback that will be invoked for code object
 * lifecycle events.
 *
 * Returns a handle that may be passed to PyCode_ClearWatcher on success,
 * or -1 and sets an error if no more handles are available.
 */
PyAPI_FUNC(int) PyCode_AddWatcher(PyCode_WatchCallback callback);

/*
 * Clear the watcher associated with the watcher_id handle.
 *
 * Returns 0 on success or -1 if no watcher exists for the provided id.
 */
PyAPI_FUNC(int) PyCode_ClearWatcher(int watcher_id);

Did you check if the PyFunction_AddWatcher API works for your use case?

3 Likes

Oh wow I didn’t expect it to already exist, I think my checkout is 3.11, I will take a look thanks

2 Likes