Mantid
Loading...
Searching...
No Matches
AlgorithmFactory.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 +
15
16#include <boost/python/class.hpp>
17#include <boost/python/def.hpp>
18#include <boost/python/dict.hpp>
19#include <boost/python/list.hpp>
20#include <boost/python/overloads.hpp>
21#include <mutex>
22
23// Python frameobject. This is under the boost includes so that boost will have
24// done the
25// include of Python.h which it ensures is done correctly
26#include <frameobject.h>
27
28using namespace Mantid::API;
29using namespace boost::python;
33
35
36namespace {
38
39//------------------------------------------------------------------------------------------------------
49dict getRegisteredAlgorithms(AlgorithmFactoryImpl const *const self, bool includeHidden) {
50 const auto keys = self->getKeys(includeHidden);
51 dict inventory;
52 for (const auto &key : keys) {
53 std::pair<std::string, int> algInfo;
54 {
55 // We need to release the GIL here to prevent a deadlock when using Python log channels
57 algInfo = self->decodeName(key);
58 }
59 object name(handle<>(to_python_value<const std::string &>()(algInfo.first)));
60 object ver(handle<>(to_python_value<const int &>()(algInfo.second)));
61 // There seems to be no way to "promote" the return of .get to a list
62 // without copying it
63 object versions;
64 if (inventory.has_key(name)) {
65 versions = inventory.get(name);
66 } else {
67 versions = list();
68 inventory[name] = versions;
69 }
70 versions.attr("append")(ver);
71 }
72 return inventory;
73}
74
80list getDescriptors(AlgorithmFactoryImpl const *const self, bool includeHidden = false, bool includeAlias = false) {
81 const auto descriptors = self->getDescriptors(includeHidden, includeAlias);
82 list pyDescriptors;
83 for (const auto &descr : descriptors) {
84 pyDescriptors.append(boost::python::object(descr));
85 }
86 return pyDescriptors;
87}
88
98dict getCategoriesandState(AlgorithmFactoryImpl const *const self) {
99 const auto categories = self->getCategoriesWithState();
100 dict pythonCategories;
101 for (auto &it : categories) {
102 object categoryName(handle<>(to_python_value<const std::string &>()(it.first)));
103 pythonCategories[categoryName] = it.second;
104 }
105
106 return pythonCategories;
107}
108
109//------------------------------------------------------------------------------
110// Python algorithm subscription
111//------------------------------------------------------------------------------
112
113// Python algorithm registration mutex in anonymous namespace (aka static)
114std::recursive_mutex PYALG_REGISTER_MUTEX;
115
116GNU_DIAG_OFF("cast-qual")
117
118
124void subscribe(AlgorithmFactoryImpl &self, const boost::python::object &obj) {
125 UninstallTrace uninstallTrace;
126 std::lock_guard<std::recursive_mutex> lock(PYALG_REGISTER_MUTEX);
127
128 static auto *const pyAlgClass = (PyObject *)converter::registered<Algorithm>::converters.to_python_target_type();
129 // obj could be or instance/class, check instance first
130 PyObject *classObject(nullptr);
131 if (PyObject_IsInstance(obj.ptr(), pyAlgClass)) {
132 classObject = PyObject_GetAttrString(obj.ptr(), "__class__");
133 } else if (PyObject_IsSubclass(obj.ptr(), pyAlgClass)) {
134 classObject = obj.ptr(); // We need to ensure the type of lifetime
135 // management so grab the raw pointer
136 } else {
137 throw std::invalid_argument("Cannot register an algorithm that does not derive from Algorithm.");
138 }
139 boost::python::object classType(handle<>(borrowed(classObject)));
140 // Takes ownership of instantiator and replaces any existing algorithm
141 std::unique_ptr<Mantid::Kernel::AbstractInstantiator<Algorithm>> temp =
142 std::make_unique<PythonObjectInstantiator<Algorithm>>(classType);
143 auto descr = self.subscribe(std::move(temp), AlgorithmFactoryImpl::OverwriteCurrent);
144
145 // Python algorithms cannot yet act as loaders so remove any registered ones
146 // from the FileLoaderRegistry
147 FileLoaderRegistry::Instance().unsubscribe(descr.first, descr.second);
148}
149
150GNU_DIAG_OFF("unused-local-typedef")
151// Ignore -Wconversion warnings coming from boost::python
152// Seen with GCC 7.1.1 and Boost 1.63.0
153GNU_DIAG_OFF("conversion")
154
155// cppcheck-suppress unknownMacro
156BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(existsOverloader, exists, 1, 2)
157BOOST_PYTHON_FUNCTION_OVERLOADS(getDescriptors_overloads, getDescriptors, 1, 3)
158
159GNU_DIAG_ON("conversion")
160GNU_DIAG_ON("unused-local-typedef")
161
163} // namespace
164GNU_DIAG_ON("cast-qual")
165
167
168 class_<AlgorithmDescriptor>("AlgorithmDescriptor")
169 .def_readonly("name", &AlgorithmDescriptor::name)
170 .def_readonly("alias", &AlgorithmDescriptor::alias)
171 .def_readonly("category", &AlgorithmDescriptor::category)
172 .def_readonly("version", &AlgorithmDescriptor::version);
173
174 class_<AlgorithmFactoryImpl, boost::noncopyable>("AlgorithmFactoryImpl", no_init)
175 .def("exists", &AlgorithmFactoryImpl::exists,
176 existsOverloader((arg("name"), arg("version") = -1), "Returns true if the given algorithm exists with "
177 "an option to specify the version"))
178
179 .def("getRegisteredAlgorithms", &getRegisteredAlgorithms, (arg("self"), arg("include_hidden")),
180 "Returns a Python dictionary of currently registered algorithms")
181 .def("highestVersion", &AlgorithmFactoryImpl::highestVersion, (arg("self"), arg("algorithm_name")),
182 "Returns the highest version of the named algorithm. Throws "
183 "ValueError if no algorithm can be found")
184 .def("subscribe", &subscribe, (arg("self"), arg("object")),
185 "Register a Python class derived from "
186 "PythonAlgorithm into the factory")
187 .def("getDescriptors", &getDescriptors,
188 getDescriptors_overloads((arg("self"), arg("include_hidden") = false, arg("include_alias") = false),
189 "Return a list of descriptors of registered algorithms. Each "
190 "descriptor is a list: [name, version, category, alias]."))
191 .def("getCategoriesandState", &getCategoriesandState,
192 "Return the categories of the algorithms. This includes those within "
193 "the Factory itself and any cleanly constructed algorithms stored "
194 "here")
195 .def("unsubscribe", &AlgorithmFactoryImpl::unsubscribe, (arg("self"), arg("name"), arg("version")),
196 "Returns the highest version of the named algorithm. Throws "
197 "ValueError if no algorithm can be found")
198 .def("enableNotifications", &AlgorithmFactoryImpl::enableNotifications)
199 .def("disableNotifications", &AlgorithmFactoryImpl::disableNotifications)
200
201 .def("Instance", &AlgorithmFactory::Instance, return_value_policy<reference_existing_object>(),
202 "Returns a reference to the AlgorithmFactory singleton")
203 .staticmethod("Instance");
204}
std::string name
Definition Run.cpp:60
#define GET_POINTER_SPECIALIZATION(TYPE)
Definition GetPointer.h:17
void export_AlgorithmFactory()
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.
The AlgorithmFactory class is in charge of the creation of concrete instances of Algorithms.
bool exists(const std::string &algorithmName, const int version=-1)
Does an algorithm of the given name and version exist.
int highestVersion(const std::string &algorithmName) const
Returns the highest version of the algorithm currently registered.
std::vector< AlgorithmDescriptor > getDescriptors(bool includeHidden=false, bool includeAliases=false) const
Returns algorithm descriptors.
void unsubscribe(const std::string &algorithmName, const int version)
Unsubscribe the given algorithm.
std::pair< std::string, int > decodeName(const std::string &mangledName) const
unmangles the names used as keys into the name and version
const std::vector< std::string > getKeys() const override
Get the algorithm names and version - mangled use decodeName to separate.
const std::map< std::string, bool > getCategoriesWithState() const
Get the algorithm categories.
Defines a structure for releasing the Python GIL using the RAII pattern.
RAII handler to temporarily remove and reinstall a Python trace function.
std::string name
Algorithm Name.