Mantid
Loading...
Searching...
No Matches
PythonObjectProperty.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL-3.0 +
8#include "MantidJson/Json.h"
14
15#include <boost/python/dict.hpp>
16#include <boost/python/errors.hpp>
17#include <boost/python/extract.hpp>
18#include <boost/python/import.hpp>
19#include <boost/python/list.hpp>
20
21namespace {
22namespace bp = boost::python;
23
25std::set<std::string> const jsonAllowedTypes{"int", "float", "str", "NoneType", "bool"};
26
27inline bool isJsonAtomic(bp::object const &obj) {
28 std::string const objname = bp::extract<std::string>(obj.attr("__class__").attr("__name__"));
29 return jsonAllowedTypes.count(objname) > 0;
30}
31
40bp::object recursiveDictDump(bp::object const &obj, unsigned char depth = 0) {
41 static unsigned char constexpr max_depth(10);
42 bp::object ret;
43 // limit recursion depth, to avoid infinity loops or possible segfaults
44 if (depth >= max_depth) {
45 ret = bp::str("...");
46 }
47 // if the object can be json-ified already, return this object
48 else if (isJsonAtomic(obj)) {
49 ret = obj;
50 }
51 // if the object is a list, json-ify each element of the list
52 else if (PyList_Check(obj.ptr()) || PyTuple_Check(obj.ptr())) {
53 bp::list ls;
54 for (bp::ssize_t i = 0; i < bp::len(obj); i++) {
55 ls.append(recursiveDictDump(obj[i], depth + 1));
56 }
57 ret = ls;
58 }
59 // if the object is a dictionary, json-ify all of the values
60 else if (PyDict_Check(obj.ptr())) {
61 bp::dict d;
62 bp::list keyvals = bp::extract<bp::dict>(obj)().items();
63 for (bp::ssize_t i = 0; i < bp::len(keyvals); i++) {
64 bp::object key = keyvals[i][0];
65 bp::object val = keyvals[i][1];
66 d[key] = recursiveDictDump(val, depth + 1);
67 }
68 ret = d;
69 }
70 // if the object is not one of the above types, then extract its __dict__ method and repeat the above
71 else if (PyObject_HasAttrString(obj.ptr(), "__dict__")) {
72 bp::dict d = bp::extract<bp::dict>(obj.attr("__dict__"));
73 ret = recursiveDictDump(d, depth); // NOTE re-run at same depth
74 } else {
75 ret = bp::str(obj);
76 }
77 return ret;
78}
79} // namespace
80
81namespace Mantid::Kernel {
82
86template <> std::string toString(PythonObject const &obj) {
88
89 // std::string ret;
90 boost::python::object rep;
91 // if the object is None type, return an empty string
93 rep = boost::python::str("");
94 }
95 // if the object can be read as a string, then return the object itself
96 else if (boost::python::extract<std::string>(obj).check()) {
97 rep = obj;
98 }
99 // otherwise, use json to return a string representation of the class
100 else {
101 // try loading as a json -- will work for most 'built-in' types
102 boost::python::object json = boost::python::import("json");
103 try {
104 rep = json.attr("dumps")(obj);
105 }
106 // if json doesn't work, then build a json-like dictionary representation of the object and dump that
107 catch (boost::python::error_already_set const &) {
108 PyErr_Clear(); // NOTE must clear error registry, or bizarre errors will occur at unexpected lines
109 boost::python::object dict = recursiveDictDump(obj);
110 try {
111 rep = json.attr("dumps")(dict);
112 } catch (boost::python::error_already_set const &) {
113 PyErr_Clear();
114 rep = boost::python::str("<unrepresentable object>");
115 }
116 }
117 }
118 return boost::python::extract<std::string>(rep);
119}
120
125template <> std::string toPrettyString(PythonObject const &value, size_t /*maxLength*/, bool /*collapseLists*/) {
126 return toString(value);
127}
128
132template <> Json::Value encodeAsJson(PythonObject const &) {
133 throw Exception::NotImplementedError("encodeAsJson(const boost::python::object &)");
134}
135
136#if defined(__APPLE__) || (defined(__linux__) && defined(__clang__))
144// Instantiate a copy of the class with our template type so we generate the symbols for the methods in the hxx header.
146#endif
147
148} // namespace Mantid::Kernel
149
150namespace Mantid::PythonInterface {
151
152using Kernel::PropertyWithValue;
153using Kernel::Exception::NotImplementedError;
154
158std::string PythonObjectProperty::getDefault() const { return Mantid::Kernel::toString(m_initialValue); }
159
164std::string PythonObjectProperty::setValue(PythonObject const &value) {
165 std::string ret;
166 try {
167 *this = value;
168 } catch (std::invalid_argument const &except) {
169 ret = except.what();
170 }
171 return ret;
172}
173
178std::string PythonObjectProperty::setValue(std::string const &value) {
180
181 std::string ret;
182 boost::python::object newVal;
183 // try to load as a json object
184 try {
185 boost::python::object json = boost::python::import("json");
186 newVal = json.attr("loads")(value);
187 }
188 // if it cannot be loaded as a json then it is probably a string
189 catch (boost::python::error_already_set const &) {
190 PyErr_Clear(); // NOTE must clear error registry, or bizarre errors will occur
191 try {
192 newVal = boost::python::str(value.c_str());
193 } catch (boost::python::error_already_set const &) {
194 PyErr_Clear(); // NOTE must clear error registry, or bizarre errors will occur
195 return "Failed to interpret string as JSON or string property: " + value;
196 }
197 }
198
199 // use the assignment operator, which also calls the validator
200 try {
201 *this = newVal;
202 } catch (std::invalid_argument const &except) {
203 ret = except.what();
204 }
205 return ret;
206}
207
214std::string PythonObjectProperty::setValueFromJson(const Json::Value &json) {
215 std::string jsonstr = JsonHelpers::jsonToString(json);
216 return setValue(jsonstr);
217}
218
219std::string PythonObjectProperty::setDataItem(const std::shared_ptr<Kernel::DataItem> &) {
220 throw NotImplementedError("PythonObjectProperty::setDataItem(const std::shared_ptr<Kernel::DataItem> &)");
221}
222
227bool PythonObjectProperty::isDefault() const { return m_value.is_none(); }
228
229} // namespace Mantid::PythonInterface
const std::string & m_value
Definition Algorithm.cpp:71
double value
The value of the point.
Definition FitMW.cpp:51
double obj
the value of the quadratic function
Marks code as not implemented yet.
Definition Exception.h:138
The concrete, templated class for properties.
Defines a structure for acquiring/releasing the Python GIL using the RAII pattern.
#define MANTID_PYTHONINTERFACE_CORE_DLL
Definition DllConfig.h:17
std::string toString(const T &value)
Convert values to strings.
boost::python::object PythonObject
MANTID_KERNEL_DLL::Json::Value encodeAsJson(const OptionalBool &)
Encode an OptionalBool as a Json::Value.
std::string toPrettyString(const T &value, size_t maxLength=0, bool collapseLists=true)
Convert values to pretty strings.
bool isNone(const PyObject *ptr)
Definition IsNone.h:26