/*************************************************************************** * Copyright (c) 2009 Werner Mayer * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ #endif #include "PropertyPythonObject.h" #include "DocumentObjectPy.h" #include "DocumentObject.h" #include #include #include #include #include #include #include using namespace App; TYPESYSTEM_SOURCE(App::PropertyPythonObject , App::Property); PropertyPythonObject::PropertyPythonObject() { } PropertyPythonObject::~PropertyPythonObject() { // this is needed because the release of the pickled object may need the // GIL. Thus, we grab the GIL and replace the pickled with an empty object Base::PyGILStateLocker lock; this->object = Py::Object(); } void PropertyPythonObject::setValue(Py::Object o) { aboutToSetValue(); this->object = o; hasSetValue(); } Py::Object PropertyPythonObject::getValue() const { return object; } PyObject *PropertyPythonObject::getPyObject(void) { return Py::new_reference_to(this->object); } void PropertyPythonObject::setPyObject(PyObject * obj) { aboutToSetValue(); this->object = obj; hasSetValue(); } std::string PropertyPythonObject::toString() const { std::string repr; Base::PyGILStateLocker lock; try { Py::Module pickle(PyImport_ImportModule("json"),true); Py::Callable method(pickle.getAttr(std::string("dumps"))); Py::Object dump; if (this->object.hasAttr("__getstate__")) { Py::Tuple args(0); Py::Callable state(this->object.getAttr("__getstate__")); dump = state.apply(args); } else if (this->object.hasAttr("__dict__")) { dump = this->object.getAttr("__dict__"); } Py::Tuple args(1); args.setItem(0, dump); Py::Object res = method.apply(args); Py::String str(res); repr = str.as_std_string(); } catch (Py::Exception&) { Base::PyException e; // extract the Python error text Base::Console().Warning("PropertyPythonObject::toString: %s\n", e.what()); } return repr; } void PropertyPythonObject::fromString(const std::string& repr) { Base::PyGILStateLocker lock; try { Py::Module pickle(PyImport_ImportModule("json"),true); Py::Callable method(pickle.getAttr(std::string("loads"))); Py::Tuple args(1); args.setItem(0, Py::String(repr)); Py::Object res = method.apply(args); if (this->object.hasAttr("__setstate__")) { Py::Tuple args(1); args.setItem(0, res); Py::Callable state(this->object.getAttr("__setstate__")); state.apply(args); } else { this->object.setAttr("__dict__", res); } } catch (Py::Exception&) { Base::PyException e; // extract the Python error text Base::Console().Warning("PropertyPythonObject::fromString: %s\n", e.what()); } } void PropertyPythonObject::loadPickle(const std::string& str) { // find the custom attributes and restore them Base::PyGILStateLocker lock; try { std::string buffer = str; boost::regex pickle("S'(\\w+)'.+S'(\\w+)'\\n"); boost::match_results what; std::string::const_iterator start, end; start = buffer.begin(); end = buffer.end(); while (boost::regex_search(start, end, what, pickle)) { std::string key = std::string(what[1].first, what[1].second); std::string val = std::string(what[2].first, what[2].second); this->object.setAttr(key, Py::String(val)); buffer = std::string(what[2].second, end); start = buffer.begin(); end = buffer.end(); } } catch (Py::Exception&) { Base::PyException e; // extract the Python error text Base::Console().Warning("PropertyPythonObject::loadPickle: %s\n", e.what()); } } std::string PropertyPythonObject::encodeValue(const std::string& str) const { std::string tmp; for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { if (*it == '<') tmp += "<"; else if (*it == '"') tmp += """; else if (*it == '&') tmp += "&"; else if (*it == '>') tmp += ">"; else if (*it == '\n') tmp += "\\n"; else tmp += *it; } return tmp; } std::string PropertyPythonObject::decodeValue(const std::string& str) const { std::string tmp; for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { if (*it == '\\') { ++it; if (it != str.end() && *it == 'n') { tmp += '\n'; } } else tmp += *it; } return tmp; } void PropertyPythonObject::saveObject(Base::Writer &writer) const { Base::PyGILStateLocker lock; try { PropertyContainer* parent = this->getContainer(); if (parent->isDerivedFrom(Base::Type::fromName("App::DocumentObject"))) { if (this->object.hasAttr("__object__")) { writer.Stream() << " object=\"yes\""; } } if (parent->isDerivedFrom(Base::Type::fromName("Gui::ViewProvider"))) { if (this->object.hasAttr("__vobject__")) { writer.Stream() << " vobject=\"yes\""; } } } catch (Py::Exception& e) { e.clear(); } } void PropertyPythonObject::restoreObject(Base::XMLReader &reader) { Base::PyGILStateLocker lock; try { PropertyContainer* parent = this->getContainer(); if (reader.hasAttribute("object")) { if (strcmp(reader.getAttribute("object"),"yes") == 0) { Py::Object obj = Py::asObject(parent->getPyObject()); this->object.setAttr("__object__", obj); } } if (reader.hasAttribute("vobject")) { if (strcmp(reader.getAttribute("vobject"),"yes") == 0) { Py::Object obj = Py::asObject(parent->getPyObject()); this->object.setAttr("__vobject__", obj); } } } catch (Py::Exception& e) { e.clear(); } catch (const Base::Exception& e) { Base::Console().Error("%s\n",e.what()); } catch (...) { Base::Console().Error("Critical error in PropertyPythonObject::restoreObject\n"); } } void PropertyPythonObject::Save (Base::Writer &writer) const { //if (writer.isForceXML()) { std::string repr = this->toString(); repr = Base::base64_encode((const unsigned char*)repr.c_str(), repr.size()); std::string val = /*encodeValue*/(repr); writer.Stream() << writer.ind() << "object.hasAttr("__module__") && this->object.hasAttr("__class__")) { Py::String mod(this->object.getAttr("__module__")); Py::Object cls(this->object.getAttr("__class__")); if (cls.hasAttr("__name__")) { Py::String name(cls.getAttr("__name__")); writer.Stream() << " module=\"" << (std::string)mod << "\"" << " class=\"" << (std::string)name << "\""; } } } catch (Py::Exception&) { Base::PyException e; // extract the Python error text Base::Console().Warning("PropertyPythonObject::Save: %s\n", e.what()); } saveObject(writer); writer.Stream() << "/>" << std::endl; //} //else { // writer.Stream() << writer.ind() << "" << std::endl; //} } void PropertyPythonObject::Restore(Base::XMLReader &reader) { reader.readElement("Python"); if (reader.hasAttribute("file")) { std::string file(reader.getAttribute("file")); reader.addFile(file.c_str(),this); } else { bool load_json=false; bool load_pickle=false; bool load_failed=false; std::string buffer = reader.getAttribute("value"); if (reader.hasAttribute("encoded") && strcmp(reader.getAttribute("encoded"),"yes") == 0) { buffer = Base::base64_decode(buffer); } else { buffer = decodeValue(buffer); } Base::PyGILStateLocker lock; try { boost::regex pickle("^\\(i(\\w+)\\n(\\w+)\\n"); boost::match_results what; std::string::const_iterator start, end; start = buffer.begin(); end = buffer.end(); if (reader.hasAttribute("module") && reader.hasAttribute("class")) { Py::Module mod(PyImport_ImportModule(reader.getAttribute("module")),true); PyObject* cls = mod.getAttr(reader.getAttribute("class")).ptr(); if (PyClass_Check(cls)) { this->object = PyInstance_NewRaw(cls, 0); } else if (PyType_Check(cls)) { this->object = PyType_GenericAlloc((PyTypeObject*)cls, 0); } else { throw Py::TypeError("neither class nor type object"); } load_json = true; } else if (boost::regex_search(start, end, what, pickle)) { std::string nam = std::string(what[1].first, what[1].second); std::string cls = std::string(what[2].first, what[2].second); Py::Module mod(PyImport_ImportModule(nam.c_str()),true); this->object = PyInstance_NewRaw(mod.getAttr(cls).ptr(), 0); load_pickle = true; buffer = std::string(what[2].second, end); } } catch (Py::Exception&) { Base::PyException e; // extract the Python error text Base::Console().Warning("PropertyPythonObject::Restore: %s\n", e.what()); this->object = Py::None(); load_failed = true; } aboutToSetValue(); if (load_json) this->fromString(buffer); else if (load_pickle) this->loadPickle(buffer); else if (!load_failed) Base::Console().Warning("PropertyPythonObject::Restore: unsupported serialisation: %s\n", buffer.c_str()); restoreObject(reader); hasSetValue(); } } void PropertyPythonObject::SaveDocFile (Base::Writer &writer) const { std::string buffer = this->toString(); for (std::string::iterator it = buffer.begin(); it != buffer.end(); ++it) writer.Stream().put(*it); } void PropertyPythonObject::RestoreDocFile(Base::Reader &reader) { aboutToSetValue(); std::string buffer; char c; while (reader.get(c)) { buffer.push_back(c); } this->fromString(buffer); hasSetValue(); } unsigned int PropertyPythonObject::getMemSize (void) const { return sizeof(Py::Object); } Property *PropertyPythonObject::Copy(void) const { PropertyPythonObject *p = new PropertyPythonObject(); p->object = this->object; return p; } void PropertyPythonObject::Paste(const Property &from) { if (from.getTypeId() == PropertyPythonObject::getClassTypeId()) { aboutToSetValue(); this->object = static_cast(from).object; hasSetValue(); } }