Skip to content

Commit

Permalink
Fix extraction of C++ Workspace types from Python
Browse files Browse the repository at this point in the history
By default boost will try to construct a new pointer type when extracting
a C++ type from a Python object. We need to ensure we get back the same
pointer object so we use a reference type in ExtractWorkspace. This then
requires that all return types that convert to a Python object return
a common Workspace_sptr or the reference extraction fails.
Refs #10649
  • Loading branch information
martyngigg committed Jun 16, 2015
1 parent 78545cf commit 6801884
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ template <typename WorkspaceType> struct WorkspacePropertyExporter {
locking, validator->clone());
}

/**
* Ensure the stored type is always a Workspace_sptr
* This allows a reference to a Workspace_sptr to be used withh boost::python::extract
*/
static Mantid::API::Workspace_sptr value(const TypedWorkspaceProperty& self) {
return self.operator()();
}

/**
* Defines the necessary exports for a WorkspaceProperty<WorkspaceType>. This
* includes a
Expand Down Expand Up @@ -140,7 +148,11 @@ template <typename WorkspaceType> struct WorkspacePropertyExporter {
args("name", "defaultValue", "direction",
"optional", "locking", "validator")))
.def("isOptional", &TypedWorkspaceProperty::isOptional,
"Returns true if the property has been marked as optional");
"Returns true if the property has been marked as optional")

.add_property("value", &value)
;

}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#ifndef MANTID_PYTHONINTERFACE_ASTYPE_H_
#define MANTID_PYTHONINTERFACE_ASTYPE_H_
/**
Copyright &copy; 2012 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
National Laboratory & European Spallation Source
This file is part of Mantid.
Mantid is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
Mantid is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
File change history is stored at: <https://github.com/mantidproject/mantid>
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/
#include <boost/python/detail/prefix.hpp>
#include <boost/python/to_python_value.hpp>
#include <boost/mpl/and.hpp>
#include <boost/type_traits/is_convertible.hpp>

/**
* Policy that can convert to return type to a super type
*/

namespace Mantid {
namespace PythonInterface {
namespace Policies {
// Utility code to help out
namespace {

//-----------------------------------------------------------------------
// Polciy implementation
//-----------------------------------------------------------------------

// The entry point for the policy is in the struct AsType below. It does
// a check as to whether the return type is valid, if so it forwards the
// call to this struct
template <typename ReturnType, typename InputType> struct AsTypeImpl {

inline PyObject *operator()(const InputType &p) const {
using namespace boost::python;
return to_python_value<ReturnType>()(ReturnType(p));
}

inline PyTypeObject const *get_pytype() const {
using namespace boost::python;
return converter::registered<ReturnType>::converters
.to_python_target_type();
}
};

// Error handler for shared pointer types. If return type is wrong then user
// sees the name of this
// class in the output, which hopefully gives a clue as to what is going on
template <typename T>
struct AsType_Requires_New_Type_Automatically_Convertible_To_Original {};

} // ends anonymous namespace

/**
* Implements the AsType policy.
*/
template<class ReturnType>
struct AsType {
template <class InputType> struct apply {
// Deduce if type is correct for policy, needs to be convertible to ReturnType
typedef typename boost::mpl::if_c<
boost::is_convertible<InputType, ReturnType>::value, AsTypeImpl<ReturnType, InputType>,
AsType_Requires_New_Type_Automatically_Convertible_To_Original<InputType>>::type type;
};
};

} // ends Policies namespace
}
} // ends Mantid::PythonInterface namespaces

#endif /* MANTID_PYTHONINTERFACE_ASTYPE_H */
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidAPI/ITableWorkspace.h"
#include "MantidAPI/IPeaksWorkspace.h"
#include "MantidPythonInterface/kernel/Policies/AsType.h"

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

using namespace boost::python;
using namespace Mantid::API;
using namespace Mantid::PythonInterface::Policies;

namespace {
/**
Expand Down Expand Up @@ -65,16 +67,16 @@ void export_WorkspaceFactory() {
arg("XLength") = -1, arg("YLength") = -1)))

.def("create", (createFromScratchPtr)&WorkspaceFactoryImpl::create,
createFromScratchDoc,
createFromScratchDoc, return_value_policy<AsType<Workspace_sptr>>(),
(arg("className"), arg("NVectors"), arg("XLength"), arg("YLength")))

.def("createTable", &WorkspaceFactoryImpl::createTable,
createTable_Overload("Creates an empty TableWorkspace",
(arg("className") = "TableWorkspace")))
(arg("className") = "TableWorkspace"))[return_value_policy<AsType<Workspace_sptr>>()])

.def("createPeaks", &WorkspaceFactoryImpl::createPeaks,
createPeaks_Overload("Creates an empty PeaksWorkspace",
(arg("className") = "PeaksWorkspace")))
(arg("className") = "PeaksWorkspace"))[return_value_policy<AsType<Workspace_sptr>>()])

.def("Instance", &WorkspaceFactory::Instance,
return_value_policy<reference_existing_object>(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ ExtractWorkspace::ExtractWorkspace(const boost::python::api::object &pyvalue)
: m_value() {
// Test for a weak pointer first
typedef boost::weak_ptr<Workspace> Workspace_wptr;
extract<Workspace_wptr> extractWeak(pyvalue);
extract<Workspace_wptr&> extractWeak(pyvalue);
if (extractWeak.check()) {
m_value = extractWeak().lock();
}
extract<Workspace_sptr> extractShared(pyvalue);
extract<Workspace_sptr&> extractShared(pyvalue);
if (extractShared.check()) {
m_value = extractShared();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def test_basicRun(self):
wsIndex = 0
inDataY = mtd[self._aWS].readY(wsIndex)
inDataE = mtd[self._aWS].readE(wsIndex)
print type(conjoinedWS)
outDataY1 = conjoinedWS.readY(0)
outDataY2 = conjoinedWS.readY(1)
outDataE1 = conjoinedWS.readE(0)
Expand All @@ -43,4 +44,4 @@ def test_basicRun(self):


if __name__ == '__main__':
unittest.main()
unittest.main()
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@
#include <boost/python/register_ptr_to_python.hpp>
#include <boost/python/return_value_policy.hpp>

#include "MantidAPI/Workspace.h"
#include "MantidDataObjects/PeaksWorkspace.h"
#include "MantidPythonInterface/kernel/Policies/AsType.h"
#include "MantidTestHelpers/MDEventsTestHelper.h" // These are still concerned with workspace creation so attach them here
#include "MantidTestHelpers/WorkspaceCreationHelper.h"

using namespace WorkspaceCreationHelper;
using namespace Mantid::API;
using namespace Mantid::DataObjects;
using namespace Mantid::DataObjects::MDEventsTestHelper;
using namespace Mantid::PythonInterface::Policies;
using namespace WorkspaceCreationHelper;

BOOST_PYTHON_FUNCTION_OVERLOADS(create2DWorkspaceWithFullInstrument_overloads, create2DWorkspaceWithFullInstrument, 2, 4)

Expand All @@ -31,7 +35,7 @@ BOOST_PYTHON_MODULE(WorkspaceCreationHelper)
docstring_options docstrings(true, true, false);

//=================================== 2D workspaces ===================================
using namespace Mantid::API;


// Function pointers to disambiguate the calls
typedef Workspace2D_sptr (*Signature1_2D)(int nHist, int nBins,
Expand All @@ -42,20 +46,24 @@ BOOST_PYTHON_MODULE(WorkspaceCreationHelper)
int numBins);

def("create2DWorkspaceWithFullInstrument", (Signature1_2D)&create2DWorkspaceWithFullInstrument,
create2DWorkspaceWithFullInstrument_overloads());
create2DWorkspaceWithFullInstrument_overloads()[return_value_policy<AsType<Workspace_sptr>>()]);
def("create2DWorkspaceWithRectangularInstrument", (Signature2_2D)&create2DWorkspaceWithRectangularInstrument,
create2DWorkspaceWithRectangularInstrument_overloads());


//=================================== Event Workspaces ===================================

def("CreateEventWorkspace", (EventWorkspace_sptr (*)())CreateEventWorkspace);
def("CreateEventWorkspace2", &CreateEventWorkspace2);
def("CreateEventWorkspace", (EventWorkspace_sptr (*)())CreateEventWorkspace,
return_value_policy<AsType<Workspace_sptr>>());
def("CreateEventWorkspace2", &CreateEventWorkspace2,
return_value_policy<AsType<Workspace_sptr>>());

//=================================== Peak Workspaces ===================================

def("createPeaksWorkspace", (PeaksWorkspace_sptr (*)(const int))createPeaksWorkspace);
def("createPeaksWorkspace", (PeaksWorkspace_sptr (*)(const int, const bool))createPeaksWorkspace);
def("createPeaksWorkspace", (PeaksWorkspace_sptr (*)(const int))createPeaksWorkspace,
return_value_policy<AsType<Workspace_sptr>>());
def("createPeaksWorkspace", (PeaksWorkspace_sptr (*)(const int, const bool))createPeaksWorkspace,
return_value_policy<AsType<Workspace_sptr>>());

//=================================== MD Workspaces ===================================

Expand All @@ -64,5 +72,5 @@ BOOST_PYTHON_MODULE(WorkspaceCreationHelper)
double, std::string name, double);

def("makeFakeMDHistoWorkspace", (Signature1_MDHisto)&makeFakeMDHistoWorkspace,
makeFakeMDHistoWorkspace_overloads());
makeFakeMDHistoWorkspace_overloads()[return_value_policy<AsType<Workspace_sptr>>()]);
}

0 comments on commit 6801884

Please sign in to comment.