From ae1584ff3c95deffb311b56da433e1d87830b0dd Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 13 Feb 2004 05:32:38 +0000 Subject: [PATCH] class_::enable_pickling() in publicized interface; tested with everything incl. VC6 and 7.0 [SVN r22254] --- doc/v2/class.html | 18 ++++++++ doc/v2/pickle.html | 42 ++++++++++++++++++- include/boost/python/class.hpp | 6 +++ include/boost/python/object/class.hpp | 2 +- .../boost/python/object/pickle_support.hpp | 6 +-- src/object/class.cpp | 2 +- test/Jamfile | 1 + test/pickle4.cpp | 41 ++++++++++++++++++ test/pickle4.py | 36 ++++++++++++++++ 9 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 test/pickle4.cpp create mode 100644 test/pickle4.py diff --git a/doc/v2/class.html b/doc/v2/class.html index d9105e0b..46c6320d 100644 --- a/doc/v2/class.html +++ b/doc/v2/class.html @@ -281,6 +281,7 @@ namespace boost { namespace python // pickle support template <typename PickleSuite> self& def_pickle(PickleSuite const&); + self& enable_pickling(); }; }} @@ -709,6 +710,23 @@ class_& def_pickle(PickleSuite const&); checks.
+
+class_& enable_pickling();
+
+ +
+
Requires: n/a
+ +
Effects: Defines the __reduce__ method and + the __safe_for_unpickling__ attribute. + +
Returns: *this
+ +
Rationale: Light-weight alternative to + def_pickle(). Enables implementation of + pickle support from Python.
+
+

Class template diff --git a/doc/v2/pickle.html b/doc/v2/pickle.html index 13c7b306..047231f4 100644 --- a/doc/v2/pickle.html +++ b/doc/v2/pickle.html @@ -281,13 +281,51 @@ is not empty.
+

Light-weight alternative: pickle support implemented in Python

-© Copyright Ralf W. Grosse-Kunstleve 20012-2002. Permission to copy, +

pickle4.cpp

+ +The pickle4.cpp example demonstrates an alternative technique +for implementing pickle support. First we direct Boost.Python via +the class_::enable_pickling() member function to define only +the basic attributes required for pickling: + +
+  class_<world>("world", args<const std::string&>())
+      // ...
+      .enable_pickling()
+      // ...
+
+ +This enables the standard Python pickle interface as described +in the Python documentation. By "injecting" a +__getinitargs__ method into the definition of the wrapped +class we make all instances pickleable: + +
+  # import the wrapped world class
+  from pickle4_ext import world
+
+  # definition of __getinitargs__
+  def world_getinitargs(self):
+    return (self.get_country(),)
+
+  # now inject __getinitargs__ (Python is a dynamic language!)
+  world.__getinitargs__ = world_getinitargs
+
+ +See also the +tutorial section on injecting additional methods from Python. + +
+ +© Copyright Ralf W. Grosse-Kunstleve 2001-2004. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.

-Updated: Aug 2002. +Updated: Feb 2004. diff --git a/include/boost/python/class.hpp b/include/boost/python/class.hpp index 4974ec86..31805a34 100644 --- a/include/boost/python/class.hpp +++ b/include/boost/python/class.hpp @@ -415,6 +415,12 @@ class class_ : public objects::class_base return *this; } + self& enable_pickling() + { + this->base::enable_pickling_(false); + return *this; + } + self& staticmethod(char const* name) { this->make_method_static(name); diff --git a/include/boost/python/object/class.hpp b/include/boost/python/object/class.hpp index 45bffbf7..22692011 100644 --- a/include/boost/python/object/class.hpp +++ b/include/boost/python/object/class.hpp @@ -32,7 +32,7 @@ struct BOOST_PYTHON_DECL class_base : python::api::object // Implementation detail. Hiding this in the private section would // require use of template friend declarations. - void enable_pickling(bool getstate_manages_dict); + void enable_pickling_(bool getstate_manages_dict); protected: void add_property(char const* name, object const& fget); diff --git a/include/boost/python/object/pickle_support.hpp b/include/boost/python/object/pickle_support.hpp index cf1c5e99..ea81e8df 100644 --- a/include/boost/python/object/pickle_support.hpp +++ b/include/boost/python/object/pickle_support.hpp @@ -59,7 +59,7 @@ namespace detail { inaccessible* (*setstate_fn)(), bool) { - cl.enable_pickling(false); + cl.enable_pickling_(false); cl.def("__getinitargs__", getinitargs_fn); } @@ -75,7 +75,7 @@ namespace detail { void (*setstate_fn)(Tsetstate, Ttuple), bool getstate_manages_dict) { - cl.enable_pickling(getstate_manages_dict); + cl.enable_pickling_(getstate_manages_dict); cl.def("__getstate__", getstate_fn); cl.def("__setstate__", setstate_fn); } @@ -93,7 +93,7 @@ namespace detail { void (*setstate_fn)(Tsetstate, Ttuple), bool getstate_manages_dict) { - cl.enable_pickling(getstate_manages_dict); + cl.enable_pickling_(getstate_manages_dict); cl.def("__getinitargs__", getinitargs_fn); cl.def("__getstate__", getstate_fn); cl.def("__setstate__", setstate_fn); diff --git a/src/object/class.cpp b/src/object/class.cpp index ea667532..97bd7bbe 100644 --- a/src/object/class.cpp +++ b/src/object/class.cpp @@ -599,7 +599,7 @@ namespace objects this->setattr("__init__", object(f)); } - void class_base::enable_pickling(bool getstate_manages_dict) + void class_base::enable_pickling_(bool getstate_manages_dict) { setattr("__reduce__", object(make_instance_reduce_function())); setattr("__safe_for_unpickling__", object(true)); diff --git a/test/Jamfile b/test/Jamfile index a8a1c67b..87f8688a 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -151,6 +151,7 @@ bpl-test crossmod_exception [ bpl-test pickle1 ] [ bpl-test pickle2 ] [ bpl-test pickle3 ] +[ bpl-test pickle4 ] [ bpl-test nested ] diff --git a/test/pickle4.cpp b/test/pickle4.cpp new file mode 100644 index 00000000..21fe9619 --- /dev/null +++ b/test/pickle4.cpp @@ -0,0 +1,41 @@ +// Example by Ralf W. Grosse-Kunstleve + +/* + This example shows how to enable pickling without using the + pickle_suite. The pickling interface (__getinitargs__) is + implemented in Python. + + For more information refer to boost/libs/python/doc/pickle.html. + */ + +#include +#include + +#include + +namespace { + + // A friendly class. + class world + { + private: + std::string country; + public: + world(const std::string& country) { + this->country = country; + } + std::string greet() const { return "Hello from " + country + "!"; } + std::string get_country() const { return country; } + }; + +} + +BOOST_PYTHON_MODULE(pickle4_ext) +{ + using namespace boost::python; + class_("world", init()) + .enable_pickling() + .def("greet", &world::greet) + .def("get_country", &world::get_country) + ; +} diff --git a/test/pickle4.py b/test/pickle4.py new file mode 100644 index 00000000..a203f1f1 --- /dev/null +++ b/test/pickle4.py @@ -0,0 +1,36 @@ +r'''>>> import pickle4_ext + >>> import pickle + >>> def world_getinitargs(self): + ... return (self.get_country(),) + >>> pickle4_ext.world.__getinitargs__ = world_getinitargs + >>> pickle4_ext.world.__module__ + 'pickle4_ext' + >>> pickle4_ext.world.__safe_for_unpickling__ + 1 + >>> pickle4_ext.world.__name__ + 'world' + >>> pickle4_ext.world('Hello').__reduce__() + (, ('Hello',)) + >>> wd = pickle4_ext.world('California') + >>> pstr = pickle.dumps(wd) + >>> wl = pickle.loads(pstr) + >>> print wd.greet() + Hello from California! + >>> print wl.greet() + Hello from California! +''' + +def run(args = None): + 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 + status = run()[0] + if (status == 0): print "Done." + sys.exit(status)