2
0
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:
Ralf W. Grosse-Kunstleve
2002-07-21 07:49:00 +00:00
parent d27e5a5e1d
commit 4ef5f77161
8 changed files with 539 additions and 0 deletions

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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])