From b9fc4f2384b5d690e279efbe1848f7efec1cb107 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Fri, 17 Nov 2000 20:34:02 +0000 Subject: [PATCH] Pickling support [SVN r8248] --- release_notes.txt | 12 ++++++ subclass.cpp | 93 +++++++++++++++++++++++++++++++++++++++-------- subclass.h | 2 +- test_extclass.py | 33 +++++++++++++++++ 4 files changed, 123 insertions(+), 17 deletions(-) diff --git a/release_notes.txt b/release_notes.txt index 0573a49f..244c7c02 100644 --- a/release_notes.txt +++ b/release_notes.txt @@ -1,3 +1,15 @@ +2000-11-17 15:04 + Added pickling support + tests thanks to "Ralf W. Grosse-Kunstleve" + + + Added a specialization of Callback to prevent unsafe usage. + + Fixed Ullrich's operator_dispatcher refcount bug + + Removed const char* return values from virtual functions in tests; that + usage was unsafe. + + 2000-11-15 12:01 Fixed Ullrich's refcount bug diff --git a/subclass.cpp b/subclass.cpp index 6c43ee5b..d590e17e 100644 --- a/subclass.cpp +++ b/subclass.cpp @@ -12,6 +12,7 @@ #include #include "callback.h" #include +#include "module.h" namespace py { @@ -55,6 +56,64 @@ namespace { Py_XDECREF(v); Py_XDECREF(tb); } + + // + // pickle support courtesy of "Ralf W. Grosse-Kunstleve" + // + PyObject* class_reduce(PyObject* klass) + { + return PyObject_GetAttrString(klass, "__name__"); + } + + Ptr global_class_reduce() + { + static Ptr result(py::new_wrapped_function(class_reduce)); + return result; + } + + + Tuple instance_reduce(PyObject* instance) + { + Ptr instance_class(PyObject_GetAttrString(instance, "__class__")); + + Ptr getinitargs(PyObject_GetAttrString(instance, "__getinitargs__"), + Ptr::null_ok); + PyErr_Clear(); + Ptr initargs; + if (getinitargs.get() != 0) + { + initargs = Ptr(PyEval_CallObject(getinitargs.get(), NULL)); + initargs = Ptr(PySequence_Tuple(initargs.get())); + } + else + { + initargs = Ptr(PyTuple_New(0)); + } + + Ptr getstate(PyObject_GetAttrString(instance, "__getstate__"), + Ptr::null_ok); + PyErr_Clear(); + if (getstate.get() != 0) + { + Ptr state = Ptr(PyEval_CallObject(getstate.get(), NULL)); + return Tuple(instance_class, initargs, state); + } + + Ptr state(PyObject_GetAttrString(instance, "__dict__"), Ptr::null_ok); + PyErr_Clear(); + if (state.get() != 0) + { + return Tuple(instance_class, initargs, state); + } + + return Tuple(instance_class, initargs); + } + + Ptr global_instance_reduce() + { + static Ptr result(py::new_wrapped_function(instance_reduce)); + return result; + } } @@ -87,7 +146,7 @@ namespace detail { m_bases = new_bases; } - PyObject* ClassBase::getattr(const char* name) const + PyObject* ClassBase::getattr(const char* name) { if (!PY_CSTD_::strcmp(name, "__dict__")) { @@ -109,6 +168,17 @@ namespace detail { Py_INCREF(result); return result; } + + // pickle support courtesy of "Ralf W. Grosse-Kunstleve" + if (!PY_CSTD_::strcmp(name, "__safe_for_unpickling__")) + { + return PyInt_FromLong(1); + } + if (!PY_CSTD_::strcmp(name, "__reduce__")) + { + Ptr target(as_object(this), Ptr::new_ref); + return new BoundFunction(target, global_class_reduce()); + } Ptr local_attribute = m_name_space.get_item(String(name).reference()); @@ -275,6 +345,11 @@ PyObject* Instance::getattr(const char* name, bool use_special_function) Py_INCREF(this->ob_type); return as_object(this->ob_type); } + + if (!PY_CSTD_::strcmp(name, "__reduce__")) + { + return new BoundFunction(Ptr(this, Ptr::new_ref), global_instance_reduce()); + } Ptr local_attribute = m_name_space.get_item(String(name).reference()); @@ -767,21 +842,7 @@ namespace { void add_current_module_name(Dict& name_space) { static String module_key("__module__", String::interned); - static String name_key("__name__", String::interned); - - Ptr existing_value = name_space.get_item(module_key); - if (existing_value.get() == 0) - { - PyObject* globals = PyEval_GetGlobals(); - if (globals != 0) // Why don't we throw in this case? Who knows? This is - { // what Python does for class objects! - PyObject* module_name = PyDict_GetItem(globals, name_key.get()); - if (module_name != 0) - { - name_space[module_key] = Ptr(module_name, Ptr::borrowed); - } - } - } + name_space.set_item(module_key, Module::name()); } } diff --git a/subclass.h b/subclass.h index 7ac27ad3..68a2a1f4 100644 --- a/subclass.h +++ b/subclass.h @@ -92,7 +92,7 @@ namespace detail { Dict& dict(); // Standard Python functions. - PyObject* getattr(const char* name) const; + PyObject* getattr(const char* name); int setattr(const char* name, PyObject* value); PyObject* repr() const; void add_base(Ptr base); diff --git a/test_extclass.py b/test_extclass.py index 4531b405..259b6c32 100644 --- a/test_extclass.py +++ b/test_extclass.py @@ -206,6 +206,39 @@ Polymorphism also works: >>> baz.get_foo_value(polymorphic_foo) 1000 +Pickling tests: + + >>> world.__module__ + 'demo' + >>> world.__safe_for_unpickling__ + 1 + >>> world.__reduce__() + 'world' + >>> reduced = world('Hello').__reduce__() + >>> reduced[0] == world + 1 + >>> reduced[1:] + (('Hello',), (0,)) + >>> import StringIO + >>> import cPickle + >>> pickle = cPickle + >>> for number in (24, 42): + ... wd = world('California') + ... wd.set_secret_number(number) + ... # Dump it out and read it back in. + ... f = StringIO.StringIO() + ... pickle.dump(wd, f) + ... f = StringIO.StringIO(f.getvalue()) + ... wl = pickle.load(f) + ... # + ... 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 + Special member attributes. Tests courtesy of Barry Scott >>> class DerivedFromFoo(Foo):