# V3.14a1: Design limitations of \`PyContext\_AddWatcher\`

**URL:** <https://discuss.python.org/t/v3-14a1-design-limitations-of-pycontext-addwatcher/68177>\
**Category:** C API\
**Created:** [October 17, 2024, 4:01am UTC](https://discuss.python.org/t/v3-14a1-design-limitations-of-pycontext-addwatcher/68177 "2024-10-17T04:01:15Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![rhansen](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rhansen/32/23427_2.png) [@rhansen](https://discuss.python.org/u/rhansen)\
**Post date:** [October 17, 2024, 4:01am UTC](https://discuss.python.org/t/v3-14a1-design-limitations-of-pycontext-addwatcher/68177/1 "2024-10-17T04:01:16Z")

</div>

I have a couple of questions about the design of the new [`PyContext_AddWatcher`](https://docs.python.org/3.14/c-api/contextvars.html#c.PyContext_AddWatcher) API, new in CPython v3.14a1:

- The registered callbacks are [stored in `PyInterpreterState`](https://github.com/python/cpython/blob/a5a7f5e16d8c3938d266703ea8fba8ffee3e3ae5/Include/internal/pycore_interp.h#L242), but the “current context” is per-thread. When there is a context switch, there’s no way to tell which thread was affected. Why isn’t this a per-thread registration? Or at least pass a `PyThreadState` pointer to the callback?
- It is idiomatic in C for a callback registration function to also take an opaque `void *` that will be passed back to the callback. This makes it possible to reuse the same code for multiple simultaneous registrations. (This would be particularly useful if the registrations were per-thread instead of per-interpreter.) Is there a reason it was omitted?

cc @fried

---

<div class="post-metadata">

**Author:** ![fried](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/fried/32/91_2.png) [@fried](https://discuss.python.org/u/fried)\
**Post date:** [October 17, 2024, 3:32pm UTC](https://discuss.python.org/t/v3-14a1-design-limitations-of-pycontext-addwatcher/68177/2 "2024-10-17T15:32:33Z")

</div>

Well the callback is executed on the thread where the switch took place.

As far as a void\* none of the other watcher interfaces have that either. For my purposes all the state I need lives on the context object

---

<div class="post-metadata">

**Author:** ![rhansen](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rhansen/32/23427_2.png) [@rhansen](https://discuss.python.org/u/rhansen)\
**Post date:** [October 17, 2024, 5:29pm UTC](https://discuss.python.org/t/v3-14a1-design-limitations-of-pycontext-addwatcher/68177/3 "2024-10-17T17:29:54Z")

</div>

> [@fried](#):
>
> Well the callback is executed on the thread where the switch took place.

Good point. An earlier version of a [change I proposed](https://github.com/python/cpython/pull/125638) would have made this not always true, but it’s an important property to preserve so I revised the PR.

> [@](#):
>
> As far as a void\* none of the other watcher interfaces have that either. For my purposes all the state I need lives on the context object

Ah, right. Stashing callback state on the context (or even thread-local storage) should be suitably expressive for all use cases, as long as the callback is always called from the affected thread.

Thanks!

---

<div class="post-metadata">

**Author:** ![rhansen](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rhansen/32/23427_2.png) [@rhansen](https://discuss.python.org/u/rhansen)\
**Post date:** [November 12, 2024, 12:08am UTC](https://discuss.python.org/t/v3-14a1-design-limitations-of-pycontext-addwatcher/68177/4 "2024-11-12T00:08:28Z")

</div>

Thinking about this more, getting the watcher config/state from the context can be problematic in a couple of ways:

- The config/state is not guaranteed to be available in the context. For example, the code can do `ctx = contextvars.Context()` instead of `ctx = contextvars.copy_context()`. Or the code can do `copy_context()`, but early during initialization and squirrel the context away for later use. The latter seems plausible for 3rd party libraries (where the user might not have much control).
- The same watcher callback cannot be registered multiple times simultaneously, each with its own config/state. This came up while I was refactoring `TestContextObjectWatchers` in `Lib/test/test_capi/test_watchers.py` to add some more tests for a change I’m working on.

I’m not sure how likely either is to come up in normal use, but it might be worth changing the API before the 3.14 release cements the design.

---

<div class="post-metadata">

**Author:** ![rhansen](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rhansen/32/23427_2.png) [@rhansen](https://discuss.python.org/u/rhansen)\
**Post date:** [November 26, 2024, 2:24am UTC](https://discuss.python.org/t/v3-14a1-design-limitations-of-pycontext-addwatcher/68177/5 "2024-11-26T02:24:10Z")

</div>

FYI: [gh-127124: Change context watcher callback to a callable object by rhansen · Pull Request #127247 · python/cpython · GitHub](https://github.com/python/cpython/pull/127247)
