Blame external/pybind11/tests/test_pickling.cpp

Packit 534379
/*
Packit 534379
    tests/test_pickling.cpp -- pickle support
Packit 534379
Packit 534379
    Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
Packit 534379
Packit 534379
    All rights reserved. Use of this source code is governed by a
Packit 534379
    BSD-style license that can be found in the LICENSE file.
Packit 534379
*/
Packit 534379
Packit 534379
#include "pybind11_tests.h"
Packit 534379
Packit 534379
TEST_SUBMODULE(pickling, m) {
Packit 534379
    // test_roundtrip
Packit 534379
    class Pickleable {
Packit 534379
    public:
Packit 534379
        Pickleable(const std::string &value) : m_value(value) { }
Packit 534379
        const std::string &value() const { return m_value; }
Packit 534379
Packit 534379
        void setExtra1(int extra1) { m_extra1 = extra1; }
Packit 534379
        void setExtra2(int extra2) { m_extra2 = extra2; }
Packit 534379
        int extra1() const { return m_extra1; }
Packit 534379
        int extra2() const { return m_extra2; }
Packit 534379
    private:
Packit 534379
        std::string m_value;
Packit 534379
        int m_extra1 = 0;
Packit 534379
        int m_extra2 = 0;
Packit 534379
    };
Packit 534379
Packit 534379
    class PickleableNew : public Pickleable {
Packit 534379
    public:
Packit 534379
        using Pickleable::Pickleable;
Packit 534379
    };
Packit 534379
Packit 534379
    py::class_<Pickleable>(m, "Pickleable")
Packit 534379
        .def(py::init<std::string>())
Packit 534379
        .def("value", &Pickleable::value)
Packit 534379
        .def("extra1", &Pickleable::extra1)
Packit 534379
        .def("extra2", &Pickleable::extra2)
Packit 534379
        .def("setExtra1", &Pickleable::setExtra1)
Packit 534379
        .def("setExtra2", &Pickleable::setExtra2)
Packit 534379
        // For details on the methods below, refer to
Packit 534379
        // http://docs.python.org/3/library/pickle.html#pickling-class-instances
Packit 534379
        .def("__getstate__", [](const Pickleable &p) {
Packit 534379
            /* Return a tuple that fully encodes the state of the object */
Packit 534379
            return py::make_tuple(p.value(), p.extra1(), p.extra2());
Packit 534379
        })
Packit 534379
        .def("__setstate__", [](Pickleable &p, py::tuple t) {
Packit 534379
            if (t.size() != 3)
Packit 534379
                throw std::runtime_error("Invalid state!");
Packit 534379
            /* Invoke the constructor (need to use in-place version) */
Packit 534379
            new (&p) Pickleable(t[0].cast<std::string>());
Packit 534379
Packit 534379
            /* Assign any additional state */
Packit 534379
            p.setExtra1(t[1].cast<int>());
Packit 534379
            p.setExtra2(t[2].cast<int>());
Packit 534379
        });
Packit 534379
Packit 534379
    py::class_<PickleableNew, Pickleable>(m, "PickleableNew")
Packit 534379
        .def(py::init<std::string>())
Packit 534379
        .def(py::pickle(
Packit 534379
            [](const PickleableNew &p) {
Packit 534379
                return py::make_tuple(p.value(), p.extra1(), p.extra2());
Packit 534379
            },
Packit 534379
            [](py::tuple t) {
Packit 534379
                if (t.size() != 3)
Packit 534379
                    throw std::runtime_error("Invalid state!");
Packit 534379
                auto p = PickleableNew(t[0].cast<std::string>());
Packit 534379
Packit 534379
                p.setExtra1(t[1].cast<int>());
Packit 534379
                p.setExtra2(t[2].cast<int>());
Packit 534379
                return p;
Packit 534379
            }
Packit 534379
        ));
Packit 534379
Packit 534379
#if !defined(PYPY_VERSION)
Packit 534379
    // test_roundtrip_with_dict
Packit 534379
    class PickleableWithDict {
Packit 534379
    public:
Packit 534379
        PickleableWithDict(const std::string &value) : value(value) { }
Packit 534379
Packit 534379
        std::string value;
Packit 534379
        int extra;
Packit 534379
    };
Packit 534379
Packit 534379
    class PickleableWithDictNew : public PickleableWithDict {
Packit 534379
    public:
Packit 534379
        using PickleableWithDict::PickleableWithDict;
Packit 534379
    };
Packit 534379
Packit 534379
    py::class_<PickleableWithDict>(m, "PickleableWithDict", py::dynamic_attr())
Packit 534379
        .def(py::init<std::string>())
Packit 534379
        .def_readwrite("value", &PickleableWithDict::value)
Packit 534379
        .def_readwrite("extra", &PickleableWithDict::extra)
Packit 534379
        .def("__getstate__", [](py::object self) {
Packit 534379
            /* Also include __dict__ in state */
Packit 534379
            return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__"));
Packit 534379
        })
Packit 534379
        .def("__setstate__", [](py::object self, py::tuple t) {
Packit 534379
            if (t.size() != 3)
Packit 534379
                throw std::runtime_error("Invalid state!");
Packit 534379
            /* Cast and construct */
Packit 534379
            auto& p = self.cast<PickleableWithDict&>();
Packit 534379
            new (&p) PickleableWithDict(t[0].cast<std::string>());
Packit 534379
Packit 534379
            /* Assign C++ state */
Packit 534379
            p.extra = t[1].cast<int>();
Packit 534379
Packit 534379
            /* Assign Python state */
Packit 534379
            self.attr("__dict__") = t[2];
Packit 534379
        });
Packit 534379
Packit 534379
    py::class_<PickleableWithDictNew, PickleableWithDict>(m, "PickleableWithDictNew")
Packit 534379
        .def(py::init<std::string>())
Packit 534379
        .def(py::pickle(
Packit 534379
            [](py::object self) {
Packit 534379
                return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__"));
Packit 534379
            },
Packit 534379
            [](const py::tuple &t) {
Packit 534379
                if (t.size() != 3)
Packit 534379
                    throw std::runtime_error("Invalid state!");
Packit 534379
Packit 534379
                auto cpp_state = PickleableWithDictNew(t[0].cast<std::string>());
Packit 534379
                cpp_state.extra = t[1].cast<int>();
Packit 534379
Packit 534379
                auto py_state = t[2].cast<py::dict>();
Packit 534379
                return std::make_pair(cpp_state, py_state);
Packit 534379
            }
Packit 534379
        ));
Packit 534379
#endif
Packit 534379
}