Skip to content

Commit

Permalink
Refs #4399. Add other declareProperty overloads.
Browse files Browse the repository at this point in the history
  • Loading branch information
martyngigg committed Mar 1, 2012
1 parent 5c04f6d commit 08f0edb
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 54 deletions.
3 changes: 2 additions & 1 deletion Code/Mantid/Framework/PythonInterface/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ set ( TEST_PY_FILES
test/python/OrientedLatticeTest.py
test/python/PythonPluginsTest.py
test/python/PropertyWithValueTest.py
test/python/PythonAlgorithmTest.py
test/python/PythonAlgorithmPropertiesTest.py
test/python/PythonAlgorithmTraitsTest.py
test/python/ReferenceFrameTest.py
test/python/RunTest.py
test/python/SimpleAPITest.py
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@ namespace Mantid
class DLLExport PropertyWithValueFactory
{
public:
/// Creates a property from the value, validator and direction
static Kernel::Property * create(const std::string & name, const boost::python::object & defaultValue,
const boost::python::object & validator, const unsigned int direction);
/// Creates a property from the given value and direction
static Kernel::Property * create(const std::string & name, const boost::python::object & defaultValue,
const unsigned int direction);
/// Creates a property from the value, validator and direction
static Kernel::Property * create(const std::string & name, const boost::python::object & defaultValue,
const boost::python::object & validator, const unsigned int direction);
private:
/// Return a handler that maps the python type to a C++ type
static Registry::PropertyValueHandler *lookup(PyTypeObject * const pythonType);
};
}
}

#endif //MANTID_PYTHONINTERFACE_PROEPRTYWITHVALUEFACTORY_H_
#endif //MANTID_PYTHONINTERFACE_PROEPRTYWITHVALUEFACTORY_H_
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ namespace Mantid
class PythonAlgorithm : public API::Algorithm
{
public:
/// Declare a property using the type of the defaultValue with a validator and doc string
void declareProperty(const std::string & name, const boost::python::object & defaultValue,
const boost::python::object & validator = boost::python::object(),
const std::string & doc = "", const int direction = Kernel::Direction::Input);

/// Declare a property with a documentation string
void declareProperty(const std::string & name, const boost::python::object & defaultValue,
const std::string & doc, const int direction = Kernel::Direction::Input);

/// Declare a property using the type of the defaultValue
void declareProperty(const std::string & name, const boost::python::object & defaultValue,
const int direction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ namespace Mantid
virtual void set(Kernel::IPropertyManager* alg, const std::string &name, const boost::python::object & value) = 0;
/// Overload to create a Property type from the given value with no validation
virtual Kernel::Property * create(const std::string & name, const boost::python::object & value,
const unsigned int direction) const = 0;
const boost::python::object & validator, const unsigned int direction) const = 0;
/// Is the given object a derived type of this objects Type
virtual bool checkExtract(const boost::python::object & value) const = 0;
/// Return the Python type corresponding to this object. May return NULL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,26 @@ namespace Mantid
* @param name :: The name of the property
* @param defaultValue :: The defaultValue of the property. The object attempts to extract
* a value of type ValueType from the python object
* @param validator :: A python object pointing to a validator instance, which can be None.
* @param direction :: The direction of the property
* @returns A pointer to a newly constructed property instance
*/
Kernel::Property * create(const std::string & name, const boost::python::object & defaultValue,
const unsigned int direction) const
const boost::python::object & validator, const unsigned int direction) const
{
using boost::python::extract;
const ValueType valueInC = extract<ValueType>(defaultValue)();
return new Kernel::PropertyWithValue<ValueType>(name, valueInC, direction);
Kernel::Property *valueProp(NULL);
if( validator.is_none() )
{
valueProp = new Kernel::PropertyWithValue<ValueType>(name, valueInC, direction);
}
else
{
const Kernel::IValidator<ValueType> * propValidator = extract<Kernel::IValidator<ValueType> *>(validator);
valueProp = new Kernel::PropertyWithValue<ValueType>(name, valueInC, propValidator->clone(), direction);
}
return valueProp;
}
/// Is the given object a derived type of this objects Type
bool checkExtract(const boost::python::object & value) const
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,46 @@
#include <boost/python/register_ptr_to_python.hpp>
#include <boost/python/bases.hpp>
#include <boost/python/args.hpp>
#include <boost/python/overloads.hpp>

using Mantid::API::Algorithm;
using Mantid::PythonInterface::AlgorithmWrapper;
using namespace boost::python;

namespace
{
// declareProperty(name, defaultValue, validator, doc, direction)
typedef void(AlgorithmWrapper::*declarePropertyType1)(const std::string &, const boost::python::object &,
const boost::python::object &, const std::string &, const int);
// declareProperty(name, defaultValue, doc, direction)
typedef void(AlgorithmWrapper::*declarePropertyType2)(const std::string &, const boost::python::object &,
const std::string &, const int);
// declareProperty(name, defaultValue, direction)
typedef void(AlgorithmWrapper::*declarePropertyType3)(const std::string &, const boost::python::object &, const int);

// Overload types
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(declarePropertyType1_Overload, declareProperty, 2, 5);
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(declarePropertyType2_Overload, declareProperty, 3, 4);
}

void export_leaf_classes()
{
// Function pointer to pick out correct declareProperty
typedef void(AlgorithmWrapper::*declarePropertyOverload1)(const std::string &, const boost::python::object &, const int);

/**
* Export the algorithm wrapper that boost.python makes look like a PythonAlgorithm
*/
// Export the algorithm wrapper that boost.python makes look like a PythonAlgorithm
class_<AlgorithmWrapper, bases<Algorithm>, boost::noncopyable>("PythonAlgorithm", "Base class for all Python algorithms")
.def("declareProperty", (declarePropertyOverload1)&AlgorithmWrapper::declareProperty, args("name", "defaultValue", "direction"),
"Declares a named property where the type is taken from the type of the defaultValue and mapped to an appropriate C++ type")
;
.def("declareProperty", (declarePropertyType1)&AlgorithmWrapper::declareProperty,
declarePropertyType1_Overload(args("name", "defaultValue", "validator=None","doc=''","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", (declarePropertyType2)&AlgorithmWrapper::declareProperty,
declarePropertyType2_Overload(args("name", "defaultValue", "doc","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)&AlgorithmWrapper::declareProperty,
args("name", "defaultValue", "direction"),
"Declares a named property where the type is taken from the type of "
"the defaultValue and mapped to an appropriate C++ type")
;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ namespace Mantid
void initTypeLookup(PyTypeIndex & index)
{
assert(index.empty());
index.insert(std::make_pair(&PyInt_Type, new Registry::SingleValueTypeHandler<long>()));

#define REGISTER_MAPPING(PyType, CppType)\
index.insert(std::make_pair(&PyType, new Registry::SingleValueTypeHandler<CppType>()));

// Map the Python types to the best match in C++
REGISTER_MAPPING(PyFloat_Type, double);
REGISTER_MAPPING(PyInt_Type, long);
REGISTER_MAPPING(PyString_Type, std::string);
}

/**
Expand All @@ -39,33 +46,35 @@ namespace Mantid
* The python type is mapped to a C type using the mapping defined by initPythonTypeMap()
* @param name :: The name of the property
* @param defaultValue :: A default value for this property.
* @param validator :: A validator object
* @param direction :: Specifies whether the property is Input, InOut or Output
* @returns A pointer to a new Property object
*/
Kernel::Property *
PropertyWithValueFactory::create(const std::string & name , const boost::python::object & value,
const unsigned int direction)
const boost::python::object & validator, const unsigned int direction)
{
Registry::PropertyValueHandler *propHandle = lookup(value.ptr()->ob_type);
return propHandle->create(name, value, direction);
return propHandle->create(name, value, validator, direction);
}

/**
* Creates a PropertyWithValue<Type> instance from the given information.
* The python type is mapped to a C type using the mapping defined by initPythonTypeMap()
* @param name :: The name of the property
* @param defaultValue :: A default value for this property.
* @param validator :: A validator object
* @param direction :: Specifies whether the property is Input, InOut or Output
* @returns A pointer to a new Property object
*/
Kernel::Property *
PropertyWithValueFactory::create(const std::string & name , const boost::python::object & value,
const boost::python::object & validator, const unsigned int direction)
const unsigned int direction)
{
throw std::runtime_error("Not implemented");
boost::python::object validator; // Default construction gives None object
return create(name, value, validator, direction);
}


//-------------------------------------------------------------------------
// Private methods
//-------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,33 @@ namespace Mantid
{
namespace PythonInterface
{
/**
* Declare a property using the type of the defaultValue, a documentation string and validator
* @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
*/
void PythonAlgorithm::declareProperty(const std::string & name, const boost::python::object & defaultValue,
const boost::python::object & validator,
const std::string & doc, const int direction)
{
this->declareProperty(PropertyWithValueFactory::create(name, defaultValue, validator, direction), doc);
}

/**
* Declare a property using the type of the defaultValue and a documentation string
* @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
*/
void PythonAlgorithm::declareProperty(const std::string & name, const boost::python::object & defaultValue,
const std::string & doc, const int direction)
{
this->declareProperty(PropertyWithValueFactory::create(name, defaultValue, direction), doc);
}

/**
* Declare a property using the type of the defaultValue
Expand All @@ -16,10 +43,9 @@ namespace Mantid
* @param direction :: The direction of the property
*/
void PythonAlgorithm::declareProperty(const std::string & name, const boost::python::object & defaultValue,
const int direction)
const int direction)
{
using namespace boost::python;
this->declareProperty(PropertyWithValueFactory::create(name, defaultValue, direction));
declareProperty(name, defaultValue, "", direction);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ namespace Mantid
void registerBuiltins()
{
// Register a handler for each basic type in IPropertyManager.cpp
REGISTER_SINGLEVALUE_HANDLER(int16_t);
REGISTER_SINGLEVALUE_HANDLER(uint16_t);
REGISTER_SINGLEVALUE_HANDLER(int32_t);
REGISTER_SINGLEVALUE_HANDLER(uint32_t);
REGISTER_SINGLEVALUE_HANDLER(int64_t);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,67 @@
#endif

#include "MantidKernel/PropertyWithValue.h"
#include <boost/python/extract.hpp>

//-------------------------------------------------------------------------

using Mantid::PythonInterface::PropertyWithValueFactory;
using Mantid::Kernel::PropertyWithValue;
using Mantid::Kernel::Direction;

class PropertyWithValueFactoryTest: public CxxTest::TestSuite
{
public:

void test_builtin_type_creates_property_without_error()
#define CREATE_PROPERTY_TEST_BODY(CType, PythonCall) \
\
{\
using namespace boost::python;\
using namespace Mantid::Kernel;\
object pyvalue = object(handle<>(PythonCall));\
boost::shared_ptr<PropertyWithValue<CType> > valueProp = createAndCheckPropertyTraits<CType>("TestProperty", pyvalue, Direction::Input);\
checkPropertyValue<CType>(valueProp, pyvalue);\
}

void test_builtin_type_creates_int_type_property_without_error()
{
CREATE_PROPERTY_TEST_BODY(long, PyInt_FromLong(10));
}

void test_builtin_type_creates_double_type_property_without_error()
{
CREATE_PROPERTY_TEST_BODY(double, PyFloat_FromDouble(50.123));
}

void test_builtin_type_creates_string_type_property_without_error()
{
CREATE_PROPERTY_TEST_BODY(std::string, PyString_FromString("unit"));
}

private:
template<typename ExpectedType>
boost::shared_ptr<PropertyWithValue<ExpectedType> >
createAndCheckPropertyTraits(const std::string & name, const boost::python::object & value, const unsigned int direction)
{
using Mantid::Kernel::Property;
Property *namedProp(NULL);
TS_ASSERT_THROWS_NOTHING(namedProp = PropertyWithValueFactory::create(name, value, direction));
TS_ASSERT(namedProp);
// Is it correctly typed
PropertyWithValue<ExpectedType> *typedProp = dynamic_cast<PropertyWithValue<ExpectedType> *>(namedProp);
TS_ASSERT(typedProp);

// Traits
TS_ASSERT_EQUALS(namedProp->name(), name);
TS_ASSERT_EQUALS(namedProp->direction(), direction);
return boost::shared_ptr<PropertyWithValue<ExpectedType> >(typedProp);
}

template<typename ValueType>
void checkPropertyValue(boost::shared_ptr<PropertyWithValue<ValueType> >valueProp, const boost::python::object & expectedValue)
{
using namespace boost::python;
using namespace Mantid::Kernel;
object pyint = object(handle<>(PyInt_FromLong(10)));
Property *p(NULL);
TS_ASSERT_THROWS_NOTHING(p = PropertyWithValueFactory::create("NewProperty", pyint, Direction::Input));
TS_ASSERT(p);
// Is it typed
PropertyWithValue<long> *typedProp = dynamic_cast<PropertyWithValue<long> *>(p);
TS_ASSERT(typedProp);
const ValueType srcValue = boost::python::extract<ValueType>(expectedValue);
const ValueType propValue = (*valueProp)();
TS_ASSERT_EQUALS(srcValue, propValue);
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import unittest

from mantid import PythonAlgorithm, Direction

class BasicPropsAlg(PythonAlgorithm):

def PyInit(self):
self.declareProperty('SimpleInput', 1)
self.declareProperty('SimpleOutput', 1.0, Direction.Output)

def PyExec(self):
pass

# ======================================================================

class PythonAlgorithmPropertiesTest(unittest.TestCase):

def test_simple_property_declarations_have_correct_attrs(self):
alg = BasicPropsAlg() #AlgorithmManager.Instance().createUnmanaged("TestPyAlgDeclaringProps")
props = alg.getProperties()
self.assertEquals(0, len(props))
alg.initialize()
props = alg.getProperties()
self.assertEquals(2, len(props))

input = alg.getProperty("SimpleInput")
self.assertEquals(input.direction, Direction.Input)
output = alg.getProperty("SimpleOutput")
self.assertEquals(output.direction, Direction.Output)

0 comments on commit 08f0edb

Please sign in to comment.