Commit Graph
20 Commits
Author SHA1 Message Date
ymwang78Claude Opus 4.7pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
46ebf5031b feat(subinterpreter): reusable PyThreadState via subinterpreter_thread_state (#6073)
* feat(subinterpreter): add opt-in TLS-cached thread state mode

subinterpreter_scoped_activate previously created and destroyed a fresh
PyThreadState on every activation when the calling OS thread was not
already running the target interpreter. Workloads that repeatedly
re-enter the same sub-interpreter from the same thread therefore churn
thread states and lose per-thread interpreter state between activations
(see pybind/pybind11#6040).

Add an opt-in subinterpreter_thread_state::cached policy: on first use a
PyThreadState is created and stored in OS-thread-local storage keyed by
the target interpreter; subsequent activations on that thread only swap
it in/out and never destroy it. The default stays transient, so existing
behavior is unchanged.

Since pybind11 does not control thread lifetime, cleanup is explicit:
subinterpreter::release_cached_thread_state() releases the calling
thread's cached state for one interpreter, and the static
release_all_cached_thread_states() releases all of the calling thread's
cached states as an end-of-thread hook. The TLS map's destructor only
frees its own nodes and never touches the Python C API, so an
unreleased state leaks rather than crashing at thread exit.

Includes test coverage and embedding docs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* style: pre-commit fixes

* refactor(subinterpreter): replace cached enum/TLS with subinterpreter_thread_state RAII

Address review feedback on the original "cached" mode by switching to an
explicit two-RAII design suggested by @b-pass:

  "Create a class ... to RAII-manage the PyThreadState but start its
   lifetime in an already released state. You could create another
   class (or modify scoped_activate) to scoped/RAII activate the
   inactive threadstate."

Removed
  - enum subinterpreter_thread_state { transient, cached } and the
    defaulted ctor parameter on subinterpreter_scoped_activate.
  - detail::subinterpreter_thread_state_cache thread_local map.
  - subinterpreter::release_cached_thread_state() and
    subinterpreter::release_all_cached_thread_states().

This eliminates: the hidden per-thread map, the "release_all" footgun
across pybind11 modules (the cache was module-local), and the implicit
"must not be active when called" contract on the release functions.

Added
  - Public class subinterpreter_thread_state that owns one PyThreadState
    for a given subinterpreter on its constructing OS thread, created in
    a released state (not current, no GIL). Non-copyable, non-movable
    (PyThreadState is bound to its creating OS thread).
  - subinterpreter_scoped_activate(subinterpreter_thread_state &)
    overload: swaps the owned PyThreadState in on entry, swaps it out
    on exit, does not touch its lifetime.

Behavior
  - The existing subinterpreter_scoped_activate(subinterpreter const &)
    overload is unchanged (still transient: New on entry, Delete on
    exit). All previously-working code keeps working.
  - With subinterpreter_thread_state, one OS thread can alternate
    between multiple subinterpreters and each PyThreadState is preserved
    across activations -- the use case that gil_scoped_release/acquire
    + a long-lived scoped_activate cannot solve alone (the per-thread
    internals.tstate slot holds only one inactive tstate).
  - The dtor of subinterpreter_thread_state guards against the
    "destroyed-while-active" contract violation: if Swap reveals the
    cached tstate was current, do not Swap back to a now-deleted
    pointer (the safe-when-active fix b-pass requested for the old
    release_* functions, applied at the natural location instead).

Lifetime contract is enforced by ordinary C++ scope: typical placement
is `thread_local`. No new release/cleanup APIs are required.

Tests cover (a) tstate identity preserved across activations on a
thread, (b) transient and reusing modes do not share state, (c)
different OS threads get distinct PyThreadStates, and (d) the
multi-subinterpreter alternation case.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(subinterpreter): address review on #6073 (same-thread checks, test scoping)

Per @b-pass's review:

- ~subinterpreter_thread_state(): add a PYBIND11_DETAILED_ERROR_MESSAGES-
  guarded check that destruction happens on the OS thread that created the
  PyThreadState (same PyThread_get_thread_native_id pattern as ~subinterpreter),
  failing with pybind11_fail otherwise.
- subinterpreter_scoped_activate(subinterpreter_thread_state &): add the
  matching DETAILED_ERROR_MESSAGES check that activation happens on the
  creating OS thread, enforcing the newly documented rule.
- docs: document that activating a subinterpreter_thread_state on another OS
  thread is illegal.
- tests: keep each subinterpreter (and its subinterpreter_thread_state) in an
  enclosing scope so destruction order is thread-state -> subinterpreter ->
  unsafe_reset_internals_for_single_interpreter(). The previous top-level
  declarations ran the reset while the subinterpreters were still alive, which
  is the likely cause of the CI crashes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: fix codespell (re-used -> reused) in embedding.rst

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-05-25 09:31:14 -04:00
Henry Schreiner e44aae2268 chore: bump CMake max policy to 4.2 (#5944)
* chore: bupm CMake to 4.1

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

* ci: use newest version of CMake in a few places

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

* chore: bupm CMake to 4.2

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

---------

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
2026-01-15 12:38:45 -05:00
Ralf W. Grosse-Kunstleveb-passpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
799f591ec3 Re-enable Move Subinterpreter test for free-threaded Python 3.14 (#5940)
* Remove skip for Move Subinterpreter test on free-threaded Python 3.14+

* Fix deadlock by detaching from the main interpreter before joining the thread.

* style: pre-commit fixes

---------

Co-authored-by: b-pass <b-pass@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-12-22 21:32:41 -08:00
Henry SchreinerandRalf W. Grosse-Kunstleve 1006933415 chore: also use typos (#5931)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Ralf W. Grosse-Kunstleve <rgrossekunst@nvidia.com>
2025-12-13 02:17:08 -08:00
b-passCopilotAaron Gokaslanpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
07d028f218 feat: Embeded sub-interpreters (#5666)
* First draft a subinterpreter embedding API

* Move subinterpreter tests to their own file

* Migrate subinterpreter tests to use the new embedded class.

* Add a test for moving subinterpreters across threads for destruction

And find a better way to make that work.

* Code organization

* Add a test which shows demostrates how gil_scoped interacts with sub-interpreters

* Add documentation for embeded sub-interpreters

* Some additional docs work

* Add some convenience accessors

* Add some docs cross references

* Sync some things that were split out into #5665

* Update subinterpreter docs example to not use the CPython api

* Fix pip test

* style: pre-commit fixes

* Fix MSVC warnings

I am surprised other compilers allowed this code with a deleted move ctor.

* Add some sub-headings to the docs

* Oops, make_unique is C++14 so remove it from the tests.

* I think this fixes the EndInterpreter issues on all versions.

It just has to be ifdef'd because it is slightly broken on 3.12, working well on 3.13, and kind of crashy on 3.14beta.  These two verion ifdefs solve all the issues.

* Add a note about exceptions.

They contain Python object references and acquire the GIL, that means they are a danger with subinterpreters!

* style: pre-commit fixes

* Add try/catch to docs examples to match the tips

* Python 3.12 is very picky about this first PyThreadState

Try special casing the destruction on the same thread.

* style: pre-commit fixes

* Missed a rename in a ifdef block

* I think this test is causing problems in 3.12, so try ifdefing it to see if the problems go away.

* style: pre-commit fixes

* Document the 3.12 constraints with a warning

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* ci: add cpptest to the clang-tidy job

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* noexcept move operations

* Update include/pybind11/subinterpreter.h

std::memset

Co-authored-by: Aaron Gokaslan <aaronGokaslan@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Aaron Gokaslan <aaronGokaslan@gmail.com>
2025-05-20 18:17:48 -04:00
b-passandHenry Schreiner 9afc9c4ffb feat: change PYBIND11_EMBEDDED_MODULE to multiphase init (#5665)
* Move embedded modules to multiphase init

So that they too can support multi-interpreter and nogil tags

* Update the multiple interpreter test for embedded module changes

* Add a note to embedded module docs about the new tags

* Oops, missed a warning pop

* Remove unused variable

* Update ci.yml

* Fix this embedded GIL test for free-threading

* Oops, need to use ptr() here

* This test created a subinterpreter when PYBIND11_SUBINTERPRETER_SUPPORT was off

So the fix is really this test should not be run in these older versions at all.

The hang was a GIL issue between the subinterpreters during pybind11::exception::what().

* fix: standard mutex for 3.13t

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2025-05-18 09:39:26 -04:00
Henry Schreiner d27fdaa272 chore: update for CMake 4.0 (#5593)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-03-31 22:50:40 -04:00
Henry Schreiner 28dbce4157 feat: require CMake 3.15+ (#5304)
* feat: require CMake 3.15+

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Apply suggestions from code review

* Update CMakeLists.txt

* fix: adapt for CMake 3.30+ (using 3.18+)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-08-14 18:21:04 -04:00
Henry Schreiner 7f2214bc68 chore: bump cmake to 3.29 (#5075)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-03-27 20:11:13 -04:00
Pol MesallesandHenry Schreiner daea1130b4 fix(cmake): upgrade maximum supported CMake version to 3.27 (#4786)
* Upgrade maximum supported CMake version to 3.27 to fix warning with CMP0148 policy (#4785)

* Update `macos_brew_install_llvm` pipeline to use expected Python installation

* Fix `Python_EXECUTABLE` Cmake variable typo

* Apply suggestions from code review

* fix: use FindPython for CMake 3.18+ by default for pybind11's tests

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* tests: fix issues with finding Python

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* tests: also set executable on subdir tests

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix(cmake): correct logic for FindPython

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update ci.yml

* Revert "Update ci.yml"

This reverts commit 33798adf3f.

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2023-12-14 14:36:45 -05:00
Henry Schreiner 2e5f5c4cf8 fix: support CMake 3.27, drop 3.4 (#4719)
* fix: support CMake 3.27, drop 3.4

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update upgrade.rst

* Update upgrade.rst

* Update upgrade.rst

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2023-07-12 13:10:24 -05:00
Aaron Gokaslan f4c81e0877 maint: Add additional linter-related pre-commit hooks (#3337)
* Add additional pygrep pre-commit hooks

* Remove useless noqas with hook

* Fix all single rst backticks

* Simplify mypy pre-commit hook with upstream fixes

* Add back missing comment

* Add one last pygrep hook
2021-10-08 08:38:04 -04:00
Aaron Gokaslan 0fb981b219 Add blacken-docs and pycln pre-commit hooks (#3292)
* Apply blacken-docs and fix language-hints

* Add blacken-docs pre-commit hook

* Add pycln pre-commit hook

* Enable a few builtin hooks

* Black no longer ignores pyi files
2021-09-22 15:38:50 -04:00
Henry Schreiner 6bcd220c8d refactor: module -> module_ with typedef (#2544)
* WIP: module -> module_ without typedef

* refactor: allow py::module to work again
2020-10-03 13:38:03 -04:00
Henry Schreiner 1729aae96f feat: new FindPython support (#2370)
* feat: FindPython support

* refactor: rename to PYBIND11_FINDPYTHON

* docs: Caps fixes

* feat: NOPYTHON mode

* test: check simple call

* docs: add changelog/upgrade guide

* feat: Support Python3 and Python2

* refactor: Use targets in tests

* fix: support CMake 3.4+

* feat: classic search also finds virtual environments

* docs: some updates from @wjakob's review

* fix: wrong name for QUIET mode variable, reported by @skoslowski

* refactor: cleaner output messaging

* fix: support debug Python's in FindPython mode too

* fixup! refactor: cleaner output messaging

* fix: missing pybind11_FOUND and pybind11_INCLUDE_DIR restored to subdir mode

* fix: nicer reporting of Python / PyPy

* fix: out-of-order variable fix

* docs: minor last-minute cleanup
2020-08-19 12:26:26 -04:00
Gunnar Läthén c64e6b1670 Added function for reloading module (#1040) 2017-09-12 08:05:05 +02:00
Jason Rhinelander 7437c69500 Add py::module_local() attribute for module-local type bindings
This commit adds a `py::module_local` attribute that lets you confine a
registered type to the module (more technically, the shared object) in
which it is defined, by registering it with:

    py::class_<C>(m, "C", py::module_local())

This will allow the same C++ class `C` to be registered in different
modules with independent sets of class definitions.  On the Python side,
two such types will be completely distinct; on the C++ side, the C++
type resolves to a different Python type in each module.

This applies `py::module_local` automatically to `stl_bind.h` bindings
when the container value type looks like something global: i.e. when it
is a converting type (for example, when binding a `std::vector<int>`),
or when it is a registered type itself bound with `py::module_local`.
This should help resolve potential future conflicts (e.g. if two
completely unrelated modules both try to bind a `std::vector<int>`.
Users can override the automatic selection by adding a
`py::module_local()` or `py::module_local(false)`.

Note that this does mildly break backwards compatibility: bound stl
containers of basic types like `std::vector<int>` cannot be bound in one
module and returned in a different module.  (This can be re-enabled with
`py::module_local(false)` as described above, but with the potential for
eventual load conflicts).
2017-08-04 10:47:34 -04:00
Ian Bell 28f3df7ff3 Fix typo in embedding.rst 2017-06-15 10:37:28 -03:00
Dean Moldovan 8f6c129689 Fix CMake example code in embedding docs
[skip ci]
2017-05-31 13:49:27 +02:00
Dean Moldovan 6d2411f1ac Add tutorial page for embedding the interpreter 2017-05-28 02:12:24 +02:00