Skip to content

Commit a4bd790

Browse files
committed
Adding unit test.
1 parent 1011dde commit a4bd790

File tree

4 files changed

+81
-1
lines changed

4 files changed

+81
-1
lines changed

include/pybind11/detail/init.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias)
298298
// Skipping setattr below, to not force use of py::dynamic_attr() for Class unnecessarily.
299299
return;
300300
}
301-
setattr((PyObject *) v_h.inst, "__dict__", result.second);
301+
setattr((PyObject *) v_h.inst, "__dict__", d);
302302
}
303303

304304
/// Implementation for py::pickle(GetState, SetState)

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ set(PYBIND11_TEST_FILES
123123
test_opaque_types.cpp
124124
test_operator_overloading.cpp
125125
test_pickling.cpp
126+
test_pickling_trampoline.cpp
126127
test_pytypes.cpp
127128
test_sequences_and_iterators.cpp
128129
test_smart_ptr.cpp

tests/test_pickling_trampoline.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) 2021 The Pybind Development Team.
2+
// All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
#include "pybind11_tests.h"
6+
7+
#include <memory>
8+
#include <utility>
9+
10+
namespace {
11+
12+
struct SimpleBase {
13+
int num = 0;
14+
virtual ~SimpleBase() = default;
15+
};
16+
17+
struct SimpleBaseTrampoline : SimpleBase {};
18+
19+
struct SimpleCppDerived : SimpleBase {};
20+
21+
} // namespace
22+
23+
TEST_SUBMODULE(pickling_trampoline, m) {
24+
py::class_<SimpleBase, SimpleBaseTrampoline>(m, "SimpleBase")
25+
.def(py::init<>())
26+
.def_readwrite("num", &SimpleBase::num)
27+
.def(py::pickle(
28+
[](py::object self) {
29+
py::dict d;
30+
if (py::hasattr(self, "__dict__"))
31+
d = self.attr("__dict__");
32+
return py::make_tuple(self.attr("num"), d);
33+
},
34+
[](py::tuple t) {
35+
if (t.size() != 2)
36+
throw std::runtime_error("Invalid state!");
37+
auto cpp_state = std::unique_ptr<SimpleBase>(new SimpleBaseTrampoline);
38+
cpp_state->num = t[0].cast<int>();
39+
auto py_state = t[1].cast<py::dict>();
40+
return std::make_pair(std::move(cpp_state), py_state);
41+
}));
42+
43+
m.def("make_SimpleCppDerivedAsBase",
44+
[]() { return std::unique_ptr<SimpleBase>(new SimpleCppDerived); });
45+
}

tests/test_pickling_trampoline.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# -*- coding: utf-8 -*-
2+
import pytest
3+
4+
from pybind11_tests import pickling_trampoline as m
5+
6+
try:
7+
import cPickle as pickle # Use cPickle on Python 2.7
8+
except ImportError:
9+
import pickle
10+
11+
12+
class SimplePyDerived(m.SimpleBase):
13+
pass
14+
15+
16+
def test_roundtrip_simple_py_derived():
17+
p = SimplePyDerived()
18+
p.num = 202
19+
p.stored_in_dict = 303
20+
data = pickle.dumps(p, pickle.HIGHEST_PROTOCOL)
21+
p2 = pickle.loads(data)
22+
assert p2.num == 202
23+
assert p2.stored_in_dict == 303
24+
25+
26+
def test_roundtrip_simple_cpp_derived():
27+
p = m.make_SimpleCppDerivedAsBase()
28+
p.num = 404
29+
with pytest.raises(AttributeError):
30+
# To ensure that future changes do not accidentally invalidate this unit test.
31+
p.__dict__
32+
data = pickle.dumps(p, pickle.HIGHEST_PROTOCOL)
33+
p2 = pickle.loads(data)
34+
assert p2.num == 404

0 commit comments

Comments
 (0)