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 // Python 2/3 have an arbitrary-sized long type. The handler
52 // will raise an error if the input value overflows a C long
53 using IntHandler = TypedPropertyValueHandler<long>;
54 index.emplace(&PyLong_Type, std::make_shared<IntHandler>());
55
56 // In Python 3 all strings are unicode but in Python 2 unicode strings
57 // must be explicitly requested. The C++ string handler will accept both
58 // but throw and error if the unicode string contains non-ascii characters
59 using AsciiStrHandler = TypedPropertyValueHandler<std::string>;
60 // Both versions have unicode objects
61 index.emplace(&PyUnicode_Type, std::make_shared<AsciiStrHandler>());
62 // Handle a dictionary type
63 index.emplace(&PyDict_Type, std::make_shared<MappingTypeHandler>());
64}
65
69const PyTypeIndex &getTypeIndex() {
70 static PyTypeIndex index;
71 if (index.empty())
72 initTypeLookup(index);
73 return index;
74}
75
76// Lookup map for arrays
77using PyArrayIndex = std::map<std::string, std::shared_ptr<PropertyValueHandler>>;
78
82void initArrayLookup(PyArrayIndex &index) {
83 assert(index.empty());
84
85 // Map the Python array types to the best match in C++
86 using FloatArrayHandler = SequenceTypeHandler<std::vector<double>>;
87 index.emplace("FloatArray", std::make_shared<FloatArrayHandler>());
88
89 using StringArrayHandler = SequenceTypeHandler<std::vector<std::string>>;
90 index.emplace("StringArray", std::make_shared<StringArrayHandler>());
91
92 using LongIntArrayHandler = SequenceTypeHandler<std::vector<long>>;
93 index.emplace("LongIntArray", std::make_shared<LongIntArrayHandler>());
94}
95
99const PyArrayIndex &getArrayIndex() {
100 static PyArrayIndex index;
101 if (index.empty())
102 initArrayLookup(index);
103 return index;
104}
105} // namespace
106
117std::unique_ptr<Kernel::Property> PropertyWithValueFactory::create(const std::string &name,
118 const boost::python::object &defaultValue,
119 const boost::python::object &validator,
120 const unsigned int direction) {
121 const auto &propHandle = lookup(defaultValue.ptr());
122 return propHandle.create(name, defaultValue, validator, direction);
123}
124
134std::unique_ptr<Kernel::Property> PropertyWithValueFactory::create(const std::string &name,
135 const boost::python::object &defaultValue,
136 const unsigned int direction) {
137 boost::python::object validator; // Default construction gives None object
138 return create(name, defaultValue, validator, direction);
139}
140
148std::unique_ptr<Mantid::Kernel::Property> PropertyWithValueFactory::createTimeSeries(const std::string &name,
149 const list &defaultValue) {
150
151 // Use a PyObject pointer to determine the type stored in the list
152 auto obj = object(defaultValue[0]).ptr();
153 auto val = defaultValue[0];
154
160 if (PyBool_Check(obj)) {
161 return std::make_unique<TimeSeriesProperty<bool>>(name);
162 } else if (extract<int>(val).check()) {
163 return std::make_unique<TimeSeriesProperty<int>>(name);
164 } else if (extract<double>(val).check()) {
165 return std::make_unique<TimeSeriesProperty<double>>(name);
166 } else if (extract<std::string>(val).check()) {
167 return std::make_unique<TimeSeriesProperty<std::string>>(name);
168 }
169
170 // If we reach here an error has occurred as there are no type to create
171 // a TimeSeriesProperty from
172 throw std::runtime_error("Cannot create a TimeSeriesProperty with that data type!");
173}
174
175//-------------------------------------------------------------------------
176// Private methods
177//-------------------------------------------------------------------------
184 // Check if object is array.
185 const auto arrayType = isArray(object);
186 if (!arrayType.empty()) {
187 const PyArrayIndex &arrayIndex = getArrayIndex();
188 auto ait = arrayIndex.find(arrayType);
189 if (ait != arrayIndex.end()) {
190 return *(ait->second);
191 }
192 }
193 // Object is not array, so check primitive types
194 const PyTypeIndex &typeIndex = getTypeIndex();
195 auto cit = typeIndex.find(object->ob_type);
196 if (cit == typeIndex.end()) {
197 std::ostringstream os;
198 os << "Cannot create PropertyWithValue from Python type " << object->ob_type->tp_name
199 << ". No converter registered in PropertyWithValueFactory.";
200 throw std::invalid_argument(os.str());
201 }
202 return *(cit->second);
203}
204
210const std::string PropertyWithValueFactory::isArray(PyObject *const object) {
211 if (PyList_Check(object) || PyTuple_Check(object)) {
212 // If we are dealing with an empty list/tuple, then we cannot deduce the
213 // ArrayType. We need to throw at this point.
214 if (PySequence_Size(object) < 1) {
215 throw std::runtime_error("Cannot have a sequence type of length zero in a mapping type.");
216 }
217
218 PyObject *item = PySequence_Fast_GET_ITEM(object, 0);
219 // Boolean can be cast to int, so check first.
220 GNU_DIAG_OFF("parentheses-equality")
221 if (PyBool_Check(item)) {
222 throw std::runtime_error("Unable to support extracting arrays of booleans.");
223 }
224 if (PyLong_Check(item)) {
225 return std::string("LongIntArray");
226 }
227 GNU_DIAG_ON("parentheses-equality")
228 if (PyFloat_Check(item)) {
229 return std::string("FloatArray");
230 }
231 if (PyUnicode_Check(item)) {
232 return std::string("StringArray");
233 }
234 if (PyBytes_Check(item)) {
235 return std::string("StringArray");
236 }
237 // If we get here, we've found a sequence and we can't interpret the item
238 // type.
239 std::ostringstream os;
240 os << "Cannot create PropertyWithValue from Python type " << object->ob_type->tp_name
241 << " containing items of type " << item->ob_type << ". No converter registered in PropertyWithValueFactory.";
242
243 throw std::invalid_argument(os.str());
244 } else {
245 return std::string("");
246 }
247}
248} // namespace Mantid::PythonInterface::Registry
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
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...