mirror of
https://github.com/boostorg/python.git
synced 2026-01-22 05:22:45 +00:00
additional files for pickle support; no modification of any existing files
[SVN r14549]
This commit is contained in:
96
include/boost/python/object/pickle_support.hpp
Normal file
96
include/boost/python/object/pickle_support.hpp
Normal file
@@ -0,0 +1,96 @@
|
||||
// (C) Copyright R.W. Grosse-Kunstleve 2002.
|
||||
// Permission to copy, use, modify, sell and distribute this software
|
||||
// is granted provided this copyright notice appears in all copies. This
|
||||
// software is provided "as is" without express or implied warranty, and
|
||||
// with no claim as to its suitability for any purpose.
|
||||
#ifndef BOOST_PYTHON_OBJECT_PICKLE_SUPPORT_RWGK20020603_HPP
|
||||
#define BOOST_PYTHON_OBJECT_PICKLE_SUPPORT_RWGK20020603_HPP
|
||||
|
||||
#include <boost/python/object/class.hpp>
|
||||
#include <boost/python/tuple.hpp>
|
||||
|
||||
namespace boost { namespace python {
|
||||
|
||||
handle<> make_instance_reduce_function();
|
||||
|
||||
namespace error_messages {
|
||||
|
||||
template <class T>
|
||||
struct missing_pickle_support_function_or_incorrect_signature {};
|
||||
|
||||
}
|
||||
|
||||
class pickle_support_base
|
||||
{
|
||||
private:
|
||||
struct dummy_return_type_ {};
|
||||
|
||||
public:
|
||||
template <class Class_, class Tgetinitargs>
|
||||
static
|
||||
void
|
||||
register_(
|
||||
Class_& cl,
|
||||
tuple (*getinitargs_fn)(Tgetinitargs),
|
||||
dummy_return_type_* (*getstate_fn)(),
|
||||
dummy_return_type_* (*setstate_fn)(),
|
||||
bool)
|
||||
{
|
||||
cl.enable_pickle_support(false);
|
||||
cl.def("__getinitargs__", getinitargs_fn);
|
||||
}
|
||||
|
||||
template <class Class_, class Tgetstate, class Tsetstate>
|
||||
static
|
||||
void
|
||||
register_(
|
||||
Class_& cl,
|
||||
dummy_return_type_* (*getinitargs_fn)(),
|
||||
tuple (*getstate_fn)(Tgetstate),
|
||||
void (*setstate_fn)(Tsetstate, object),
|
||||
bool getstate_manages_dict)
|
||||
{
|
||||
cl.enable_pickle_support(getstate_manages_dict);
|
||||
cl.def("__getstate__", getstate_fn);
|
||||
cl.def("__setstate__", setstate_fn);
|
||||
}
|
||||
|
||||
template <class Class_,
|
||||
class Tgetinitargs, class Tgetstate, class Tsetstate>
|
||||
static
|
||||
void
|
||||
register_(
|
||||
Class_& cl,
|
||||
tuple (*getinitargs_fn)(Tgetinitargs),
|
||||
tuple (*getstate_fn)(Tgetstate),
|
||||
void (*setstate_fn)(Tsetstate, object),
|
||||
bool getstate_manages_dict)
|
||||
{
|
||||
cl.enable_pickle_support(getstate_manages_dict);
|
||||
cl.def("__getinitargs__", getinitargs_fn);
|
||||
cl.def("__getstate__", getstate_fn);
|
||||
cl.def("__setstate__", setstate_fn);
|
||||
}
|
||||
|
||||
template <class Class_>
|
||||
static
|
||||
void
|
||||
register_(
|
||||
Class_&,
|
||||
...)
|
||||
{
|
||||
typedef typename
|
||||
error_messages::missing_pickle_support_function_or_incorrect_signature<
|
||||
Class_>::error_type error_type;
|
||||
}
|
||||
|
||||
static dummy_return_type_* getinitargs() { return 0; }
|
||||
static dummy_return_type_* getstate() { return 0; }
|
||||
static dummy_return_type_* setstate() { return 0; }
|
||||
|
||||
static bool getstate_manages_dict() { return false; }
|
||||
};
|
||||
|
||||
}} // namespace boost::python
|
||||
|
||||
#endif // BOOST_PYTHON_OBJECT_PICKLE_SUPPORT_RWGK20020603_HPP
|
||||
63
src/object/pickle_support.cpp
Normal file
63
src/object/pickle_support.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
// (C) Copyright R.W. Grosse-Kunstleve 2002.
|
||||
// Permission to copy, use, modify, sell and distribute this software
|
||||
// is granted provided this copyright notice appears in all copies. This
|
||||
// software is provided "as is" without express or implied warranty, and
|
||||
// with no claim as to its suitability for any purpose.
|
||||
|
||||
#include <boost/python/make_function.hpp>
|
||||
#include <boost/python/object/class.hpp>
|
||||
#include <boost/python/tuple.hpp>
|
||||
#include <boost/python/list.hpp>
|
||||
#include <boost/python/dict.hpp>
|
||||
#include <boost/python/detail/api_placeholder.hpp>
|
||||
|
||||
namespace boost { namespace python {
|
||||
|
||||
namespace {
|
||||
|
||||
tuple instance_reduce(object instance_obj)
|
||||
{
|
||||
list result;
|
||||
object instance_class(instance_obj.attr("__class__"));
|
||||
result.append(instance_class);
|
||||
object none;
|
||||
object getinitargs = getattr(instance_obj, "__getinitargs__", none);
|
||||
tuple initargs;
|
||||
if (getinitargs.ptr() != none.ptr()) {
|
||||
initargs = tuple(getinitargs());
|
||||
}
|
||||
result.append(initargs);
|
||||
object getstate = getattr(instance_obj, "__getstate__", none);
|
||||
object instance_dict = getattr(instance_obj, "__dict__", none);
|
||||
long len_instance_dict = 0;
|
||||
if (instance_dict.ptr() != none.ptr()) {
|
||||
len_instance_dict = len(instance_dict);
|
||||
}
|
||||
if (getstate.ptr() != none.ptr()) {
|
||||
if (len_instance_dict > 0) {
|
||||
object getstate_manages_dict = getattr(
|
||||
instance_obj, "__getstate_manages_dict__", none);
|
||||
if (getstate_manages_dict.ptr() == none.ptr()) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"Incomplete pickle support"
|
||||
" (__getstate_manages_dict__ not set)");
|
||||
throw_error_already_set();
|
||||
}
|
||||
}
|
||||
result.append(getstate());
|
||||
}
|
||||
else if (len_instance_dict > 0) {
|
||||
result.append(instance_dict);
|
||||
}
|
||||
return tuple(result);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
handle<> make_instance_reduce_function()
|
||||
{
|
||||
static handle<> result(make_function(&instance_reduce));
|
||||
return result;
|
||||
}
|
||||
|
||||
}} // namespace boost::python
|
||||
60
test/pickle1.cpp
Normal file
60
test/pickle1.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
// Example by Ralf W. Grosse-Kunstleve
|
||||
|
||||
/*
|
||||
This example shows how to make an Extension Class "pickleable".
|
||||
|
||||
The world class below can be fully restored by passing the
|
||||
appropriate argument to the constructor. Therefore it is sufficient
|
||||
to define the pickle interface method __getinitargs__.
|
||||
|
||||
For more information refer to boost/libs/python/doc/pickle.html.
|
||||
*/
|
||||
|
||||
#include <boost/python/module.hpp>
|
||||
#include <boost/python/class.hpp>
|
||||
#include <boost/python/list.hpp>
|
||||
#include <boost/python/tuple.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
struct world_pickle_support : boost::python::pickle_support_base
|
||||
{
|
||||
static
|
||||
boost::python::tuple
|
||||
getinitargs(const world& w)
|
||||
{
|
||||
using namespace boost::python;
|
||||
list result;
|
||||
result.append(object(w.get_country()));
|
||||
return tuple(result);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
BOOST_PYTHON_MODULE_INIT(pickle1_ext)
|
||||
{
|
||||
using namespace boost::python;
|
||||
module("pickle1_ext")
|
||||
.add(class_<world>("world")
|
||||
.def_init(args<const std::string&>())
|
||||
.def("greet", &world::greet)
|
||||
.pickle_support(world_pickle_support())
|
||||
)
|
||||
;
|
||||
}
|
||||
30
test/pickle1.py
Normal file
30
test/pickle1.py
Normal file
@@ -0,0 +1,30 @@
|
||||
r'''>>> import pickle1_ext
|
||||
>>> import pickle
|
||||
>>> pickle1_ext.world.__module__
|
||||
'pickle1_ext'
|
||||
>>> pickle1_ext.world.__safe_for_unpickling__
|
||||
1
|
||||
>>> pickle1_ext.world.__name__
|
||||
'world'
|
||||
>>> pickle1_ext.world('Hello').__reduce__()
|
||||
(<class 'pickle1_ext.world'>, ('Hello',))
|
||||
>>> wd = pickle1_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):
|
||||
if args is not None:
|
||||
import sys
|
||||
sys.argv = args
|
||||
import doctest, pickle1
|
||||
return doctest.testmod(pickle1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
sys.exit(run()[0])
|
||||
|
||||
101
test/pickle2.cpp
Normal file
101
test/pickle2.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
// Example by Ralf W. Grosse-Kunstleve
|
||||
|
||||
/*
|
||||
This example shows how to make an Extension Class "pickleable".
|
||||
|
||||
The world class below contains member data (secret_number) that
|
||||
cannot be restored by any of the constructors. Therefore it is
|
||||
necessary to provide the __getstate__/__setstate__ pair of pickle
|
||||
interface methods.
|
||||
|
||||
For simplicity, the __dict__ is not included in the result of
|
||||
__getstate__. This is not generally recommended, but a valid
|
||||
approach if it is anticipated that the object's __dict__ will
|
||||
always be empty. Note that safety guards are provided to catch
|
||||
the cases where this assumption is not true.
|
||||
|
||||
pickle3.cpp shows how to include the object's __dict__ in the
|
||||
result of __getstate__.
|
||||
|
||||
For more information refer to boost/libs/python/doc/pickle.html.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/python/module.hpp>
|
||||
#include <boost/python/class.hpp>
|
||||
#include <boost/python/list.hpp>
|
||||
#include <boost/python/tuple.hpp>
|
||||
#include <boost/python/extract.hpp>
|
||||
#include <boost/python/detail/api_placeholder.hpp>
|
||||
|
||||
namespace { // Avoid cluttering the global namespace.
|
||||
|
||||
// A friendly class.
|
||||
class world
|
||||
{
|
||||
public:
|
||||
world(const std::string& country) : secret_number(0) {
|
||||
this->country = country;
|
||||
}
|
||||
std::string greet() const { return "Hello from " + country + "!"; }
|
||||
std::string get_country() const { return country; }
|
||||
void set_secret_number(int number) { secret_number = number; }
|
||||
int get_secret_number() const { return secret_number; }
|
||||
private:
|
||||
std::string country;
|
||||
int secret_number;
|
||||
};
|
||||
|
||||
struct world_pickle_support : boost::python::pickle_support_base
|
||||
{
|
||||
static
|
||||
boost::python::tuple
|
||||
getinitargs(const world& w)
|
||||
{
|
||||
using namespace boost::python;
|
||||
list result;
|
||||
result.append(object(w.get_country()));
|
||||
return tuple(result);
|
||||
}
|
||||
|
||||
static
|
||||
boost::python::tuple
|
||||
getstate(const world& w)
|
||||
{
|
||||
using namespace boost::python;
|
||||
list result;
|
||||
result.append(object(w.get_secret_number()));
|
||||
return tuple(result);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
setstate(world& w, boost::python::object state)
|
||||
{
|
||||
using namespace boost::python;
|
||||
extract<tuple> state_proxy(state);
|
||||
if (!state_proxy.check() || len(state_proxy()) != 1) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Unexpected argument in call to __setstate__.");
|
||||
throw_error_already_set();
|
||||
}
|
||||
long number = extract<long>(state_proxy()[0])();
|
||||
if (number != 42) w.set_secret_number(number);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
BOOST_PYTHON_MODULE_INIT(pickle2_ext)
|
||||
{
|
||||
boost::python::module("pickle2_ext")
|
||||
.add(boost::python::class_<world>("world")
|
||||
.def_init(boost::python::args<const std::string&>())
|
||||
.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())
|
||||
)
|
||||
;
|
||||
}
|
||||
44
test/pickle2.py
Normal file
44
test/pickle2.py
Normal file
@@ -0,0 +1,44 @@
|
||||
r'''>>> import pickle2_ext
|
||||
>>> import pickle
|
||||
>>> pickle2_ext.world.__module__
|
||||
'pickle2_ext'
|
||||
>>> pickle2_ext.world.__safe_for_unpickling__
|
||||
1
|
||||
>>> pickle2_ext.world.__name__
|
||||
'world'
|
||||
>>> pickle2_ext.world('Hello').__reduce__()
|
||||
(<class 'pickle2_ext.world'>, ('Hello',), (0,))
|
||||
>>> for number in (24, 42):
|
||||
... wd = pickle2_ext.world('California')
|
||||
... wd.set_secret_number(number)
|
||||
... pstr = pickle.dumps(wd)
|
||||
... wl = pickle.loads(pstr)
|
||||
... print wd.greet(), wd.get_secret_number()
|
||||
... print wl.greet(), wl.get_secret_number()
|
||||
Hello from California! 24
|
||||
Hello from California! 24
|
||||
Hello from California! 42
|
||||
Hello from California! 0
|
||||
|
||||
# Now show that the __dict__ is not taken care of.
|
||||
>>> wd = pickle2_ext.world('California')
|
||||
>>> wd.x = 1
|
||||
>>> wd.__dict__
|
||||
{'x': 1}
|
||||
>>> try: pstr = pickle.dumps(wd)
|
||||
... except RuntimeError, err: print err[0]
|
||||
...
|
||||
Incomplete pickle support (__getstate_manages_dict__ not set)
|
||||
'''
|
||||
|
||||
def run(args = None):
|
||||
if args is not None:
|
||||
import sys
|
||||
sys.argv = args
|
||||
import doctest, pickle2
|
||||
return doctest.testmod(pickle2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
sys.exit(run()[0])
|
||||
|
||||
107
test/pickle3.cpp
Normal file
107
test/pickle3.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
// Example by Ralf W. Grosse-Kunstleve
|
||||
|
||||
/*
|
||||
This example shows how to make an Extension Class "pickleable".
|
||||
|
||||
The world class below contains member data (secret_number) that
|
||||
cannot be restored by any of the constructors. Therefore it is
|
||||
necessary to provide the __getstate__/__setstate__ pair of pickle
|
||||
interface methods.
|
||||
|
||||
The object's __dict__ is included in the result of __getstate__.
|
||||
This requires more code (compare with pickle2.cpp), but is
|
||||
unavoidable if the object's __dict__ is not always empty.
|
||||
|
||||
For more information refer to boost/libs/python/doc/pickle.html.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/python/module.hpp>
|
||||
#include <boost/python/class.hpp>
|
||||
#include <boost/python/list.hpp>
|
||||
#include <boost/python/tuple.hpp>
|
||||
#include <boost/python/dict.hpp>
|
||||
#include <boost/python/extract.hpp>
|
||||
#include <boost/python/detail/api_placeholder.hpp>
|
||||
|
||||
namespace { // Avoid cluttering the global namespace.
|
||||
|
||||
// A friendly class.
|
||||
class world
|
||||
{
|
||||
public:
|
||||
world(const std::string& country) : secret_number(0) {
|
||||
this->country = country;
|
||||
}
|
||||
std::string greet() const { return "Hello from " + country + "!"; }
|
||||
std::string get_country() const { return country; }
|
||||
void set_secret_number(int number) { secret_number = number; }
|
||||
int get_secret_number() const { return secret_number; }
|
||||
private:
|
||||
std::string country;
|
||||
int secret_number;
|
||||
};
|
||||
|
||||
struct world_pickle_support : boost::python::pickle_support_base
|
||||
{
|
||||
static
|
||||
boost::python::tuple
|
||||
getinitargs(const world& w)
|
||||
{
|
||||
using namespace boost::python;
|
||||
list result;
|
||||
result.append(object(w.get_country()));
|
||||
return tuple(result);
|
||||
}
|
||||
|
||||
static
|
||||
boost::python::tuple
|
||||
getstate(boost::python::object w_obj)
|
||||
{
|
||||
using namespace boost::python;
|
||||
world const& w = extract<world const&>(w_obj)();
|
||||
list result;
|
||||
// store the object's __dict__
|
||||
result.append(w_obj.attr("__dict__"));
|
||||
// store the internal state of the C++ object
|
||||
result.append(object(w.get_secret_number()));
|
||||
return tuple(result);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
setstate(boost::python::object w_obj, boost::python::object state)
|
||||
{
|
||||
using namespace boost::python;
|
||||
world& w = extract<world&>(w_obj)();
|
||||
extract<tuple> state_proxy(state);
|
||||
if (!state_proxy.check() || len(state_proxy()) != 2) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Unexpected argument in call to __setstate__.");
|
||||
throw_error_already_set();
|
||||
}
|
||||
// restore the object's __dict__
|
||||
w_obj.attr("__dict__").attr("update")(object(state_proxy()[0]));
|
||||
// restore the internal state of the C++ object
|
||||
long number = extract<long>(state_proxy()[1])();
|
||||
if (number != 42) w.set_secret_number(number);
|
||||
}
|
||||
|
||||
static bool getstate_manages_dict() { return true; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
BOOST_PYTHON_MODULE_INIT(pickle3_ext)
|
||||
{
|
||||
boost::python::module("pickle3_ext")
|
||||
.add(boost::python::class_<world>("world")
|
||||
.def_init(boost::python::args<const std::string&>())
|
||||
.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())
|
||||
)
|
||||
;
|
||||
}
|
||||
38
test/pickle3.py
Normal file
38
test/pickle3.py
Normal file
@@ -0,0 +1,38 @@
|
||||
r'''>>> import pickle3_ext
|
||||
>>> import pickle
|
||||
>>> pickle3_ext.world.__module__
|
||||
'pickle3_ext'
|
||||
>>> pickle3_ext.world.__safe_for_unpickling__
|
||||
1
|
||||
>>> pickle3_ext.world.__getstate_manages_dict__
|
||||
1
|
||||
>>> pickle3_ext.world.__name__
|
||||
'world'
|
||||
>>> pickle3_ext.world('Hello').__reduce__()
|
||||
(<class 'pickle3_ext.world'>, ('Hello',), ({}, 0))
|
||||
>>> for number in (24, 42):
|
||||
... wd = pickle3_ext.world('California')
|
||||
... wd.set_secret_number(number)
|
||||
... wd.x = 2 * number
|
||||
... wd.y = 'y' * number
|
||||
... wd.z = 3. * number
|
||||
... pstr = pickle.dumps(wd)
|
||||
... wl = pickle.loads(pstr)
|
||||
... print wd.greet(), wd.get_secret_number(), wd.x, wd.y, wd.z
|
||||
... print wl.greet(), wl.get_secret_number(), wl.x, wl.y, wl.z
|
||||
Hello from California! 24 48 yyyyyyyyyyyyyyyyyyyyyyyy 72.0
|
||||
Hello from California! 24 48 yyyyyyyyyyyyyyyyyyyyyyyy 72.0
|
||||
Hello from California! 42 84 yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy 126.0
|
||||
Hello from California! 0 84 yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy 126.0
|
||||
'''
|
||||
|
||||
def run(args = None):
|
||||
if args is not None:
|
||||
import sys
|
||||
sys.argv = args
|
||||
import doctest, pickle3
|
||||
return doctest.testmod(pickle3)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
sys.exit(run()[0])
|
||||
Reference in New Issue
Block a user