blob: 821462ac4bd973c5e10a8967d9dd4ba7614b06d8 [file] [log] [blame]
Wenzel Jakob1c329aa2016-04-13 02:37:36 +02001/*
Dean Moldovana0c1ccf2016-08-12 13:50:00 +02002 tests/test_pickling.cpp -- pickle support
Wenzel Jakob1c329aa2016-04-13 02:37:36 +02003
Wenzel Jakob8cb6cb32016-04-17 20:21:41 +02004 Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
Wenzel Jakob1c329aa2016-04-13 02:37:36 +02005
6 All rights reserved. Use of this source code is governed by a
7 BSD-style license that can be found in the LICENSE file.
8*/
9
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020010#include "pybind11_tests.h"
Wenzel Jakob1c329aa2016-04-13 02:37:36 +020011
Jason Rhinelander391c7542017-07-25 16:47:36 -040012TEST_SUBMODULE(pickling, m) {
13 // test_roundtrip
14 class Pickleable {
15 public:
16 Pickleable(const std::string &value) : m_value(value) { }
17 const std::string &value() const { return m_value; }
Wenzel Jakob1c329aa2016-04-13 02:37:36 +020018
Jason Rhinelander391c7542017-07-25 16:47:36 -040019 void setExtra1(int extra1) { m_extra1 = extra1; }
20 void setExtra2(int extra2) { m_extra2 = extra2; }
21 int extra1() const { return m_extra1; }
22 int extra2() const { return m_extra2; }
23 private:
24 std::string m_value;
25 int m_extra1 = 0;
26 int m_extra2 = 0;
27 };
Dean Moldovan1e5a7da2017-08-24 01:53:15 +020028
29 class PickleableNew : public Pickleable {
30 public:
31 using Pickleable::Pickleable;
32 };
33
Wenzel Jakob1c329aa2016-04-13 02:37:36 +020034 py::class_<Pickleable>(m, "Pickleable")
35 .def(py::init<std::string>())
36 .def("value", &Pickleable::value)
37 .def("extra1", &Pickleable::extra1)
38 .def("extra2", &Pickleable::extra2)
39 .def("setExtra1", &Pickleable::setExtra1)
40 .def("setExtra2", &Pickleable::setExtra2)
41 // For details on the methods below, refer to
42 // http://docs.python.org/3/library/pickle.html#pickling-class-instances
43 .def("__getstate__", [](const Pickleable &p) {
44 /* Return a tuple that fully encodes the state of the object */
45 return py::make_tuple(p.value(), p.extra1(), p.extra2());
46 })
47 .def("__setstate__", [](Pickleable &p, py::tuple t) {
48 if (t.size() != 3)
49 throw std::runtime_error("Invalid state!");
50 /* Invoke the constructor (need to use in-place version) */
51 new (&p) Pickleable(t[0].cast<std::string>());
52
53 /* Assign any additional state */
54 p.setExtra1(t[1].cast<int>());
55 p.setExtra2(t[2].cast<int>());
56 });
Dean Moldovan6fccf692016-10-11 01:12:48 +020057
Dean Moldovan1e5a7da2017-08-24 01:53:15 +020058 py::class_<PickleableNew, Pickleable>(m, "PickleableNew")
59 .def(py::init<std::string>())
60 .def(py::pickle(
61 [](const PickleableNew &p) {
62 return py::make_tuple(p.value(), p.extra1(), p.extra2());
63 },
64 [](py::tuple t) {
65 if (t.size() != 3)
66 throw std::runtime_error("Invalid state!");
67 auto p = PickleableNew(t[0].cast<std::string>());
68
69 p.setExtra1(t[1].cast<int>());
70 p.setExtra2(t[2].cast<int>());
71 return p;
72 }
73 ));
74
Wenzel Jakob64cb6992016-12-26 13:12:10 +010075#if !defined(PYPY_VERSION)
Jason Rhinelander391c7542017-07-25 16:47:36 -040076 // test_roundtrip_with_dict
77 class PickleableWithDict {
78 public:
79 PickleableWithDict(const std::string &value) : value(value) { }
80
81 std::string value;
82 int extra;
83 };
Dean Moldovan1e5a7da2017-08-24 01:53:15 +020084
85 class PickleableWithDictNew : public PickleableWithDict {
86 public:
87 using PickleableWithDict::PickleableWithDict;
88 };
89
Dean Moldovan6fccf692016-10-11 01:12:48 +020090 py::class_<PickleableWithDict>(m, "PickleableWithDict", py::dynamic_attr())
91 .def(py::init<std::string>())
92 .def_readwrite("value", &PickleableWithDict::value)
93 .def_readwrite("extra", &PickleableWithDict::extra)
94 .def("__getstate__", [](py::object self) {
95 /* Also include __dict__ in state */
96 return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__"));
97 })
98 .def("__setstate__", [](py::object self, py::tuple t) {
99 if (t.size() != 3)
100 throw std::runtime_error("Invalid state!");
101 /* Cast and construct */
102 auto& p = self.cast<PickleableWithDict&>();
Wenzel Jakobcd7eacc2017-01-04 09:01:59 -0500103 new (&p) PickleableWithDict(t[0].cast<std::string>());
Dean Moldovan6fccf692016-10-11 01:12:48 +0200104
105 /* Assign C++ state */
106 p.extra = t[1].cast<int>();
107
108 /* Assign Python state */
109 self.attr("__dict__") = t[2];
110 });
Dean Moldovan1e5a7da2017-08-24 01:53:15 +0200111
112 py::class_<PickleableWithDictNew, PickleableWithDict>(m, "PickleableWithDictNew")
113 .def(py::init<std::string>())
114 .def(py::pickle(
115 [](py::object self) {
116 return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__"));
117 },
118 [](py::tuple t) {
119 if (t.size() != 3)
120 throw std::runtime_error("Invalid state!");
121
122 auto cpp_state = PickleableWithDictNew(t[0].cast<std::string>());
123 cpp_state.extra = t[1].cast<int>();
124
125 auto py_state = t[2].cast<py::dict>();
126 return std::make_pair(cpp_state, py_state);
127 }
128 ));
Wenzel Jakob64cb6992016-12-26 13:12:10 +0100129#endif
Jason Rhinelander391c7542017-07-25 16:47:36 -0400130}