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"
15
16#include <boost/python/dict.hpp>
17#include <boost/python/errors.hpp>
18#include <boost/python/extract.hpp>
19#include <boost/python/import.hpp>
20#include <boost/python/list.hpp>
21
22namespace {
23namespace bp = boost::python;
24
26std::set<std::string> const jsonAllowedTypes{"int", "float", "str", "NoneType", "bool"};
27
28inline bool isJsonAtomic(bp::object const &obj) {
29 std::string const objname = bp::extract<std::string>(obj.attr("__class__").attr("__name__"));
30 return jsonAllowedTypes.count(objname) > 0;
31}
32
41bp::object recursiveDictDump(bp::object const &obj, unsigned char depth = 0) {
42 static unsigned char constexpr max_depth(10);
43 bp::object ret;
44 // limit recursion depth, to avoid infinity loops or possible segfaults
45 if (depth >= max_depth) {
46 ret = bp::str("...");
47 }
48 // if the object can be json-ified already, return this object
49 else if (isJsonAtomic(obj)) {
50 ret = obj;
51 }
52 // if the object is a list, json-ify each element of the list
53 else if (PyList_Check(obj.ptr()) || PyTuple_Check(obj.ptr())) {
54 bp::list ls;
55 for (bp::ssize_t i = 0; i < bp::len(obj); i++) {
56 ls.append(recursiveDictDump(obj[i], depth + 1));
57 }
58 ret = ls;
59 }
60 // if the object is a dictionary, json-ify all of the values
61 else if (PyDict_Check(obj.ptr())) {
62 bp::dict d;
63 bp::list keyvals = bp::extract<bp::dict>(obj)().items();
64 for (bp::ssize_t i = 0; i < bp::len(keyvals); i++) {
65 bp::object key = keyvals[i][0];
66 bp::object val = keyvals[i][1];
67 d[key] = recursiveDictDump(val, depth + 1);
68 }
69 ret = d;
70 }
71 // if the object is not one of the above types, then extract its __dict__ method and repeat the above
72 else if (PyObject_HasAttrString(obj.ptr(), "__dict__")) {
73 bp::dict d = bp::extract<bp::dict>(obj.attr("__dict__"));
74 ret = recursiveDictDump(d, depth); // NOTE re-run at same depth
75 } else {
76 ret = bp::str(obj);
77 }
78 return ret;
79}
80} // namespace
81
82namespace Mantid::Kernel {
83
87template <> std::string toString(PythonObject const &obj) {
89
90 // std::string ret;
91 boost::python::object rep;
92 // if the object is None type, return an empty string
94 rep = boost::python::str("");
95 }
96 // if the object can be read as a string, then return the object itself
97 else if (boost::python::extract<std::string>(obj).check()) {
98 rep = obj;
99 }
100 // otherwise, use json to return a string representation of the class
101 else {
102 // try loading as a json -- will work for most 'built-in' types
103 boost::python::object json = boost::python::import("json");
104 try {
105 rep = json.attr("dumps")(obj);
106 }
107 // if json doesn't work, then build a json-like dictionary representation of the object and dump that
108 catch (boost::python::error_already_set const &) {
109 PyErr_Clear(); // NOTE must clear error registry, or bizarre errors will occur at unexpected lines
110 boost::python::object dict = recursiveDictDump(obj);
111 try {
112 rep = json.attr("dumps")(dict);
113 } catch (boost::python::error_already_set const &) {
114 PyErr_Clear();
115 rep = boost::python::str("<unrepresentable object>");
116 }
117 }
118 }
119 return boost::python::extract<std::string>(rep);
120}
121
126template <> std::string toPrettyString(PythonObject const &value, size_t /*maxLength*/, bool /*collapseLists*/) {
127 return toString(value);
128}
129
133template <> Json::Value encodeAsJson(PythonObject const &) {
134 throw Exception::NotImplementedError("encodeAsJson(const boost::python::object &)");
135}
136
138// PythonObjectProperty.h declares this with MANTID_PYTHONINTERFACE_CORE_EXTERN_TEMPLATE, which already carries the
139// export attribute, so gcc warns that the one here is ignored.
140GNU_DIAG_OFF("attributes")
141template class MANTID_PYTHONINTERFACE_CORE_DLL PropertyWithValue<PythonObject>;
142GNU_DIAG_ON("attributes")
144
145} // namespace Mantid::Kernel
146
147namespace Mantid::PythonInterface {
148
151
155std::string PythonObjectProperty::getDefault() const { return Mantid::Kernel::toString(m_initialValue); }
156
161std::string PythonObjectProperty::setValue(PythonObject const &value) {
162 std::string ret;
163 try {
164 *this = value;
165 } catch (std::invalid_argument const &except) {
166 ret = except.what();
167 }
168 return ret;
169}
170
175std::string PythonObjectProperty::setValue(std::string const &value) {
177
178 std::string ret;
179 boost::python::object newVal;
180 // try to load as a json object
181 try {
182 boost::python::object json = boost::python::import("json");
183 newVal = json.attr("loads")(value);
184 }
185 // if it cannot be loaded as a json then it is probably a string
186 catch (boost::python::error_already_set const &) {
187 PyErr_Clear(); // NOTE must clear error registry, or bizarre errors will occur
188 try {
189 newVal = boost::python::str(value.c_str());
190 } catch (boost::python::error_already_set const &) {
191 PyErr_Clear(); // NOTE must clear error registry, or bizarre errors will occur
192 return "Failed to interpret string as JSON or string property: " + value;
193 }
194 }
195
196 // use the assignment operator, which also calls the validator
197 try {
198 *this = newVal;
199 } catch (std::invalid_argument const &except) {
200 ret = except.what();
201 }
202 return ret;
203}
204
211std::string PythonObjectProperty::setValueFromJson(const Json::Value &json) {
212 std::string jsonstr = JsonHelpers::jsonToString(json);
213 return setValue(jsonstr);
214}
215
216std::string PythonObjectProperty::setDataItem(const std::shared_ptr<Kernel::DataItem> &) {
217 throw NotImplementedError("PythonObjectProperty::setDataItem(const std::shared_ptr<Kernel::DataItem> &)");
218}
219
224bool PythonObjectProperty::isDefault() const { return m_value.is_none(); }
225
226} // namespace Mantid::PythonInterface
const std::string & m_value
Definition Algorithm.cpp:72
double value
The value of the point.
Definition FitMW.cpp:51
double obj
the value of the quadratic function
#define GNU_DIAG_ON(x)
#define GNU_DIAG_OFF(x)
This is a collection of macros for turning compiler warnings off in a controlled manner.
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.
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
Helper class which provides the Collimation Length for SANS instruments.