Skip to content

Add safe context watcher API - #6227

Open
florentinl wants to merge 8 commits into
PyO3:mainfrom
florentinl:florentin.labelle/add-safe-wrappers-for-context-watching-api
Open

Add safe context watcher API#6227
florentinl wants to merge 8 commits into
PyO3:mainfrom
florentinl:florentin.labelle/add-safe-wrappers-for-context-watching-api

Conversation

@florentinl

@florentinl florentinl commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Adds PyContext and safe context-watcher bindings for GIL-enabled CPython 3.14:

  • typed context-switch events
  • RAII watcher registration
  • panic- and error-safe callback trampoline
use pyo3::{context::ContextEvent, prelude::*};

fn context_changed(_py: Python<'_>, event: ContextEvent<'_, '_>) -> PyResult<()> {
    if let ContextEvent::Switched(context) = event {
        println!("current context: {context:?}");
    }
    Ok(())
}

fn main() -> PyResult<()> {
    Python::initialize();

    Python::attach(|py| {
        let _watcher = pyo3::register_context_watcher!(py, context_changed)?;
        py.run(
            c"import contextvars\ncontextvars.Context().run(lambda: None)",
            None,
            None,
        )
    })
}

Tested with unit tests, doctests, and Clippy on Python 3.14.

Comment thread src/context.rs
/// storage. State can still be shared through safe static synchronization primitives.
///
/// Panics and returned [`PyErr`][crate::PyErr] values are reported as unraisable exceptions and
/// never unwind across the C boundary.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we mention this as well?

/// # Thread Safety
///
/// On GIL-enabled builds, callbacks are invoked synchronously. On free-threaded
/// builds (when this API is available), callbacks may be invoked concurrently.
/// Ensure your callback is thread-safe if needed.

@florentinl florentinl Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true, It should definitely be mentioned if we enable the API on free threaded builds.

The problem is that PyContext_AddWatcher and PyContext_ClearWatcher while available on free threaded are not callable concurrently and require external synchronization, which is not something we can guarantee from an extension.

I was thinking of making this API only available on GIL-enabled builds for this reason.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can should requireSend + Sync + 'static on the provided closure.

@florentinl florentinl Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API in this PR accepts a function path, which is coerced to a function pointer. Function pointers carry no captured state and are already Send + Sync + 'static, so there is nothing additional to enforce.

CPython’s watcher API does not expose a user-data pointer where we could store a closure, which is why I did not try to implement a closure registration api.

Comment thread src/context.rs
@florentinl
florentinl force-pushed the florentin.labelle/add-safe-wrappers-for-context-watching-api branch from eb0dff3 to 66ad857 Compare July 28, 2026 07:47
@florentinl
florentinl marked this pull request as ready for review July 28, 2026 08:19
@florentinl
florentinl requested a review from chirizxc July 28, 2026 08:19
Comment thread src/context.rs
@florentinl
florentinl force-pushed the florentin.labelle/add-safe-wrappers-for-context-watching-api branch from 7259b06 to 69b141c Compare July 28, 2026 10:40
@florentinl
florentinl requested a review from chirizxc July 28, 2026 11:20
@chirizxc

Copy link
Copy Markdown
Contributor

LGTM, but im not a maintainer

@florentinl

Copy link
Copy Markdown
Contributor Author

Still, thank you for taking a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants