From b06d9e50ebf64969d198ab03430d21eb40cb8442 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 3 Mar 2001 12:55:53 +0000 Subject: [PATCH] added: pickle safety measures; bug fix: use bound_function::create() [SVN r9399] --- include/boost/python/class_builder.hpp | 10 ++++++ src/classes.cpp | 46 ++++++++++++++++++++------ test/comprehensive.py | 41 +++++++++++++++++++++++ 3 files changed, 87 insertions(+), 10 deletions(-) diff --git a/include/boost/python/class_builder.hpp b/include/boost/python/class_builder.hpp index 7ef843c4..4a9ec1b2 100644 --- a/include/boost/python/class_builder.hpp +++ b/include/boost/python/class_builder.hpp @@ -1,3 +1,6 @@ +// Revision History: +// Mar 03 01 added: pickle safety measures (Ralf W. Grosse-Kunstleve) + #ifndef CLASS_WRAPPER_DWA101000_H_ # define CLASS_WRAPPER_DWA101000_H_ @@ -24,6 +27,13 @@ class class_builder ~class_builder() {} + + inline void dict_defines_state() { + add(ref(BOOST_PYTHON_CONVERSION::to_python(1)), "__dict_defines_state__"); + } + inline void getstate_manages_dict() { + add(ref(BOOST_PYTHON_CONVERSION::to_python(1)), "__getstate_manages_dict__"); + } // define constructors template diff --git a/src/classes.cpp b/src/classes.cpp index 2901aa81..625d67b7 100644 --- a/src/classes.cpp +++ b/src/classes.cpp @@ -5,6 +5,10 @@ // // The author gratefully acknowleges the support of Dragon Systems, Inc., in // producing this work. +// +// Revision History: +// Mar 03 01 added: pickle safety measures (Ralf W. Grosse-Kunstleve) +// Mar 03 01 bug fix: use bound_function::create() (instead of new bound_function) #include #include @@ -67,8 +71,7 @@ namespace { ref global_class_reduce() { - static ref result(detail::new_wrapped_function(class_reduce)); - return result; + return ref(detail::new_wrapped_function(class_reduce)); } @@ -93,17 +96,41 @@ namespace { ref getstate(PyObject_GetAttrString(obj, const_cast("__getstate__")), ref::null_ok); PyErr_Clear(); + + ref dict(PyObject_GetAttrString(obj, const_cast("__dict__")), ref::null_ok); + PyErr_Clear(); + if (getstate.get() != 0) { + if (dict.get() != 0 && dictionary(dict).size() > 0) + { + ref getstate_manages_dict(PyObject_GetAttrString(instance_class.get(), const_cast("__getstate_manages_dict__")), ref::null_ok); + PyErr_Clear(); + if (getstate_manages_dict.get() == 0) + { + PyErr_SetString(PyExc_RuntimeError, "Incomplete pickle support (__getstate_manages_dict__ not set)"); + throw error_already_set(); + } + } + ref state = ref(PyEval_CallObject(getstate.get(), NULL)); return tuple(instance_class, initargs, state); } - ref state(PyObject_GetAttrString(obj, const_cast("__dict__")), ref::null_ok); - PyErr_Clear(); - if (state.get() != 0 && dictionary(state).size() > 0) + if (getinitargs.get() == 0) { - return tuple(instance_class, initargs, state); + ref dict_defines_state(PyObject_GetAttrString(instance_class.get(), const_cast("__dict_defines_state__")), ref::null_ok); + PyErr_Clear(); + if (dict_defines_state.get() == 0) + { + PyErr_SetString(PyExc_RuntimeError, "Incomplete pickle support (__dict_defines_state__ not set)"); + throw error_already_set(); + } + } + + if (dict.get() != 0 && dictionary(dict).size() > 0) + { + return tuple(instance_class, initargs, dict); } return tuple(instance_class, initargs); @@ -111,8 +138,7 @@ namespace { ref global_instance_reduce() { - static ref result(detail::new_wrapped_function(instance_reduce)); - return result; + return ref(detail::new_wrapped_function(instance_reduce)); } } @@ -177,7 +203,7 @@ namespace detail { if (!BOOST_CSTD_::strcmp(name, "__reduce__")) { ref target(as_object(this), ref::increment_count); - return new bound_function(target, global_class_reduce()); + return bound_function::create(target, global_class_reduce()); } ref local_attribute = m_name_space.get_item(string(name).reference()); @@ -348,7 +374,7 @@ PyObject* instance::getattr(const char* name, bool use_special_function) if (!BOOST_CSTD_::strcmp(name, "__reduce__")) { - return new detail::bound_function(ref(this, ref::increment_count), global_instance_reduce()); + return detail::bound_function::create(ref(this, ref::increment_count), global_instance_reduce()); } ref local_attribute = m_name_space.get_item(string(name).reference()); diff --git a/test/comprehensive.py b/test/comprehensive.py index 8bc8021c..3c7f3c61 100644 --- a/test/comprehensive.py +++ b/test/comprehensive.py @@ -239,6 +239,47 @@ Pickling tests: Hello from California! 42 Hello from California! 0 +Pickle safety measures: + >>> r=Rational(3, 4) + >>> r + Rational(3, 4) + >>> try: s=pickle.dumps(r) + ... except RuntimeError, err: print err[0] + ... + Incomplete pickle support (__dict_defines_state__ not set) + >>> class myrational(Rational): + ... __dict_defines_state__ = 1 # this is a lie but good enough for testing. + ... + >>> r=myrational(3, 4) + >>> r + Rational(3, 4) + >>> s=pickle.dumps(r) + + >>> class myworld(world): + ... def __init__(self): + ... world.__init__(self, 'anywhere') + ... self.x = 1 + ... + >>> w = myworld() + >>> w.greet() + 'Hello from anywhere!' + >>> w.__dict__ + {'x': 1} + >>> try: s=pickle.dumps(w) + ... except RuntimeError, err: print err[0] + ... + Incomplete pickle support (__getstate_manages_dict__ not set) + + >>> class myunsafeworld(myworld): + ... __getstate_manages_dict__ = 1 # this is a lie but good enough for testing. + ... + >>> w = myunsafeworld() + >>> w.greet() + 'Hello from anywhere!' + >>> w.__dict__ + {'x': 1} + >>> s=pickle.dumps(w) + Special member attributes. Tests courtesy of Barry Scott >>> class DerivedFromFoo(Foo):