Algorithm.cpp 11.2 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
#ifdef _MSC_VER
8
9
10
#pragma warning(disable : 4250) // Disable warning regarding inheritance via
                                // dominance, we have no way around it with the
                                // design
11
#endif
12
#include "MantidAPI/DistributedAlgorithm.h"
LamarMoore's avatar
LamarMoore committed
13
14
15
#include "MantidAPI/ParallelAlgorithm.h"
#include "MantidAPI/SerialAlgorithm.h"
#include "MantidKernel/WarningSuppressions.h"
16
17
#include "MantidPythonInterface/api/PythonAlgorithm/AlgorithmAdapter.h"
#ifdef _MSC_VER
18
#pragma warning(default : 4250)
19
#endif
20
#include "MantidPythonInterface/core/GetPointer.h"
21

22
#include <boost/optional.hpp>
23
#include <boost/python/bases.hpp>
24
#include <boost/python/class.hpp>
25
#include <boost/python/dict.hpp>
26
#include <boost/python/exception_translator.hpp>
27
#include <boost/python/overloads.hpp>
28
#include <boost/python/raw_function.hpp>
29
#include <boost/python/register_ptr_to_python.hpp>
30
#include <boost/python/scope.hpp>
31

32
33
#include <cstddef>

34
using Mantid::API::Algorithm;
35
using Mantid::API::DistributedAlgorithm;
LamarMoore's avatar
LamarMoore committed
36
37
using Mantid::API::ParallelAlgorithm;
using Mantid::API::SerialAlgorithm;
38
using Mantid::Kernel::Direction;
LamarMoore's avatar
LamarMoore committed
39
using Mantid::PythonInterface::AlgorithmAdapter;
40
using namespace boost::python;
41

42
GET_POINTER_SPECIALIZATION(Algorithm)
43
44
45
GET_POINTER_SPECIALIZATION(SerialAlgorithm)
GET_POINTER_SPECIALIZATION(ParallelAlgorithm)
GET_POINTER_SPECIALIZATION(DistributedAlgorithm)
46

47
namespace {
48
49
50
51
using PythonAlgorithm = AlgorithmAdapter<Algorithm>;
using PythonSerialAlgorithm = AlgorithmAdapter<SerialAlgorithm>;
using PythonParallelAlgorithm = AlgorithmAdapter<ParallelAlgorithm>;
using PythonDistributedAlgorithm = AlgorithmAdapter<DistributedAlgorithm>;
52
53

// declarePyAlgProperty(property*,doc)
Samuel Jones's avatar
Samuel Jones committed
54
using declarePropertyType1 = void (*)(boost::python::object &, Mantid::Kernel::Property *, const std::string &);
55
// declarePyAlgProperty(name, defaultValue, validator, doc, direction)
Samuel Jones's avatar
Samuel Jones committed
56
57
using declarePropertyType2 = void (*)(boost::python::object &, const std::string &, const boost::python::object &,
                                      const boost::python::object &, const std::string &, const int);
58
// declarePyAlgProperty(name, defaultValue, doc, direction)
Samuel Jones's avatar
Samuel Jones committed
59
using declarePropertyType3 = void (*)(boost::python::object &, const std::string &, const boost::python::object &,
60
                                      const std::string &, const int);
61
// declarePyAlgProperty(name, defaultValue, direction)
Samuel Jones's avatar
Samuel Jones committed
62
63
using declarePropertyType4 = void (*)(boost::python::object &, const std::string &, const boost::python::object &,
                                      const int);
64

Samuel Jackson's avatar
Samuel Jackson committed
65
GNU_DIAG_OFF("unused-local-typedef")
66
67
// Ignore -Wconversion warnings coming from boost::python
// Seen with GCC 7.1.1 and Boost 1.63.0
Samuel Jackson's avatar
Samuel Jackson committed
68
GNU_DIAG_OFF("conversion")
Hahn, Steven's avatar
Hahn, Steven committed
69
// Overload types
Samuel Jones's avatar
Samuel Jones committed
70
71
72
BOOST_PYTHON_FUNCTION_OVERLOADS(declarePropertyType1_Overload, PythonAlgorithm::declarePyAlgProperty, 2, 3)
BOOST_PYTHON_FUNCTION_OVERLOADS(declarePropertyType2_Overload, PythonAlgorithm::declarePyAlgProperty, 3, 6)
BOOST_PYTHON_FUNCTION_OVERLOADS(declarePropertyType3_Overload, PythonAlgorithm::declarePyAlgProperty, 4, 5)
73

Samuel Jackson's avatar
Samuel Jackson committed
74
GNU_DIAG_ON("conversion")
Samuel Jackson's avatar
Samuel Jackson committed
75
GNU_DIAG_ON("unused-local-typedef")
76

Hahn, Steven's avatar
Hahn, Steven committed
77
78
79
80
81
82
/**
 * Map a CancelException to a Python KeyboardInterupt
 * @param exc A cancel exception to translate. Unused here as the message is
 * ignored
 */
void translateCancel(const Algorithm::CancelException &exc) {
83
84
  UNUSED_ARG(exc);
  PyErr_SetString(PyExc_KeyboardInterrupt, "");
85
}
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

template <typename T> boost::optional<T> extractArg(ssize_t index, const tuple &args) {
  if (index < len(args)) {
    return boost::optional<T>(extract<T>(args[index]));
  }
  return boost::none;
}

template <typename T> void extractKwargs(const dict &kwargs, const std::string &keyName, boost::optional<T> &out) {
  if (kwargs.has_key(keyName)) {
    out = boost::optional<T>(extract<T>(kwargs.get(keyName)));
  }
}

// Signature createChildWithProps(self, name, startProgress, endProgress, enableLogging, version, **kwargs)
object createChildWithProps(tuple args, dict kwargs) {
  std::shared_ptr<Algorithm> parentAlg = extract<std::shared_ptr<Algorithm>>(args[0]);
  auto name = extractArg<std::string>(1, args);
  auto startProgress = extractArg<double>(2, args);
  auto endProgress = extractArg<double>(3, args);
  auto enableLogging = extractArg<bool>(4, args);
  auto version = extractArg<int>(5, args);

  const std::array<std::string, 5> reservedNames = {"name", "startProgress", "endProgress", "enableLogging", "version"};

  extractKwargs<std::string>(kwargs, reservedNames[0], name);
  extractKwargs<double>(kwargs, reservedNames[1], startProgress);
  extractKwargs<double>(kwargs, reservedNames[2], endProgress);
  extractKwargs<bool>(kwargs, reservedNames[3], enableLogging);
  extractKwargs<int>(kwargs, reservedNames[4], version);

  if (!name.is_initialized()) {
    throw std::invalid_argument("Please specify the algorithm name");
  }

  auto childAlg = parentAlg->createChildAlgorithm(name.value(), startProgress.value_or(-1), endProgress.value_or(-1),
                                                  enableLogging.value_or(true), version.value_or(-1));

  const list keys = kwargs.keys();
  for (int i = 0; i < len(keys); ++i) {
    const std::string propName = extract<std::string>(keys[i]);

    if (std::find(reservedNames.cbegin(), reservedNames.cend(), propName) != reservedNames.cend())
      continue;

    object curArg = kwargs[keys[i]];
Gemma Guest's avatar
Gemma Guest committed
132
133
134
    if (!curArg)
      continue;

135
136
137
138
139
    const PyObject *rawptr = curArg.ptr();

    // This currently  doesn't handle lists, but this could be retrofitted in future work
    if (PyBool_Check(rawptr) == 1) {
      bool val = extract<bool>(curArg);
Gemma Guest's avatar
Gemma Guest committed
140
      childAlg->setProperty(propName, val);
141
142
    } else if (PyFloat_Check(rawptr) == 1) {
      double val = extract<double>(curArg);
Gemma Guest's avatar
Gemma Guest committed
143
      childAlg->setProperty(propName, val);
144
145
    } else if (PyLong_Check(rawptr) == 1) {
      int val = extract<int>(curArg);
Gemma Guest's avatar
Gemma Guest committed
146
      childAlg->setProperty(propName, val);
147
148
149
    } else {
      childAlg->setPropertyValue(propName, extract<std::string>(curArg));
    }
150
151
152
153
  }
  return object(childAlg);
}

LamarMoore's avatar
LamarMoore committed
154
} // namespace
155

156
void export_leaf_classes() {
157
  register_ptr_to_python<std::shared_ptr<Algorithm>>();
158
  register_exception_translator<Algorithm::CancelException>(&translateCancel);
159

160
  // Export Algorithm but the actual held type in Python is
161
  // std::shared_ptr<AlgorithmAdapter>
162
163
  // See
  // http://wiki.python.org/moin/boost.python/HowTo#ownership_of_C.2B-.2B-_object_extended_in_Python
Samuel Jones's avatar
Samuel Jones committed
164
  class_<Algorithm, bases<Mantid::API::IAlgorithm>, std::shared_ptr<PythonAlgorithm>, boost::noncopyable>(
165
      "Algorithm", "Base class for all algorithms")
Samuel Jones's avatar
Samuel Jones committed
166
      .def("fromString", &Algorithm::fromString, "Initialize the algorithm from a string representation")
167
      .staticmethod("fromString")
168
      .def("createChildAlgorithm", raw_function(&createChildWithProps, 1),
169
170
           "Creates and intializes a named child algorithm. Output workspaces "
           "are given a dummy name.")
Samuel Jones's avatar
Samuel Jones committed
171
172
173
      .def("declareProperty", (declarePropertyType1)&PythonAlgorithm::declarePyAlgProperty,
           declarePropertyType1_Overload((arg("self"), arg("prop"), arg("doc") = "")))
      .def("enableHistoryRecordingForChild", &Algorithm::enableHistoryRecordingForChild, (arg("self"), arg("on")),
174
           "Turns history recording on or off for an algorithm.")
Samuel Jones's avatar
Samuel Jones committed
175
176
177
178
179
180
181
      .def("declareProperty", (declarePropertyType2)&PythonAlgorithm::declarePyAlgProperty,
           declarePropertyType2_Overload((arg("self"), arg("name"), arg("defaultValue"), arg("validator") = object(),
                                          arg("doc") = "", arg("direction") = Direction::Input),
                                         "Declares a named property where the type is taken from "
                                         "the type of the defaultValue and mapped to an appropriate C++ "
                                         "type"))
      .def("declareProperty", (declarePropertyType3)&PythonAlgorithm::declarePyAlgProperty,
182
           declarePropertyType3_Overload(
Samuel Jones's avatar
Samuel Jones committed
183
               (arg("self"), arg("name"), arg("defaultValue"), arg("doc") = "", arg("direction") = Direction::Input),
184
185
186
               "Declares a named property where the type is taken from the "
               "type "
               "of the defaultValue and mapped to an appropriate C++ type"))
Samuel Jones's avatar
Samuel Jones committed
187
188
      .def("declareProperty", (declarePropertyType4)&PythonAlgorithm::declarePyAlgProperty,
           (arg("self"), arg("name"), arg("defaultValue"), arg("direction") = Direction::Input),
189
190
           "Declares a named property where the type is taken from the type "
           "of the defaultValue and mapped to an appropriate C++ type")
Samuel Jones's avatar
Samuel Jones committed
191
      .def("getLogger", &PythonAlgorithm::getLogger, arg("self"), return_value_policy<reference_existing_object>(),
192
           "Returns a reference to this algorithm's logger")
Samuel Jones's avatar
Samuel Jones committed
193
      .def("log", &PythonAlgorithm::getLogger, arg("self"), return_value_policy<reference_existing_object>(),
194
195
196
           "Returns a reference to this algorithm's logger") // Traditional name

      // deprecated methods
Samuel Jones's avatar
Samuel Jones committed
197
      .def("setWikiSummary", &PythonAlgorithm::setWikiSummary, (arg("self"), arg("summary")),
198
199
200
201
202
203
204
205
           "(Deprecated.) Set summary for the help.");

  // Prior to version 3.2 there was a separate C++ PythonAlgorithm class that
  // inherited from Algorithm and the "PythonAlgorithm"
  // name was a distinct class in Python from the Algorithm export. In 3.2 the
  // need for the C++ PythonAlgorithm class
  // was removed in favour of simply adapting the Algorithm base class. A lot of
  // client code relies on the "PythonAlgorithm" name in
206
207
208
  // Python so we simply add an alias of the Algorithm name to PythonAlgorithm
  scope().attr("PythonAlgorithm") = scope().attr("Algorithm");
}
209
210

void export_SerialAlgorithm() {
211
  register_ptr_to_python<std::shared_ptr<SerialAlgorithm>>();
Samuel Jones's avatar
Samuel Jones committed
212
213
  register_exception_translator<SerialAlgorithm::CancelException>(&translateCancel);
  class_<SerialAlgorithm, bases<Mantid::API::Algorithm>, std::shared_ptr<PythonSerialAlgorithm>, boost::noncopyable>(
214
215
216
217
218
      "SerialAlgorithm", "Base class for simple serial algorithms");
  scope().attr("PythonSerialAlgorithm") = scope().attr("SerialAlgorithm");
}

void export_ParallelAlgorithm() {
219
  register_ptr_to_python<std::shared_ptr<ParallelAlgorithm>>();
Samuel Jones's avatar
Samuel Jones committed
220
221
222
  register_exception_translator<ParallelAlgorithm::CancelException>(&translateCancel);
  class_<ParallelAlgorithm, bases<Mantid::API::Algorithm>, std::shared_ptr<PythonParallelAlgorithm>,
         boost::noncopyable>("ParallelAlgorithm", "Base class for simple parallel algorithms");
223
224
225
226
  scope().attr("PythonParallelAlgorithm") = scope().attr("ParallelAlgorithm");
}

void export_DistributedAlgorithm() {
227
  register_ptr_to_python<std::shared_ptr<DistributedAlgorithm>>();
Samuel Jones's avatar
Samuel Jones committed
228
229
230
231
  register_exception_translator<DistributedAlgorithm::CancelException>(&translateCancel);
  class_<DistributedAlgorithm, bases<Mantid::API::Algorithm>, std::shared_ptr<PythonDistributedAlgorithm>,
         boost::noncopyable>("DistributedAlgorithm", "Base class for simple distributed algorithms");
  scope().attr("PythonDistributedAlgorithm") = scope().attr("DistributedAlgorithm");
232
}