diff --git a/Jamfile b/Jamfile index fea5e97a..d4f30be5 100644 --- a/Jamfile +++ b/Jamfile @@ -27,6 +27,7 @@ dll bpl src/object/function.cpp src/object/inheritance.cpp src/object/life_support.cpp + src/object/pickle_support.cpp src/errors.cpp src/module.cpp src/converter/builtin_converters.cpp diff --git a/include/boost/python/class.hpp b/include/boost/python/class.hpp index 861d8e72..e8610cef 100644 --- a/include/boost/python/class.hpp +++ b/include/boost/python/class.hpp @@ -25,6 +25,7 @@ # include # include # include +# include namespace boost { namespace python { @@ -195,6 +196,19 @@ class class_ : public objects::class_base self& setattr(char const* name, handle<> const&); + // Pickle support + template + self& def_pickle(PickleGroupType) + { + detail::pickle_group_finalize::register_( + *this, + &PickleGroupType::getinitargs, + &PickleGroupType::getstate, + &PickleGroupType::setstate, + PickleGroupType::getstate_manages_dict()); + return *this; + } + private: // types typedef objects::class_id class_id; diff --git a/include/boost/python/detail/api_placeholder.hpp b/include/boost/python/detail/api_placeholder.hpp new file mode 100644 index 00000000..fd571d88 --- /dev/null +++ b/include/boost/python/detail/api_placeholder.hpp @@ -0,0 +1,24 @@ +#ifndef BOOST_PYTHON_API_PLACE_HOLDER_HPP +#define BOOST_PYTHON_API_PLACE_HOLDER_HPP + +namespace boost { namespace python { + + inline long len(object const& obj) + { + long result = PyObject_Length(obj.ptr()); + if (PyErr_Occurred()) throw_error_already_set(); + return result; + } + + inline object getattr(object const& a0, const char* a1, object const& a2) + { + handle<> result(allow_null(PyObject_GetAttrString( + a0.ptr(), const_cast(a1)))); + if (!PyErr_Occurred()) return object(result); + PyErr_Clear(); + return a2; + } + +}} // namespace boost::python + +#endif // BOOST_PYTHON_API_PLACE_HOLDER_HPP diff --git a/include/boost/python/object/class.hpp b/include/boost/python/object/class.hpp index 8cf29b23..8a686505 100644 --- a/include/boost/python/object/class.hpp +++ b/include/boost/python/object/class.hpp @@ -39,6 +39,7 @@ struct BOOST_PYTHON_DECL class_base : private noncopyable void add_property(char const* name, handle<> const& fget); void add_property(char const* name, handle<> const& fget, handle<> const& fset); void setattr(char const* name, handle<> const&); + void enable_pickling(bool getstate_manages_dict); private: type_handle m_object; }; diff --git a/include/boost/python/object/pickle_support.hpp b/include/boost/python/object/pickle_support.hpp index abc4553b..667b60e0 100644 --- a/include/boost/python/object/pickle_support.hpp +++ b/include/boost/python/object/pickle_support.hpp @@ -16,17 +16,17 @@ handle<> make_instance_reduce_function(); namespace error_messages { template - struct missing_pickle_support_function_or_incorrect_signature {}; + struct missing_pickle_group_function_or_incorrect_signature {}; } -namespace detail { struct pickle_support_registration; } +namespace detail { struct pickle_group_registration; } -struct pickle_support_base +struct pickle_group { private: struct inaccessible {}; - friend struct detail::pickle_support_registration; + friend struct detail::pickle_group_registration; public: static inaccessible* getinitargs() { return 0; } static inaccessible* getstate() { return 0; } @@ -36,9 +36,9 @@ struct pickle_support_base namespace detail { - struct pickle_support_registration + struct pickle_group_registration { - typedef pickle_support_base::inaccessible inaccessible; + typedef pickle_group::inaccessible inaccessible; template static @@ -50,7 +50,7 @@ namespace detail { inaccessible* (*setstate_fn)(), bool) { - cl.enable_pickle_support(false); + cl.enable_pickling(false); cl.def("__getinitargs__", getinitargs_fn); } @@ -64,7 +64,7 @@ namespace detail { void (*setstate_fn)(Tsetstate, object), bool getstate_manages_dict) { - cl.enable_pickle_support(getstate_manages_dict); + cl.enable_pickling(getstate_manages_dict); cl.def("__getstate__", getstate_fn); cl.def("__setstate__", setstate_fn); } @@ -80,7 +80,7 @@ namespace detail { void (*setstate_fn)(Tsetstate, object), bool getstate_manages_dict) { - cl.enable_pickle_support(getstate_manages_dict); + cl.enable_pickling(getstate_manages_dict); cl.def("__getinitargs__", getinitargs_fn); cl.def("__getstate__", getstate_fn); cl.def("__setstate__", setstate_fn); @@ -94,15 +94,15 @@ namespace detail { ...) { typedef typename - error_messages::missing_pickle_support_function_or_incorrect_signature< + error_messages::missing_pickle_group_function_or_incorrect_signature< Class_>::error_type error_type; } }; - template - struct pickle_support_finalize - : UserPickleSupportType, - pickle_support_registration + template + struct pickle_group_finalize + : UserPickleGroupType, + pickle_group_registration {}; } // namespace detail diff --git a/src/object/class.cpp b/src/object/class.cpp index e49df96f..8b37752d 100644 --- a/src/object/class.cpp +++ b/src/object/class.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -304,6 +305,16 @@ namespace objects throw_error_already_set(); } + void class_base::enable_pickling(bool getstate_manages_dict) + { + setattr("__reduce__", make_instance_reduce_function()); + handle<> one(PyInt_FromLong(1)); + setattr("__safe_for_unpickling__", one); + if (getstate_manages_dict) { + setattr("__getstate_manages_dict__", one); + } + } + BOOST_PYTHON_DECL type_handle registered_class_object(class_id id) { return objects::query_class(id); diff --git a/test/Jamfile b/test/Jamfile index 5fb0eff4..21be34fe 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -82,6 +82,10 @@ bpl-test iterator : iterator.py iterator.cpp input_iterator.cpp ; bpl-test extract ; +bpl-test pickle1 ; +bpl-test pickle2 ; +bpl-test pickle3 ; + if $(TEST_BIENSTMAN_NON_BUGS) { bpl-test bienstman4 ; diff --git a/test/pickle1.cpp b/test/pickle1.cpp index b8b0fa92..c2de26dd 100644 --- a/test/pickle1.cpp +++ b/test/pickle1.cpp @@ -32,7 +32,7 @@ namespace { std::string get_country() const { return country; } }; - struct world_pickle_support : boost::python::pickle_support_base + struct world_pickle_group : boost::python::pickle_group { static boost::python::tuple @@ -54,7 +54,7 @@ BOOST_PYTHON_MODULE_INIT(pickle1_ext) .add(class_("world") .def_init(args()) .def("greet", &world::greet) - .pickle_support(world_pickle_support()) + .def_pickle(world_pickle_group()) ) ; } diff --git a/test/pickle1.py b/test/pickle1.py index 1aeabc70..d333919d 100644 --- a/test/pickle1.py +++ b/test/pickle1.py @@ -18,13 +18,14 @@ r'''>>> import pickle1_ext ''' def run(args = None): - if args is not None: - import sys - sys.argv = args - import doctest, pickle1 - return doctest.testmod(pickle1) + import sys + import doctest + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + if __name__ == '__main__': + print "running..." import sys sys.exit(run()[0]) - diff --git a/test/pickle2.cpp b/test/pickle2.cpp index fed1066b..916f99cf 100644 --- a/test/pickle2.cpp +++ b/test/pickle2.cpp @@ -47,7 +47,7 @@ namespace { // Avoid cluttering the global namespace. int secret_number; }; - struct world_pickle_support : boost::python::pickle_support_base + struct world_pickle_group : boost::python::pickle_group { static boost::python::tuple @@ -95,7 +95,7 @@ BOOST_PYTHON_MODULE_INIT(pickle2_ext) .def("greet", &world::greet) .def("get_secret_number", &world::get_secret_number) .def("set_secret_number", &world::set_secret_number) - .pickle_support(world_pickle_support()) + .def_pickle(world_pickle_group()) ) ; } diff --git a/test/pickle2.py b/test/pickle2.py index 7d31e48b..ec141bc9 100644 --- a/test/pickle2.py +++ b/test/pickle2.py @@ -32,13 +32,14 @@ r'''>>> import pickle2_ext ''' def run(args = None): - if args is not None: - import sys - sys.argv = args - import doctest, pickle2 - return doctest.testmod(pickle2) + import sys + import doctest + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + if __name__ == '__main__': + print "running..." import sys sys.exit(run()[0]) - diff --git a/test/pickle3.cpp b/test/pickle3.cpp index 617143d3..b6d00a82 100644 --- a/test/pickle3.cpp +++ b/test/pickle3.cpp @@ -43,7 +43,7 @@ namespace { // Avoid cluttering the global namespace. int secret_number; }; - struct world_pickle_support : boost::python::pickle_support_base + struct world_pickle_group : boost::python::pickle_group { static boost::python::tuple @@ -101,7 +101,7 @@ BOOST_PYTHON_MODULE_INIT(pickle3_ext) .def("greet", &world::greet) .def("get_secret_number", &world::get_secret_number) .def("set_secret_number", &world::set_secret_number) - .pickle_support(world_pickle_support()) + .def_pickle(world_pickle_group()) ) ; } diff --git a/test/pickle3.py b/test/pickle3.py index 8cce8dfb..b700c1b0 100644 --- a/test/pickle3.py +++ b/test/pickle3.py @@ -27,12 +27,14 @@ r'''>>> import pickle3_ext ''' def run(args = None): - if args is not None: - import sys - sys.argv = args - import doctest, pickle3 - return doctest.testmod(pickle3) + import sys + import doctest + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + if __name__ == '__main__': + print "running..." import sys sys.exit(run()[0])