mirror of
https://github.com/pybind/pybind11.git
synced 2026-05-17 03:20:18 +00:00
Merge tag 'v3.0.4' into stable
v3.0.4 release
This commit is contained in:
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@@ -188,7 +188,7 @@ jobs:
|
||||
allow-prereleases: true
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
uses: astral-sh/setup-uv@v8.0.0
|
||||
with:
|
||||
enable-cache: true
|
||||
|
||||
@@ -302,7 +302,7 @@ jobs:
|
||||
debug: ${{ matrix.python-debug }}
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v2.1
|
||||
uses: jwlawson/actions-setup-cmake@v2.2
|
||||
|
||||
- name: Valgrind cache
|
||||
if: matrix.valgrind
|
||||
@@ -570,7 +570,7 @@ jobs:
|
||||
run: python3 -m pip install --upgrade pip
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v2.1
|
||||
uses: jwlawson/actions-setup-cmake@v2.2
|
||||
|
||||
- name: Configure
|
||||
shell: bash
|
||||
@@ -906,7 +906,7 @@ jobs:
|
||||
${{ matrix.python == '3.13' && runner.os == 'Windows' }}
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v2.1
|
||||
uses: jwlawson/actions-setup-cmake@v2.2
|
||||
|
||||
- name: Prepare MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
@@ -956,7 +956,7 @@ jobs:
|
||||
architecture: x86
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v2.1
|
||||
uses: jwlawson/actions-setup-cmake@v2.2
|
||||
|
||||
- name: Prepare MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
@@ -1007,7 +1007,7 @@ jobs:
|
||||
run: python3 -m pip install -r tests/requirements.txt
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v2.1
|
||||
uses: jwlawson/actions-setup-cmake@v2.2
|
||||
|
||||
- name: Configure C++20
|
||||
run: >
|
||||
@@ -1189,7 +1189,7 @@ jobs:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v2.1
|
||||
uses: jwlawson/actions-setup-cmake@v2.2
|
||||
|
||||
- name: Install ninja-build tool
|
||||
uses: seanmiddleditch/gha-setup-ninja@v6
|
||||
|
||||
4
.github/workflows/configure.yml
vendored
4
.github/workflows/configure.yml
vendored
@@ -56,7 +56,7 @@ jobs:
|
||||
python-version: 3.11
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
uses: astral-sh/setup-uv@v8.0.0
|
||||
|
||||
- name: Prepare env
|
||||
run: uv pip install --python=python --system -r tests/requirements.txt
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
# An action for adding a specific version of CMake:
|
||||
# https://github.com/jwlawson/actions-setup-cmake
|
||||
- name: Setup CMake ${{ matrix.cmake }}
|
||||
uses: jwlawson/actions-setup-cmake@v2.1
|
||||
uses: jwlawson/actions-setup-cmake@v2.2
|
||||
with:
|
||||
cmake-version: ${{ matrix.cmake }}
|
||||
|
||||
|
||||
2
.github/workflows/nightlies.yml
vendored
2
.github/workflows/nightlies.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
uses: astral-sh/setup-uv@v8.0.0
|
||||
|
||||
- name: Build SDist and wheels
|
||||
run: |
|
||||
|
||||
4
.github/workflows/pip.yml
vendored
4
.github/workflows/pip.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
python-version: 3.8
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
uses: astral-sh/setup-uv@v8.0.0
|
||||
|
||||
- name: Prepare env
|
||||
run: uv pip install --system -r tests/requirements.txt
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
python-version: 3.8
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
uses: astral-sh/setup-uv@v8.0.0
|
||||
|
||||
- name: Prepare env
|
||||
run: uv pip install --system -r tests/requirements.txt twine nox
|
||||
|
||||
2
.github/workflows/reusable-standard.yml
vendored
2
.github/workflows/reusable-standard.yml
vendored
@@ -51,7 +51,7 @@ jobs:
|
||||
run: brew install boost
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
uses: astral-sh/setup-uv@v8.0.0
|
||||
with:
|
||||
enable-cache: true
|
||||
|
||||
|
||||
6
.github/workflows/tests-cibw.yml
vendored
6
.github/workflows/tests-cibw.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: pypa/cibuildwheel@v3.3
|
||||
- uses: pypa/cibuildwheel@v3.4
|
||||
env:
|
||||
PYODIDE_BUILD_EXPORTS: whole_archive
|
||||
with:
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
# We have to uninstall first because GH is now using a local tap to build cmake<4, iOS needs cmake>=4
|
||||
- run: brew uninstall cmake && brew install cmake
|
||||
|
||||
- uses: pypa/cibuildwheel@v3.3
|
||||
- uses: pypa/cibuildwheel@v3.4
|
||||
env:
|
||||
CIBW_PLATFORM: ios
|
||||
CIBW_SKIP: cp314-* # https://github.com/pypa/cibuildwheel/issues/2494
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
if: contains(matrix.runs-on, 'macos')
|
||||
run: echo "CIBW_TEST_COMMAND=" >> "$GITHUB_ENV"
|
||||
|
||||
- uses: pypa/cibuildwheel@v3.3
|
||||
- uses: pypa/cibuildwheel@v3.4
|
||||
env:
|
||||
CIBW_PLATFORM: android
|
||||
with:
|
||||
|
||||
2
.github/workflows/upstream.yml
vendored
2
.github/workflows/upstream.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
run: sudo apt-get install libboost-dev
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v2.1
|
||||
uses: jwlawson/actions-setup-cmake@v2.2
|
||||
|
||||
- name: Run pip installs
|
||||
run: |
|
||||
|
||||
@@ -25,14 +25,14 @@ repos:
|
||||
|
||||
# Clang format the codebase automatically
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: "v22.1.0"
|
||||
rev: "v22.1.2"
|
||||
hooks:
|
||||
- id: clang-format
|
||||
types_or: [c++, c, cuda]
|
||||
|
||||
# Ruff, the Python auto-correcting linter/formatter written in Rust
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.15.4
|
||||
rev: v0.15.9
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args: ["--fix", "--show-fixes"]
|
||||
@@ -40,7 +40,7 @@ repos:
|
||||
|
||||
# Check static types with mypy
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: "v1.19.1"
|
||||
rev: "v1.20.0"
|
||||
hooks:
|
||||
- id: mypy
|
||||
args: []
|
||||
@@ -112,7 +112,7 @@ repos:
|
||||
# Use tools/codespell_ignore_lines_from_errors.py
|
||||
# to rebuild .codespell-ignore-lines
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: "v2.4.1"
|
||||
rev: "v2.4.2"
|
||||
hooks:
|
||||
- id: codespell
|
||||
exclude: "(.supp|^pyproject.toml)$"
|
||||
@@ -122,7 +122,7 @@ repos:
|
||||
# Use mirror because pre-commit autoupdate confuses tags in the upstream repo.
|
||||
# See https://github.com/crate-ci/typos/issues/390
|
||||
- repo: https://github.com/adhtruong/mirrors-typos
|
||||
rev: "v1.44.0"
|
||||
rev: "v1.45.0"
|
||||
hooks:
|
||||
- id: typos
|
||||
args: []
|
||||
@@ -151,7 +151,7 @@ repos:
|
||||
|
||||
# Check schemas on some of our YAML files
|
||||
- repo: https://github.com/python-jsonschema/check-jsonschema
|
||||
rev: 0.37.0
|
||||
rev: 0.37.1
|
||||
hooks:
|
||||
- id: check-readthedocs
|
||||
- id: check-github-workflows
|
||||
|
||||
@@ -13,6 +13,34 @@ Changes will be added here periodically from the "Suggested changelog
|
||||
entry" block in pull request descriptions.
|
||||
|
||||
|
||||
## Version 3.0.4 (April 18, 2026)
|
||||
|
||||
Bug fixes:
|
||||
|
||||
- Fixed test builds with installed Eigen 5 by improving `Eigen3` CMake package detection.
|
||||
[#6036](https://github.com/pybind/pybind11/pull/6036)
|
||||
|
||||
- Fixed move semantics of `scoped_ostream_redirect` to preserve buffered output and avoid crashes when moved redirects restore stream buffers.
|
||||
[#6033](https://github.com/pybind/pybind11/pull/6033)
|
||||
|
||||
- Fixed `py::dynamic_attr()` traversal on Python 3.13+ to correctly propagate `PyObject_VisitManagedDict()` results.
|
||||
[#6032](https://github.com/pybind/pybind11/pull/6032)
|
||||
|
||||
- Fixed `std::shared_ptr<T>` fallback casting to avoid unnecessary copy-constructor instantiation in `reference_internal` paths.
|
||||
[#6028](https://github.com/pybind/pybind11/pull/6028)
|
||||
|
||||
CI:
|
||||
|
||||
- Updated `setup-uv` to the maintained GitHub Action tag scheme.
|
||||
[#6035](https://github.com/pybind/pybind11/pull/6035)
|
||||
|
||||
- Updated pre-commit hooks.
|
||||
[#6029](https://github.com/pybind/pybind11/pull/6029)
|
||||
|
||||
- Updated GitHub Actions dependencies, including `actions-setup-cmake` and `cibuildwheel`.
|
||||
[#6027](https://github.com/pybind/pybind11/pull/6027)
|
||||
|
||||
|
||||
## Version 3.0.3 (March 31, 2026)
|
||||
|
||||
Bug fixes:
|
||||
|
||||
@@ -1026,7 +1026,7 @@ public:
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
return type_caster_base<type>::cast(
|
||||
return type_caster_generic::cast_non_owning(
|
||||
srcs, return_value_policy::reference_internal, parent);
|
||||
}
|
||||
|
||||
|
||||
@@ -578,7 +578,10 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
|
||||
/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`.
|
||||
extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) {
|
||||
#if PY_VERSION_HEX >= 0x030D0000
|
||||
PyObject_VisitManagedDict(self, visit, arg);
|
||||
int ret = PyObject_VisitManagedDict(self, visit, arg);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||
Py_VISIT(dict);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
/* -- start version constants -- */
|
||||
#define PYBIND11_VERSION_MAJOR 3
|
||||
#define PYBIND11_VERSION_MINOR 0
|
||||
#define PYBIND11_VERSION_MICRO 3
|
||||
#define PYBIND11_VERSION_MICRO 4
|
||||
// ALPHA = 0xA, BETA = 0xB, GAMMA = 0xC (release candidate), FINAL = 0xF (stable release)
|
||||
// - The release level is set to "alpha" for development versions.
|
||||
// Use 0xA0 (LEVEL=0xA, SERIAL=0) for development versions.
|
||||
@@ -27,7 +27,7 @@
|
||||
#define PYBIND11_VERSION_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL
|
||||
#define PYBIND11_VERSION_RELEASE_SERIAL 0
|
||||
// String version of (micro, release level, release serial), e.g.: 0a0, 0b1, 0rc1, 0
|
||||
#define PYBIND11_VERSION_PATCH 3
|
||||
#define PYBIND11_VERSION_PATCH 4
|
||||
/* -- end version constants -- */
|
||||
|
||||
#if !defined(Py_PACK_FULL_VERSION)
|
||||
|
||||
@@ -1004,6 +1004,18 @@ public:
|
||||
return cast(srcs, policy, parent, copy_constructor, move_constructor, existing_holder);
|
||||
}
|
||||
|
||||
static handle cast_non_owning(const cast_sources &srcs,
|
||||
return_value_policy policy,
|
||||
handle parent,
|
||||
const void *existing_holder = nullptr) {
|
||||
// Reference-like policies alias an existing C++ object instead of creating
|
||||
// a new one, so copy/move constructor callbacks must remain null here.
|
||||
assert(policy == return_value_policy::reference
|
||||
|| policy == return_value_policy::reference_internal
|
||||
|| policy == return_value_policy::automatic_reference);
|
||||
return cast(srcs, policy, parent, nullptr, nullptr, existing_holder);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE static handle cast(const cast_sources &srcs,
|
||||
return_value_policy policy,
|
||||
handle parent,
|
||||
|
||||
@@ -131,7 +131,22 @@ public:
|
||||
setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
|
||||
}
|
||||
|
||||
pythonbuf(pythonbuf &&) = default;
|
||||
pythonbuf(pythonbuf &&other) noexcept
|
||||
: buf_size(other.buf_size), d_buffer(std::move(other.d_buffer)),
|
||||
pywrite(std::move(other.pywrite)), pyflush(std::move(other.pyflush)) {
|
||||
const auto pending = (other.pbase() != nullptr && other.pptr() != nullptr)
|
||||
? static_cast<int>(other.pptr() - other.pbase())
|
||||
: 0;
|
||||
if (d_buffer != nullptr) {
|
||||
// Rebuild the put area from the transferred storage.
|
||||
setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
|
||||
pbump(pending);
|
||||
} else {
|
||||
setp(nullptr, nullptr);
|
||||
}
|
||||
// Prevent the moved-from destructor from flushing through moved-out handles.
|
||||
other.setp(nullptr, nullptr);
|
||||
}
|
||||
|
||||
/// Sync before destroy
|
||||
~pythonbuf() override { _sync(); }
|
||||
@@ -169,6 +184,7 @@ protected:
|
||||
std::streambuf *old;
|
||||
std::ostream &costream;
|
||||
detail::pythonbuf buffer;
|
||||
bool active = true;
|
||||
|
||||
public:
|
||||
explicit scoped_ostream_redirect(std::ostream &costream = std::cout,
|
||||
@@ -178,10 +194,22 @@ public:
|
||||
old = costream.rdbuf(&buffer);
|
||||
}
|
||||
|
||||
~scoped_ostream_redirect() { costream.rdbuf(old); }
|
||||
~scoped_ostream_redirect() {
|
||||
if (active) {
|
||||
costream.rdbuf(old);
|
||||
}
|
||||
}
|
||||
|
||||
scoped_ostream_redirect(const scoped_ostream_redirect &) = delete;
|
||||
scoped_ostream_redirect(scoped_ostream_redirect &&other) = default;
|
||||
// NOLINTNEXTLINE(performance-noexcept-move-constructor)
|
||||
scoped_ostream_redirect(scoped_ostream_redirect &&other)
|
||||
: old(other.old), costream(other.costream), buffer(std::move(other.buffer)),
|
||||
active(other.active) {
|
||||
if (active) {
|
||||
costream.rdbuf(&buffer); // Re-point stream to our buffer
|
||||
other.active = false;
|
||||
}
|
||||
}
|
||||
scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete;
|
||||
scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete;
|
||||
};
|
||||
|
||||
@@ -202,6 +202,10 @@ fo = "fo"
|
||||
quater = "quater"
|
||||
optin = "optin"
|
||||
othr = "othr"
|
||||
# NumPy uses "writeable" in public API names and flags.
|
||||
writeable = "writeable"
|
||||
Writeable = "Writeable"
|
||||
WRITEABLE = "WRITEABLE"
|
||||
|
||||
#[tool.typos.type.cpp.extend-words]
|
||||
setp = "setp"
|
||||
|
||||
@@ -167,6 +167,7 @@ set(PYBIND11_TEST_FILES
|
||||
test_operator_overloading
|
||||
test_pickling
|
||||
test_potentially_slicing_weak_ptr
|
||||
test_pytorch_shared_ptr_cast_regression
|
||||
test_python_multiple_inheritance
|
||||
test_pytypes
|
||||
test_scoped_critical_section
|
||||
@@ -299,10 +300,17 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
|
||||
else()
|
||||
find_package(Eigen3 3.2.7 QUIET CONFIG)
|
||||
if(NOT Eigen3_FOUND)
|
||||
find_package(Eigen3 5 QUIET CONFIG)
|
||||
endif()
|
||||
set(EIGEN3_FOUND ${Eigen3_FOUND})
|
||||
set(EIGEN3_VERSION ${Eigen3_VERSION})
|
||||
|
||||
if(NOT EIGEN3_FOUND)
|
||||
# Couldn't load via target, so fall back to allowing module mode finding, which will pick up
|
||||
# tools/FindEigen3.cmake
|
||||
# This MODULE-mode fallback is for older Eigen 3 setups; Eigen 5 is expected to be found
|
||||
# via the CONFIG-mode probes above.
|
||||
find_package(Eigen3 3.2.7 QUIET)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -104,6 +104,10 @@ TEST_SUBMODULE(class_, m) {
|
||||
~NoConstructorNew() { print_destroyed(this); }
|
||||
};
|
||||
|
||||
struct DynamicAttr {
|
||||
DynamicAttr() = default;
|
||||
};
|
||||
|
||||
py::class_<NoConstructor>(m, "NoConstructor")
|
||||
.def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
|
||||
|
||||
@@ -112,6 +116,8 @@ TEST_SUBMODULE(class_, m) {
|
||||
.def_static("__new__",
|
||||
[](const py::object &) { return NoConstructorNew::new_instance(); });
|
||||
|
||||
py::class_<DynamicAttr>(m, "DynamicAttr", py::dynamic_attr()).def(py::init<>());
|
||||
|
||||
// test_pass_unique_ptr
|
||||
struct ToBeHeldByUniquePtr {};
|
||||
py::class_<ToBeHeldByUniquePtr, std::unique_ptr<ToBeHeldByUniquePtr>>(m, "ToBeHeldByUniquePtr")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import gc
|
||||
import sys
|
||||
from unittest import mock
|
||||
|
||||
@@ -18,6 +19,13 @@ def refcount_immortal(ob: object) -> int:
|
||||
return sys.getrefcount(ob)
|
||||
|
||||
|
||||
MANAGED_DICT_GET_REFERRERS_SUPPORTED = (
|
||||
env.CPYTHON
|
||||
and sys.version_info >= (3, 13, 13)
|
||||
and (sys.version_info < (3, 14) or sys.version_info >= (3, 14, 4))
|
||||
)
|
||||
|
||||
|
||||
def test_obj_class_name():
|
||||
expected_name = "UserType" if env.PYPY else "pybind11_tests.UserType"
|
||||
assert m.obj_class_name(UserType(1)) == expected_name
|
||||
@@ -45,6 +53,16 @@ def test_instance(msg):
|
||||
assert cstats.alive() == 0
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not MANAGED_DICT_GET_REFERRERS_SUPPORTED,
|
||||
reason="Requires CPython 3.13.13+ or 3.14.4+ managed dict traversal support",
|
||||
)
|
||||
def test_get_referrers():
|
||||
instance = m.DynamicAttr()
|
||||
instance.a = "test"
|
||||
assert instance in gc.get_referrers(instance.__dict__)
|
||||
|
||||
|
||||
def test_instance_new():
|
||||
instance = m.NoConstructorNew() # .__new__(m.NoConstructor.__class__)
|
||||
|
||||
|
||||
@@ -204,3 +204,15 @@ def test_non_smart_holder_member_type_with_smart_holder_owner_aliases_member():
|
||||
legacy = obj.legacy
|
||||
legacy.value = 13
|
||||
assert obj.legacy.value == 13
|
||||
|
||||
|
||||
def test_non_smart_holder_member_type_with_smart_holder_owner_aliases_member_multiple_reads():
|
||||
obj = m.ShWithSimpleStructMember()
|
||||
|
||||
a = obj.legacy
|
||||
b = obj.legacy
|
||||
|
||||
a.value = 13
|
||||
|
||||
assert b.value == 13
|
||||
assert obj.legacy.value == 13
|
||||
|
||||
@@ -123,4 +123,40 @@ TEST_SUBMODULE(iostream, m) {
|
||||
.def("stop", &TestThread::stop)
|
||||
.def("join", &TestThread::join)
|
||||
.def("sleep", &TestThread::sleep);
|
||||
|
||||
m.def("move_redirect_output", [](const std::string &msg_before, const std::string &msg_after) {
|
||||
py::scoped_ostream_redirect redir1(std::cout, py::module_::import("sys").attr("stdout"));
|
||||
std::cout << msg_before << std::flush;
|
||||
py::scoped_ostream_redirect redir2(std::move(redir1));
|
||||
std::cout << msg_after << std::flush;
|
||||
});
|
||||
|
||||
m.def("move_redirect_output_unflushed",
|
||||
[](const std::string &msg_before, const std::string &msg_after) {
|
||||
py::scoped_ostream_redirect redir1(std::cout,
|
||||
py::module_::import("sys").attr("stdout"));
|
||||
std::cout << msg_before;
|
||||
py::scoped_ostream_redirect redir2(std::move(redir1));
|
||||
std::cout << msg_after << std::flush;
|
||||
});
|
||||
|
||||
// Redirect a stream whose original rdbuf is nullptr, then move the redirect.
|
||||
// Verifies that nullptr is correctly restored (not confused with a moved-from sentinel).
|
||||
m.def("move_redirect_null_rdbuf", [](const std::string &msg) {
|
||||
std::ostream os(nullptr);
|
||||
py::scoped_ostream_redirect redir1(os, py::module_::import("sys").attr("stdout"));
|
||||
os << msg << std::flush;
|
||||
py::scoped_ostream_redirect redir2(std::move(redir1));
|
||||
os << msg << std::flush;
|
||||
// After redir2 goes out of scope, os.rdbuf() should be restored to nullptr.
|
||||
});
|
||||
|
||||
m.def("get_null_rdbuf_restored", [](const std::string &msg) -> bool {
|
||||
std::ostream os(nullptr);
|
||||
{
|
||||
py::scoped_ostream_redirect redir(os, py::module_::import("sys").attr("stdout"));
|
||||
os << msg << std::flush;
|
||||
}
|
||||
return os.rdbuf() == nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -284,6 +284,31 @@ def test_redirect_both(capfd):
|
||||
assert stream2.getvalue() == msg2
|
||||
|
||||
|
||||
def test_move_redirect(capsys):
|
||||
m.move_redirect_output("before_move", "after_move")
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == "before_moveafter_move"
|
||||
assert not stderr
|
||||
|
||||
|
||||
def test_move_redirect_unflushed(capsys):
|
||||
m.move_redirect_output_unflushed("before_move", "after_move")
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == "before_moveafter_move"
|
||||
assert not stderr
|
||||
|
||||
|
||||
def test_move_redirect_null_rdbuf(capsys):
|
||||
m.move_redirect_null_rdbuf("hello")
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == "hellohello"
|
||||
assert not stderr
|
||||
|
||||
|
||||
def test_null_rdbuf_restored():
|
||||
assert m.get_null_rdbuf_restored("test")
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
|
||||
def test_threading():
|
||||
with m.ostream_redirect(stdout=True, stderr=False):
|
||||
|
||||
62
tests/test_pytorch_shared_ptr_cast_regression.cpp
Normal file
62
tests/test_pytorch_shared_ptr_cast_regression.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#if defined(__clang__)
|
||||
# if __has_warning("-Wdeprecated-copy-with-user-provided-dtor")
|
||||
# pragma clang diagnostic error "-Wdeprecated-copy-with-user-provided-dtor"
|
||||
# endif
|
||||
# if __has_warning("-Wdeprecated-copy-with-dtor")
|
||||
# pragma clang diagnostic error "-Wdeprecated-copy-with-dtor"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace test_pytorch_regressions {
|
||||
|
||||
// Directly extracted from PyTorch patterns that regressed in CI.
|
||||
struct TracingState : std::enable_shared_from_this<TracingState> {
|
||||
TracingState() = default;
|
||||
~TracingState() = default;
|
||||
int value = 0;
|
||||
};
|
||||
|
||||
const std::shared_ptr<TracingState> &get_tracing_state() {
|
||||
static std::shared_ptr<TracingState> state = std::make_shared<TracingState>();
|
||||
return state;
|
||||
}
|
||||
|
||||
struct InterfaceType {
|
||||
~InterfaceType() = default;
|
||||
int value = 0;
|
||||
};
|
||||
using InterfaceTypePtr = std::shared_ptr<InterfaceType>;
|
||||
|
||||
struct CompilationUnit {
|
||||
InterfaceTypePtr iface = std::make_shared<InterfaceType>();
|
||||
|
||||
InterfaceTypePtr get_interface(const std::string &) const { return iface; }
|
||||
};
|
||||
|
||||
} // namespace test_pytorch_regressions
|
||||
|
||||
TEST_SUBMODULE(pybind11_pytorch_regressions, m) {
|
||||
using namespace test_pytorch_regressions;
|
||||
|
||||
py::class_<TracingState, std::shared_ptr<TracingState>>(m, "TracingState")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("value", &TracingState::value);
|
||||
|
||||
m.def("_get_tracing_state", []() { return get_tracing_state(); });
|
||||
|
||||
py::class_<InterfaceType, InterfaceTypePtr>(m, "InterfaceType")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("value", &InterfaceType::value);
|
||||
|
||||
py::class_<CompilationUnit, std::shared_ptr<CompilationUnit>>(m, "CompilationUnit")
|
||||
.def(py::init<>())
|
||||
.def("get_interface",
|
||||
[](const std::shared_ptr<CompilationUnit> &self, const std::string &name) {
|
||||
return self->get_interface(name);
|
||||
});
|
||||
}
|
||||
25
tests/test_pytorch_shared_ptr_cast_regression.py
Normal file
25
tests/test_pytorch_shared_ptr_cast_regression.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pybind11_tests import pybind11_pytorch_regressions as m
|
||||
|
||||
|
||||
def test_pytorch_like_get_tracing_state_aliases_singleton_shared_ptr():
|
||||
a = m._get_tracing_state()
|
||||
b = m._get_tracing_state()
|
||||
|
||||
a.value = 17
|
||||
|
||||
assert b.value == 17
|
||||
assert m._get_tracing_state().value == 17
|
||||
|
||||
|
||||
def test_pytorch_like_compilation_unit_get_interface_aliases_member_shared_ptr():
|
||||
cu = m.CompilationUnit()
|
||||
|
||||
a = cu.get_interface("iface")
|
||||
b = cu.get_interface("iface")
|
||||
|
||||
a.value = 23
|
||||
|
||||
assert b.value == 23
|
||||
assert cu.get_interface("iface").value == 23
|
||||
Reference in New Issue
Block a user