Mantid
Loading...
Searching...
No Matches
PropertyWithValueFactory.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 +
7//-----------------------------------------------------------------------------
8// Includes
9//-----------------------------------------------------------------------------
17
18#include <boost/python.hpp>
19#include <boost/python/class.hpp>
20#include <boost/python/extract.hpp>
21#include <boost/python/list.hpp>
22#include <boost/python/object.hpp>
23#include <memory>
24
25#include <cassert>
26
29using namespace boost::python;
30using namespace Mantid::Kernel;
31using namespace Mantid::PythonInterface;
32
34namespace {
36using PyTypeIndex = std::map<const PyTypeObject *, std::shared_ptr<PropertyValueHandler>>;
37
41void initTypeLookup(PyTypeIndex &index) {
42 assert(index.empty());
43
44 // Map the Python types to the best match in C++
45 using FloatHandler = TypedPropertyValueHandler<double>;
46 index.emplace(&PyFloat_Type, std::make_shared<FloatHandler>());
47
48 using BoolHandler = TypedPropertyValueHandler<bool>;
49 index.emplace(&PyBool_Type, std::make_shared<BoolHandler>());
50
51 using IntHandler = TypedPropertyValueHandler<int>;
52 index.emplace(&PyLong_Type, std::make_shared<IntHandler>());
53
54 // In Python 3 all strings are unicode but in Python 2 unicode strings
55 // must be explicitly requested. The C++ string handler will accept both
56 // but throw and error if the unicode string contains non-ascii characters
57 using AsciiStrHandler = TypedPropertyValueHandler<std::string>;
58 // Both versions have unicode objects
59 index.emplace(&PyUnicode_Type, std::make_shared<AsciiStrHandler>());
60 // Handle a dictionary type
61 index.emplace(&PyDict_Type, std::make_shared<MappingTypeHandler>());
62}
63
67const PyTypeIndex &getTypeIndex() {
68 static PyTypeIndex index;
69 if (index.empty())
70 initTypeLookup(index);
71 return index;
72}
73
74// Lookup map for arrays
75using PyArrayIndex = std::map<std::string, std::shared_ptr<PropertyValueHandler>>;
76
80void initArrayLookup(PyArrayIndex &index) {
81 assert(index.empty());
82
83 // Map the Python array types to the best match in C++
84 using FloatArrayHandler = SequenceTypeHandler<std::vector<double>>;
85 index.emplace("FloatArray", std::make_shared<FloatArrayHandler>());
86
87 using StringArrayHandler = SequenceTypeHandler<std::vector<std::string>>;
88 index.emplace("StringArray", std::make_shared<StringArrayHandler>());
89
90 using IntArrayHandler = SequenceTypeHandler<std::vector<int>>;
91 index.emplace("IntArray", std::make_shared<IntArrayHandler>());
92}
93
97const PyArrayIndex &getArrayIndex() {
98 static PyArrayIndex index;
99 if (index.empty())
100 initArrayLookup(index);
101 return index;
102}
103} // namespace
104
115std::unique_ptr<Kernel::Property> PropertyWithValueFactory::create(const std::string &name,
116 const boost::python::object &defaultValue,
117 const boost::python::object &validator,
118 const unsigned int direction) {
119 const auto &propHandle = lookup(defaultValue.ptr());
120 return propHandle.create(name, defaultValue, validator, direction);
121}
122
132std::unique_ptr<Kernel::Property> PropertyWithValueFactory::create(const std::string &name,
133 const boost::python::object &defaultValue,
134 const unsigned int direction) {
135 boost::python::object validator; // Default construction gives None object
136 return create(name, defaultValue, validator, direction);
137}
138
146std::unique_ptr<Mantid::Kernel::Property> PropertyWithValueFactory::createTimeSeries(const std::string &name,
147 const list &defaultValue) {
148
149 // Use a PyObject pointer to determine the type stored in the list
150 auto obj = object(defaultValue[0]).ptr();
151 auto val = defaultValue[0];
152
158 if (PyBool_Check(obj)) {
159 return std::make_unique<TimeSeriesProperty<bool>>(name);
160 } else if (extract<int>(val).check()) {
161 return std::make_unique<TimeSeriesProperty<int>>(name);
162 } else if (extract<double>(val).check()) {
163 return std::make_unique<TimeSeriesProperty<double>>(name);
164 } else if (extract<std::string>(val).check()) {
165 return std::make_unique<TimeSeriesProperty<std::string>>(name);
166 }
167
168 // If we reach here an error has occurred as there are no type to create
169 // a TimeSeriesProperty from
170 throw std::runtime_error("Cannot create a TimeSeriesProperty with that data type!");
171}
172
173//-------------------------------------------------------------------------
174// Private methods
175//-------------------------------------------------------------------------
182 // Check if object is array.
183 const auto arrayType = isArray(object);
184 if (!arrayType.empty()) {
185 const PyArrayIndex &arrayIndex = getArrayIndex();
186 auto ait = arrayIndex.find(arrayType);
187 if (ait != arrayIndex.end()) {
188 return *(ait->second);
189 }
190 }
191 // Object is not array, so check primitive types
192 const PyTypeIndex &typeIndex = getTypeIndex();
193 auto cit = typeIndex.find(object->ob_type);
194 if (cit == typeIndex.end()) {
195 std::ostringstream os;
196 os << "Cannot create PropertyWithValue from Python type " << object->ob_type->tp_name
197 << ". No converter registered in PropertyWithValueFactory.";
198 throw std::invalid_argument(os.str());
199 }
200 return *(cit->second);
201}
202
208const std::string PropertyWithValueFactory::isArray(PyObject *const object) {
209 if (PyList_Check(object) || PyTuple_Check(object)) {
210 // If we are dealing with an empty list/tuple, then we cannot deduce the
211 // ArrayType. We need to throw at this point.
212 if (PySequence_Size(object) < 1) {
213 throw std::runtime_error("Cannot have a sequence type of length zero in a mapping type.");
214 }
215
216 PyObject *item = PySequence_Fast_GET_ITEM(object, 0);
217 // Boolean can be cast to int, so check first.
218 GNU_DIAG_OFF("parentheses-equality")
219 if (PyBool_Check(item)) {
220 throw std::runtime_error("Unable to support extracting arrays of booleans.");
221 }
222 if (PyLong_Check(item)) {
223 return std::string("IntArray");
224 }
225 GNU_DIAG_ON("parentheses-equality")
226 if (PyFloat_Check(item)) {
227 return std::string("FloatArray");
228 }
229 if (PyUnicode_Check(item)) {
230 return std::string("StringArray");
231 }
232 if (PyBytes_Check(item)) {
233 return std::string("StringArray");
234 }
235 // If we get here, we've found a sequence and we can't interpret the item
236 // type.
237 std::ostringstream os;
238 os << "Cannot create PropertyWithValue from Python type " << object->ob_type->tp_name
239 << " containing items of type " << item->ob_type << ". No converter registered in PropertyWithValueFactory.";
240
241 throw std::invalid_argument(os.str());
242 } else {
243 return std::string("");
244 }
245}
246} // namespace Mantid::PythonInterface::Registry
std::string name
Definition Run.cpp:60
std::map< DeltaEMode::Type, std::string > index
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.
A specialised Property class for holding a series of time-value pairs.
Defines a static factory class that creates PropertyWithValue instances from python objects.
static std::unique_ptr< Kernel::Property > createTimeSeries(const std::string &name, const boost::python::list &defaultValue)
Creates a TimeSeriesProperty<Type> instance from the given information.
static std::unique_ptr< Kernel::Property > create(const std::string &name, const boost::python::object &defaultValue, const boost::python::object &validator, const unsigned int direction)
Creates a PropertyWithValue<Type> instance from the given information.
static const PropertyValueHandler & lookup(PyObject *const object)
Return a handler that maps the python type to a C++ type.
static const std::string isArray(PyObject *const object)
Return a string based on the python array type.
This class provides a base-class objects that are able to take a python object and set it on an algor...