AlgorithmAdapter.cpp 13.6 KB
Newer Older
1
2
3
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4
5
//   NScD Oak Ridge National Laboratory, European Spallation Source,
//   Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6
// SPDX - License - Identifier: GPL - 3.0 +
7
#include "MantidPythonInterface/api/PythonAlgorithm/AlgorithmAdapter.h"
8
#include "MantidAPI/DataProcessorAlgorithm.h"
9
#include "MantidAPI/DistributedAlgorithm.h"
10
11
12
#include "MantidAPI/ParallelAlgorithm.h"
#include "MantidAPI/SerialAlgorithm.h"
#include "MantidKernel/WarningSuppressions.h"
13
#include "MantidPythonInterface/core/CallMethod.h"
14
#include "MantidPythonInterface/core/Converters/PySequenceToVector.h"
15
16
#include "MantidPythonInterface/core/GlobalInterpreterLock.h"
#include "MantidPythonInterface/core/WrapperHelpers.h"
17
#include "MantidPythonInterface/kernel/Registry/PropertyWithValueFactory.h"
18
19
20
21
22
23
24

#include <boost/python/class.hpp>
#include <boost/python/dict.hpp>

//-----------------------------------------------------------------------------
// AlgorithmAdapter definition
//-----------------------------------------------------------------------------
25
26
27
28
29
30
31
32
33
34
namespace Mantid {
namespace PythonInterface {
using namespace boost::python;

/**
 * Construct the "wrapper" and stores the reference to the PyObject
 * @param self A reference to the calling Python object
 */
template <typename BaseAlgorithm>
AlgorithmAdapter<BaseAlgorithm>::AlgorithmAdapter(PyObject *self)
35
    : BaseAlgorithm(), m_self(self), m_isRunningObj(nullptr), m_wikiSummary("") {
36
37
38
  // Only cache the isRunning attribute if it is overridden by the
  // inheriting type otherwise we end up with an infinite recursive call
  // as isRunning always exists from the interface
39
  if (typeHasAttribute(self, "isRunning"))
40
41
    m_isRunningObj = PyObject_GetAttrString(self, "isRunning");
}
42

43
44
45
/**
 * Returns the name of the algorithm. This cannot be overridden in Python.
 */
46
template <typename BaseAlgorithm> const std::string AlgorithmAdapter<BaseAlgorithm>::name() const {
47
48
  return std::string(getSelf()->ob_type->tp_name);
}
49

50
51
52
53
/**
 * Returns the version of the algorithm. If not overridden
 * it returns 1
 */
54
template <typename BaseAlgorithm> int AlgorithmAdapter<BaseAlgorithm>::version() const {
55
56
57
58
59
  try {
    return callMethod<int>(getSelf(), "version");
  } catch (UndefinedAttributeError &) {
    return 1;
  }
60
}
61

62
63
64
65
66
/**
 * Returns checkGroups. If false, workspace groups will be treated as a whole
 * If true, the algorithm will act on each component of the workspace group
 * individually
 */
67
template <typename BaseAlgorithm> bool AlgorithmAdapter<BaseAlgorithm>::checkGroups() {
68
69
70
71
72
  try {
    return callMethod<bool>(getSelf(), "checkGroups");
  } catch (UndefinedAttributeError &) {
    return BaseAlgorithm::checkGroups();
  }
73
}
74

75
76
77
78
/**
 * Returns the category of the algorithm. If not overridden
 * it return defaultCategory()
 */
79
template <typename BaseAlgorithm> const std::string AlgorithmAdapter<BaseAlgorithm>::category() const {
80
81
82
83
84
85
86
  const static std::string defaultCategory = "PythonAlgorithms";
  std::string category = defaultCategory;
  try {
    category = callMethod<std::string>(getSelf(), "category");
  } catch (UndefinedAttributeError &) {
  }
  if (category == defaultCategory) {
87
    // output a warning
88
89
90
    this->getLogger().warning() << "Python Algorithm " << this->name() << " v" << this->version()
                                << " does not have a category defined. See "
                                   "http://www.mantidproject.org/Basic_PythonAlgorithm_Structure\n";
91
  }
92
  return category;
93
}
94

95
/**
LamarMoore's avatar
LamarMoore committed
96
97
98
 * Returns seeAlso related algorithms. If not overridden
 * it returns an empty vector of strings
 */
99
template <typename BaseAlgorithm> const std::vector<std::string> AlgorithmAdapter<BaseAlgorithm>::seeAlso() const {
100
  try {
101
102
    // The GIL is required so that the the reference count of the
    // list object can be decremented safely
103
    GlobalInterpreterLock gil;
104
    return Converters::PySequenceToVector<std::string>(callMethod<list>(getSelf(), "seeAlso"))();
105
106
107
108
109
  } catch (UndefinedAttributeError &) {
    return {};
  }
}

110
111
112
113
114
115
116
117
118
119
120
/**
 * Returns the aliases of the algorithm. If not overridden returns the base algorithm implementation
 */
template <typename BaseAlgorithm> const std::string AlgorithmAdapter<BaseAlgorithm>::alias() const {
  try {
    return callMethod<std::string>(getSelf(), "alias");
  } catch (UndefinedAttributeError &) {
    return BaseAlgorithm::alias();
  }
}

121
122
123
124
125
126
127
128
129
130
131
132
/**
 * Returns the expiration date (in ISO8601 format) of algorithm aliases. If not overridden, returns the
 * base algorithm implementation
 */
template <typename BaseAlgorithm> const std::string AlgorithmAdapter<BaseAlgorithm>::aliasDeprecated() const {
  try {
    return callMethod<std::string>(getSelf(), "aliasDeprecated");
  } catch (UndefinedAttributeError &) {
    return BaseAlgorithm::aliasDeprecated();
  }
}

133
134
135
136
/**
 * Returns the summary of the algorithm. If not overridden
 * it returns defaultSummary
 */
137
template <typename BaseAlgorithm> const std::string AlgorithmAdapter<BaseAlgorithm>::summary() const {
138
139
140
141
142
  try {
    return callMethod<std::string>(getSelf(), "summary");
  } catch (UndefinedAttributeError &) {
    return m_wikiSummary;
  }
143
}
144

145
/**
146
147
 * Optional documentation URL of the algorithm, empty string if not overridden.
 */
148
template <typename BaseAlgorithm> const std::string AlgorithmAdapter<BaseAlgorithm>::helpURL() const {
149
150
151
152
153
154
155
  try {
    return callMethod<std::string>(getSelf(), "helpURL");
  } catch (UndefinedAttributeError &) {
    return std::string();
  }
}

156
/**
LamarMoore's avatar
LamarMoore committed
157
158
 *@return True if the algorithm is considered to be running
 */
159
template <typename BaseAlgorithm> bool AlgorithmAdapter<BaseAlgorithm>::isRunning() const {
160
161
162
  if (!m_isRunningObj) {
    return SuperClass::isRunning();
  } else {
163
    GlobalInterpreterLock gil;
164

Samuel Jackson's avatar
Samuel Jackson committed
165
    GNU_DIAG_OFF("parentheses-equality")
166
    PyObject *result = PyObject_CallObject(m_isRunningObj, nullptr);
167
    if (PyErr_Occurred())
168
      throw PythonException();
169
    if (PyBool_Check(result)) {
Samuel Jackson's avatar
Samuel Jackson committed
170

171
172
173
174
175
#if PY_MAJOR_VERSION >= 3
      return static_cast<bool>(PyLong_AsLong(result));
#else
      return static_cast<bool>(PyInt_AsLong(result));
#endif
Samuel Jackson's avatar
Samuel Jackson committed
176

177
    } else
178
      throw std::runtime_error("Algorithm.isRunning - Expected bool return type.");
179
  }
Samuel Jackson's avatar
Samuel Jackson committed
180
  GNU_DIAG_ON("parentheses-equality")
181
}
182

183
template <typename BaseAlgorithm> void AlgorithmAdapter<BaseAlgorithm>::cancel() {
184
185
186
  try {
    return callMethod<void>(getSelf(), "cancel");
  } catch (UndefinedAttributeError &) {
187
    SuperClass::cancel();
188
  }
189
}
190

191
192
193
/**
 * @copydoc Mantid::API::Algorithm::validateInputs
 */
194
template <typename BaseAlgorithm> std::map<std::string, std::string> AlgorithmAdapter<BaseAlgorithm>::validateInputs() {
195
  using boost::python::dict;
196
197
  std::map<std::string, std::string> resultMap;

198
  try {
199
    GlobalInterpreterLock gil;
200
    dict resultDict = callMethod<dict>(getSelf(), "validateInputs");
201
202
203
204
    // convert to a map<string,string>
    boost::python::list keys = resultDict.keys();
    size_t numItems = boost::python::len(keys);
    for (size_t i = 0; i < numItems; ++i) {
205
206
      boost::python::object key = keys[i];
      boost::python::object value = resultDict[key];
207
208
      if (value) {
        try {
209
          std::string keyAsString = boost::python::extract<std::string>(key);
210
          std::string valueAsString = boost::python::extract<std::string>(value);
211
          resultMap[std::move(keyAsString)] = std::move(valueAsString);
212
        } catch (boost::python::error_already_set &) {
213
214
215
          this->getLogger().error() << "In validateInputs(self): Invalid type for key/value pair "
                                    << "detected in dict.\n"
                                    << "All keys and values must be strings\n";
216
        }
217
218
      }
    }
219
220
  } catch (UndefinedAttributeError &) {
    return resultMap;
221
  }
222

223
224
  return resultMap;
}
225

226
227
/// Set the summary text
/// @param summary Wiki text
228
229
230
231
232
233
234
235
236
template <typename BaseAlgorithm> void AlgorithmAdapter<BaseAlgorithm>::setWikiSummary(const std::string &summary) {
  std::string msg = "self.setWikiSummary() is deprecated and will be removed in a future "
                    "release.\n"
                    "To ensure continued functionality remove the line containing "
                    "'self.setWikiSummary'\n"
                    "and add a new function outside of the current one defined like so:\n"
                    "def summary(self):\n"
                    "    \"" +
                    summary + "\"\n";
237
238
239
240

  PyErr_Warn(PyExc_DeprecationWarning, msg.c_str());
  m_wikiSummary = summary;
}
241

242
243
244
245
246
247
248
/**
 * Declare a preconstructed property.
 * @param self A reference to the calling Python object
 * @param prop :: A pointer to a property
 * @param doc :: An optional doc string
 */
template <typename BaseAlgorithm>
249
250
void AlgorithmAdapter<BaseAlgorithm>::declarePyAlgProperty(boost::python::object &self, Kernel::Property *prop,
                                                           const std::string &doc) {
251
252
253
254
  BaseAlgorithm &caller = extract<BaseAlgorithm &>(self);
  // We need to clone the property so that python doesn't own the object that
  // gets inserted
  // into the manager
255
  caller.declareProperty(std::unique_ptr<Kernel::Property>(prop->clone()), doc);
256
}
257

258
/**
259
260
 * Declare a property using the type of the defaultValue, a documentation
 * string
261
262
263
264
265
266
267
268
269
270
 * and validator
 * @param self A reference to the calling Python object
 * @param name :: The name of the new property
 * @param defaultValue :: A default value for the property. The type is mapped
 * to a C++ type
 * @param validator :: A validator object
 * @param doc :: The documentation string
 * @param direction :: The direction of the property
 */
template <typename BaseAlgorithm>
271
272
273
274
void AlgorithmAdapter<BaseAlgorithm>::declarePyAlgProperty(boost::python::object &self, const std::string &name,
                                                           const boost::python::object &defaultValue,
                                                           const boost::python::object &validator,
                                                           const std::string &doc, const int direction) {
275
  BaseAlgorithm &caller = extract<BaseAlgorithm &>(self);
276
  auto prop = std::unique_ptr<Kernel::Property>(
277
      Registry::PropertyWithValueFactory::create(name, defaultValue, validator, direction));
278
  caller.declareProperty(std::move(prop), doc);
279
}
280

281
282
283
284
285
286
287
288
289
290
291
/**
 * Declare a property using the type of the defaultValue and a documentation
 * string
 * @param self A reference to the calling Python object
 * @param name :: The name of the new property
 * @param defaultValue :: A default value for the property. The type is mapped
 * to a C++ type
 * @param doc :: The documentation string
 * @param direction :: The direction of the property
 */
template <typename BaseAlgorithm>
292
293
294
void AlgorithmAdapter<BaseAlgorithm>::declarePyAlgProperty(boost::python::object &self, const std::string &name,
                                                           const boost::python::object &defaultValue,
                                                           const std::string &doc, const int direction) {
295
  BaseAlgorithm &caller = extract<BaseAlgorithm &>(self);
296
297
  auto prop =
      std::unique_ptr<Kernel::Property>(Registry::PropertyWithValueFactory::create(name, defaultValue, direction));
298
  caller.declareProperty(std::move(prop), doc);
299
}
300

301
302
303
304
305
306
307
308
309
/**
 * Declare a property using the type of the defaultValue
 * @param self A reference to the calling Python object
 * @param name :: The name of the new property
 * @param defaultValue :: A default value for the property. The type is mapped
 * to a C++ type
 * @param direction :: The direction of the property
 */
template <typename BaseAlgorithm>
310
311
312
void AlgorithmAdapter<BaseAlgorithm>::declarePyAlgProperty(boost::python::object &self, const std::string &name,
                                                           const boost::python::object &defaultValue,
                                                           const int direction) {
313
314
  declarePyAlgProperty(self, name, defaultValue, "", direction);
}
315

316
317
318
//---------------------------------------------------------------------------------------------
// Private members
//---------------------------------------------------------------------------------------------
319

320
321
322
323
324
/**
 * Private init for this algorithm. Expected to be
 * overridden in the subclass by a function named PyInit
 */
template <typename BaseAlgorithm> void AlgorithmAdapter<BaseAlgorithm>::init() {
325
  callMethod<void>(getSelf(), "PyInit");
326
}
327

328
329
330
331
332
/**
 * Private exec for this algorithm. Expected to be
 * overridden in the subclass by a function named PyExec
 */
template <typename BaseAlgorithm> void AlgorithmAdapter<BaseAlgorithm>::exec() {
333
334
335
336
337
338
339
340
  try {
    callMethod<void>(getSelf(), "PyExec");
  } catch (Mantid::PythonInterface::PythonException &) {
    if (BaseAlgorithm::getCancel())
      throw Mantid::API::Algorithm::CancelException();
    else
      throw;
  }
341
}
342

343
344
345
346
347
//-----------------------------------------------------------------------------------------------------------------------------
// Concete instantiations (avoids definitions being all in the headers)
//-----------------------------------------------------------------------------------------------------------------------------
/// API::Algorithm as base
template class AlgorithmAdapter<API::Algorithm>;
348
349
350
template class AlgorithmAdapter<API::SerialAlgorithm>;
template class AlgorithmAdapter<API::ParallelAlgorithm>;
template class AlgorithmAdapter<API::DistributedAlgorithm>;
351
352
/// API::DataProcesstor as base
template class AlgorithmAdapter<API::DataProcessorAlgorithm>;
353
354
355
template class AlgorithmAdapter<API::SerialDataProcessorAlgorithm>;
template class AlgorithmAdapter<API::ParallelDataProcessorAlgorithm>;
template class AlgorithmAdapter<API::DistributedDataProcessorAlgorithm>;
LamarMoore's avatar
LamarMoore committed
356
357
} // namespace PythonInterface
} // namespace Mantid