Mantid
Loading...
Searching...
No Matches
IAlgorithm.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#ifdef _MSC_VER
8#pragma warning(disable : 4250) // Disable warning regarding inheritance via
9 // dominance, we have no way around it with the
10 // design
11#endif
14#ifdef _MSC_VER
15#pragma warning(default : 4250)
16#endif
24
25#include <Poco/ActiveResult.h>
26#include <Poco/Thread.h>
27
28#include <boost/python/arg_from_python.hpp>
29#include <boost/python/bases.hpp>
30#include <boost/python/class.hpp>
31#include <boost/python/object.hpp>
32#include <boost/python/operators.hpp>
33#include <boost/python/register_ptr_to_python.hpp>
34
35#include <unordered_map>
36
47using namespace boost::python;
48
50
51namespace {
52
54using ToPyString = to_python_value<const std::string &>;
55
56// Global map of the thread ID to the current algorithm object
57using ThreadIDObjectMap = std::unordered_map<long, object>;
58
59ThreadIDObjectMap &threadIDMap() {
60 static ThreadIDObjectMap threadIDs;
61 return threadIDs;
62}
63
70void _trackAlgorithm(long threadID, const object &alg) { threadIDMap()[threadID] = alg; }
71
76void _forgetAlgorithm(long threadID) { threadIDMap().erase(threadID); }
77
83object _algorithmInThread(long threadID) {
84 auto &threadMap = threadIDMap();
85 auto it = threadMap.find(threadID);
86 if (it != threadMap.end()) {
87 auto value = it->second;
88 threadMap.erase(it);
89 return value;
90 } else {
91 return object();
92 }
93}
94
97struct MandatoryFirst {
100 bool operator()(const Property *p1, const Property *p2) const {
101 // this is false, unless p1 is not valid and p2 is valid
102 return (!p1->isValid().empty()) && (p2->isValid().empty());
103 }
104};
105
106//----------------------- Property ordering ------------------------------
108using PropertyVector = std::vector<Property *>;
109
117PropertyVector apiOrderedProperties(const IAlgorithm &propMgr) {
118 PropertyVector properties(propMgr.getProperties()); // Makes a copy so that it can be sorted
119 std::stable_sort(properties.begin(), properties.end(), MandatoryFirst());
120 return properties;
121}
122
131list getInputPropertiesWithMandatoryFirst(const IAlgorithm &self) {
132 PropertyVector properties(apiOrderedProperties(self));
133
135 list names;
136 ToPyString toPyStr;
137 for (const auto &prop : properties) {
138 if (prop->direction() != Direction::Output) {
139 names.append(handle<>(toPyStr(prop->name())));
140 }
141 }
142 return names;
143}
144
152list getAlgorithmPropertiesOrdered(const IAlgorithm &self) {
153 PropertyVector properties(apiOrderedProperties(self));
154
156 list names;
157 ToPyString toPyStr;
158 for (const auto &prop : properties) {
159 names.append(handle<>(toPyStr(prop->name())));
160 }
161 return names;
162}
163
169list getOutputProperties(const IAlgorithm &self) {
170 const PropertyVector &properties(self.getProperties()); // No copy
171
173 list names;
174 ToPyString toPyStr;
175 for (const auto &p : properties) {
176 if (p->direction() == Direction::Output) {
177 names.append(handle<>(toPyStr(p->name())));
178 }
179 }
180 return names;
181}
182
188list getInOutProperties(const IAlgorithm &self) {
189 const PropertyVector &properties(self.getProperties()); // No copy
190
192 list names;
193 ToPyString toPyStr;
194 for (const auto &p : properties) {
195 if (p->direction() == Direction::InOut) {
196 names.append(handle<>(toPyStr(p->name())));
197 }
198 }
199 return names;
200}
201
207std::string createDocString(const IAlgorithm &self) {
208 const std::string EOL = "\n";
209
210 // Put in the quick overview message
211 std::stringstream buffer;
212 std::string temp = self.summary();
213 if (!temp.empty())
214 buffer << temp << EOL << EOL;
215
216 // get a sorted copy of the properties
217 PropertyVector properties(apiOrderedProperties(self));
218
219 const size_t numProps(properties.size());
220
221 buffer << "Property descriptions: " << EOL << EOL;
222 // write the actual property descriptions
223 for (size_t i = 0; i < numProps; ++i) {
224 Mantid::Kernel::Property *prop = properties[i];
225 buffer << prop->name() << "(" << Mantid::Kernel::Direction::asText(prop->direction());
226 if (!prop->isValid().empty())
227 buffer << ":req";
228 buffer << ") *" << prop->type() << "* ";
229 std::vector<std::string> allowed = prop->allowedValues();
230 if (!prop->documentation().empty() || !allowed.empty()) {
231 buffer << " " << prop->documentation();
232 if (!allowed.empty()) {
233 buffer << "[" << Mantid::Kernel::Strings::join(allowed.begin(), allowed.end(), ", ");
234 buffer << "]";
235 }
236 buffer << EOL;
237 if (i < numProps - 1)
238 buffer << EOL;
239 }
240 }
241
242 return buffer.str();
243}
244
251struct AllowCThreads {
252 explicit AllowCThreads(const object &algm)
253 : m_tracefunc(nullptr), m_tracearg(nullptr), m_saved(nullptr), m_tracking(false) {
254 PyThreadState *curThreadState = PyThreadState_GET();
255 m_tracefunc = curThreadState->c_tracefunc;
256 m_tracearg = curThreadState->c_traceobj;
257 Py_XINCREF(m_tracearg);
258 PyEval_SetTrace(nullptr, nullptr);
259 if (!isNone(algm)) {
260 _trackAlgorithm(curThreadState->thread_id, algm);
261 m_tracking = true;
262 }
263 m_saved = PyEval_SaveThread();
264 }
265 ~AllowCThreads() {
266 PyEval_RestoreThread(m_saved);
267 if (m_tracking) {
268 _forgetAlgorithm(m_saved->thread_id);
269 }
270 PyEval_SetTrace(m_tracefunc, m_tracearg);
271 Py_XDECREF(m_tracearg);
272 }
273
274private:
275 Py_tracefunc m_tracefunc;
276 PyObject *m_tracearg;
277 PyThreadState *m_saved;
278 bool m_tracking;
279};
280
285bool executeProxy(object &self) {
286 // We need to do 2 things before we execute the algorthm:
287 // 1. store a reference to this algorithm mapped to the current thread ID to
288 // allow it to be looked up when an abort request is received
289 // 2. release the GIL, drop the Python threadstate and reset anything
290 // installed
291 // via PyEval_SetTrace while we execute the C++ code - AllowCThreads()
292 // does this for us
293
294 // Extract this before dropping GIL as I'm not sure if it calls any Python
295 auto &calg = extract<IAlgorithm &>(self)();
296 AllowCThreads threadStateHolder((!calg.isChild()) ? self : object());
297 return calg.execute();
298}
299
304void executeAsync(const object &self) {
305 auto &calg = extract<IAlgorithm &>(self)();
306 calg.executeAsync();
307}
308
314PyObject *getAlgorithmID(const IAlgorithm &self) {
315 AlgorithmID id = self.getAlgorithmID();
316 if (id)
317 return to_python_value<AlgorithmIDProxy>()(AlgorithmIDProxy(id));
318 else
319 Py_RETURN_NONE;
320}
321
322//--------------------------------------------------------------------------------------
323// Deprecated wrappers
324//--------------------------------------------------------------------------------------
329std::string getOptionalMessage(const IAlgorithm &self) {
330 PyErr_Warn(PyExc_DeprecationWarning, ".getOptionalMessage() is deprecated. Use .summary() instead.");
331 return self.summary();
332}
333
338std::string getWikiSummary(const IAlgorithm &self) {
339 PyErr_Warn(PyExc_DeprecationWarning, ".getWikiSummary() is deprecated. Use .summary() instead.");
340 return self.summary();
341}
342
347boost::python::dict validateInputs(IAlgorithm &self) {
348 auto map = self.validateInputs();
350 return MapToPyDictionary(map)();
351}
352
353void initializeProxy(IAlgorithm &self) {
355 self.initialize();
356}
357
358} // namespace
359
361 class_<AlgorithmIDProxy>("AlgorithmID", no_init).def(self == self);
362
363 register_ptr_to_python<std::shared_ptr<IAlgorithm>>();
364 register_ptr_to_python<std::shared_ptr<const IAlgorithm>>();
365
366 class_<IAlgorithm, bases<IPropertyManager>, boost::noncopyable>("IAlgorithm", "Interface for all algorithms", no_init)
367 .def("name", &IAlgorithm::name, arg("self"), "Returns the name of the algorithm")
368 .def("alias", &IAlgorithm::alias, arg("self"), "Return the aliases for the algorithm")
369 .def("aliasDeprecated", &IAlgorithm::aliasDeprecated, arg("self"),
370 "Deprecation date (in ISO8601 format) for the algorithm aliases. "
371 "Returns empty string if no deprecation date")
372 .def("version", &IAlgorithm::version, arg("self"), "Returns the version number of the algorithm")
373 .def("cancel", &IAlgorithm::cancel, arg("self"), "Request that the algorithm stop running")
374 .def("category", &IAlgorithm::category, arg("self"), "Returns the category containing the algorithm")
375 .def("categories", &IAlgorithm::categories, arg("self"), return_value_policy<VectorToNumpy>(),
376 "Returns the list of categories this algorithm belongs to")
377 .def("seeAlso", &IAlgorithm::seeAlso, arg("self"), return_value_policy<VectorToNumpy>(),
378 "Returns the list of similar algorithms")
379 .def("summary", &IAlgorithm::summary, arg("self"), "Returns a summary message describing the algorithm")
380 .def("helpURL", &IAlgorithm::helpURL, arg("self"), "Returns optional URL for algorithm documentation")
381 .def("workspaceMethodName", &IAlgorithm::workspaceMethodName, arg("self"),
382 "Returns a name that will be used when attached as a workspace "
383 "method. Empty string indicates do not attach")
384 .def("workspaceMethodOn", &IAlgorithm::workspaceMethodOn, arg("self"),
385 return_value_policy<VectorToNumpy>(), // creates a list for strings
386 "Returns a set of class names that will have the method attached. "
387 "Empty list indicates all types")
388 .def("workspaceMethodInputProperty", &IAlgorithm::workspaceMethodInputProperty, arg("self"),
389 "Returns the name of the input workspace property used by the "
390 "calling object")
391 .def("getAlgorithmID", &getAlgorithmID, arg("self"), "Returns a unique identifier for this algorithm object")
392 .def("docString", &createDocString, arg("self"), "Returns a doc string for the algorithm")
393 .def("mandatoryProperties", &getInputPropertiesWithMandatoryFirst, arg("self"),
394 "Returns a list of input and in/out property names that is ordered "
395 "such that the mandatory properties are first followed by the "
396 "optional ones.")
397 .def("orderedProperties", &getAlgorithmPropertiesOrdered, arg("self"),
398 "Return a list of input, in/out and output properties "
399 "such that the mandatory properties are first followed by the "
400 "optional ones.")
401 .def("outputProperties", &getOutputProperties, arg("self"),
402 "Returns a list of the output properties on the algorithm")
403 .def("inoutProperties", &getInOutProperties, arg("self"),
404 "Returns a list of the inout properties on the algorithm")
405 .def("isInitialized", &IAlgorithm::isInitialized, arg("self"),
406 "Returns True if the algorithm is initialized, False otherwise")
407 .def("isExecuted", &IAlgorithm::isExecuted, arg("self"),
408 "Returns True if the algorithm has been executed successfully, "
409 "False otherwise")
410 .def("isLogging", &IAlgorithm::isLogging, arg("self"),
411 "Returns True if the "
412 "algorithm's logger is turned "
413 "on, False otherwise")
414 .def("isRunning", &IAlgorithm::isRunning, arg("self"),
415 "Returns True if the algorithm "
416 "is considered to be running, "
417 "False otherwise")
418 .def("setChild", &IAlgorithm::setChild, (arg("self"), arg("is_child")),
419 "If true this algorithm is run as a child algorithm. There will be "
420 "no logging and nothing is stored in the Analysis Data Service")
421 .def("enableHistoryRecordingForChild", &IAlgorithm::enableHistoryRecordingForChild, (arg("self"), arg("on")),
422 "If true then history will be recorded regardless of the child "
423 "status")
424 .def("setAlgStartupLogging", &IAlgorithm::setAlgStartupLogging, (arg("self"), arg("enabled")),
425 "If true then allow logging of start and end messages")
426 .def("getAlgStartupLogging", &IAlgorithm::getAlgStartupLogging, arg("self"),
427 "Returns true if logging of start and end messages")
428 .def("setAlwaysStoreInADS", &IAlgorithm::setAlwaysStoreInADS, (arg("self"), arg("do_store")),
429 "If true then even child algorithms will have their workspaces "
430 "stored in the ADS.")
431 .def("isChild", &IAlgorithm::isChild, arg("self"),
432 "Returns True if the algorithm has been marked to run as a child. "
433 "If True then Output workspaces "
434 "are NOT stored in the Analysis Data Service but must be retrieved "
435 "from the property.")
436 .def("setLogging", &IAlgorithm::setLogging, (arg("self"), arg("value")), "Toggle logging on/off.")
437 .def("setRethrows", &IAlgorithm::setRethrows, (arg("self"), arg("rethrow")),
438 "To query whether an algorithm "
439 "should rethrow exceptions when "
440 "executing.")
441 .def("initialize", &initializeProxy, arg("self"), "Initializes the algorithm")
442 .def("validateInputs", &validateInputs, arg("self"),
443 "Cross-check all inputs and return any errors as a dictionary")
444 .def("execute", &executeProxy, arg("self"), "Runs the algorithm and returns whether it has been successful")
445 .def("executeAsync", &executeAsync, arg("self"),
446 "Starts the algorithm in a separate thread and returns immediately")
447 // 'Private' static methods
448 .def("_algorithmInThread", &_algorithmInThread, arg("thread_id"))
449 .staticmethod("_algorithmInThread")
450 // Special methods
451 .def("__str__", &IAlgorithm::toString, arg("self"))
452
453 // deprecated methods
454 .def("getOptionalMessage", &getOptionalMessage, arg("self"),
455 "Returns the optional user message attached to the algorithm")
456 .def("getWikiSummary", &getWikiSummary, arg("self"), "Returns the summary found on the wiki page");
457}
double value
The value of the point.
Definition: FitMW.cpp:51
#define GET_POINTER_SPECIALIZATION(TYPE)
Definition: GetPointer.h:17
void export_ialgorithm()
Definition: IAlgorithm.cpp:360
IAlgorithm is the interface implemented by the Algorithm base class.
Definition: IAlgorithm.h:45
virtual const std::string workspaceMethodInputProperty() const =0
Returns the name of the input workspace property used by the calling object.
virtual std::string toString() const =0
Serialize an algorithm.
virtual bool isExecuted() const =0
Check whether the algorithm has been executed sucessfully.
virtual bool isInitialized() const =0
Check whether the algorithm is initialized properly.
virtual const std::string alias() const =0
function to return any aliases of the algorithm.
virtual bool isChild() const =0
To query whether algorithm is a child. Default to false.
virtual int version() const =0
function to return a version of the algorithm, must be overridden in all algorithms
virtual const std::string aliasDeprecated() const =0
Expiration date (in ISO8601 format) for the algorithm aliases. Empty if no expiration date.
virtual const std::string helpURL() const =0
function to return an optional URL for documentation.
virtual const std::vector< std::string > categories() const =0
Function to return all of the categories that contain this algorithm.
virtual bool isLogging() const =0
returns the status of logging, True = enabled
virtual const std::vector< std::string > seeAlso() const =0
Function to return all of the seeAlso algorithms related to this algorithm.
virtual bool isRunning() const =0
True if the algorithm is running.
virtual const std::string category() const =0
function to return a category of the algorithm.
virtual void setChild(const bool isChild)=0
To set whether algorithm is a child.
virtual const std::string workspaceMethodName() const =0
virtual const std::vector< std::string > workspaceMethodOn() const =0
Returns a set of class names that will have the method attached.
virtual bool getAlgStartupLogging() const =0
get the state of Logging of start and end messages
virtual const std::string summary() const =0
function returns a summary message that will be displayed in the default GUI, and in the help.
virtual void enableHistoryRecordingForChild(const bool on)=0
If true history will be recorded for a child.
virtual void setLogging(const bool value)=0
Logging can be disabled by passing a value of false.
virtual void cancel()=0
Raises the cancel flag.
virtual void setAlwaysStoreInADS(const bool doStore)=0
Set whether we always store the output in the analysis data service.
virtual void setRethrows(const bool rethrow)=0
To query whether an algorithm should rethrow exceptions when executing.
virtual void setAlgStartupLogging(const bool enabled)=0
disable Logging of start and end messages
virtual const std::string name() const =0
function to return a name of the algorithm, must be overridden in all algorithms
virtual std::map< std::string, std::string > validateInputs()=0
Method checking errors on ALL the inputs, before execution.
virtual AlgorithmID getAlgorithmID() const =0
Algorithm ID.
virtual void initialize()=0
Initialization method invoked by the framework.
Interface to PropertyManager.
virtual const std::vector< Property * > & getProperties() const =0
Get the list of managed properties.
Base class for properties.
Definition: Property.h:94
unsigned int direction() const
returns the direction of the property
Definition: Property.h:172
const std::string & documentation() const
Get the property's documentation string.
Definition: Property.cpp:65
virtual std::string isValid() const
Overridden function that checks whether the property, if not overriden returns "".
Definition: Property.cpp:82
const std::string & name() const
Get the property's name.
Definition: Property.cpp:60
virtual std::vector< std::string > allowedValues() const
Returns the set of valid values for this property, if such a set exists.
Definition: Property.cpp:140
const std::string type() const
Returns the type of the property as a string.
Definition: Property.cpp:76
Defines a structure for acquiring/releasing the Python GIL using the RAII pattern.
Defines a structure for releasing the Python GIL using the RAII pattern.
std::shared_ptr< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm
void * AlgorithmID
As we have multiple interfaces to the same logical algorithm we need a way of uniquely identifying ma...
Definition: IAlgorithm.h:28
DLLExport std::string join(ITERATOR_TYPE begin, ITERATOR_TYPE end, const std::string &separator, typename std::enable_if<!(std::is_same< typename std::iterator_traits< ITERATOR_TYPE >::iterator_category, std::random_access_iterator_tag >::value)>::type *=nullptr)
Join a set or vector of (something that turns into a string) together into one string,...
Definition: Strings.h:84
bool isNone(PyObject *ptr)
Definition: IsNone.h:26
Describes the direction (within an algorithm) of a Property.
Definition: Property.h:50
@ InOut
Both an input & output workspace.
Definition: Property.h:55
@ Output
An output workspace.
Definition: Property.h:54
static const std::string asText(const unsigned int &direction)
Returns a text representation of the input Direction enum.
Definition: Property.h:60
Provides a concrete type to wrap & return AlgorithmIDs that are actually just typedefs for void*.
Implements a return value policy that returns a numpy array from a function returning a std::vector b...