24#include <boost/preprocessor/list/for_each.hpp>
25#include <boost/preprocessor/tuple/to_list.hpp>
26#include <boost/python/class.hpp>
27#include <boost/python/converter/builtin_converters.hpp>
28#include <boost/python/dict.hpp>
29#include <boost/python/extract.hpp>
30#include <boost/python/list.hpp>
31#include <boost/python/make_constructor.hpp>
32#include <boost/python/overloads.hpp>
36#define PY_ARRAY_UNIQUE_SYMBOL API_ARRAY_API
37#define NO_IMPORT_ARRAY
38#include <numpy/ndarrayobject.h>
52#define TO_LONG PyLong_AsLong
53#define STR_CHECK PyUnicode_Check
54#if NPY_API_VERSION < 0x0000000a
55#define IS_ARRAY_INTEGER(obj) (PyLong_Check(obj) || PyArray_IsScalar((obj), Integer))
57#define IS_ARRAY_INTEGER PyArray_IsIntegerScalar
61#define BUILTIN_TYPES BOOST_PP_TUPLE_TO_LIST(8, (double, std::string, int, size_t, uint32_t, int64_t, float, uint64_t))
62#define USER_TYPES BOOST_PP_TUPLE_TO_LIST(1, (Mantid::Kernel::V3D))
63#define ARRAY_TYPES BOOST_PP_TUPLE_TO_LIST(2, (std::vector<int>, std::vector<double>))
76 return to_python_value<const bool &>()(res);
79#define GET_BUILTIN(R, _, T) \
80 else if (typeID.hash_code() == typeid(T).hash_code()) { \
81 result = to_python_value<const T &>()(column->cell<T>(row)); \
83#define GET_USER(R, _, T) \
84 else if (typeID.hash_code() == typeid(T).hash_code()) { \
85 const converter::registration *entry = converter::registry::query(typeid(T)); \
87 throw std::invalid_argument("Cannot find converter from C++ type."); \
88 result = entry->to_python((const void *)&column->cell<T>(row)); \
90#define GET_ARRAY(R, _, T) \
91 else if (typeID.hash_code() == typeid(T).hash_code()) { \
92 result = Converters::Clone::apply<T::value_type>::create1D(column->cell<T>(row)); \
97 PyObject *result(
nullptr);
104 throw std::invalid_argument(
"Cannot convert C++ type to Python: " + column->type());
115void setValue(
const Column_sptr &column,
const int row,
const object &
value) {
116 const auto &typeID = column->get_type_info();
127 column->cell<
int>(row) =
static_cast<int>(
TO_LONG(
value.ptr()));
132#define SET_CELL(R, _, T) \
133 else if (typeID.hash_code() == typeid(T).hash_code()) { \
134 column->cell<T>(row) = extract<T>(value)(); \
136#define SET_VECTOR_CELL(R, _, T) \
137 else if (typeID.hash_code() == typeid(T).hash_code()) { \
138 if (!NDArray::check(value)) { \
139 column->cell<T>(row) = Converters::PySequenceToVector<T::value_type>(value)(); \
141 column->cell<T>(row) = Converters::NDArrayToVector<T::value_type>(value)(); \
153 throw std::invalid_argument(
"Cannot convert Python type to C++: " + column->type());
165bool addColumnPlotType(
ITableWorkspace &self,
const std::string &type,
const std::string &
name,
int plottype) {
169 newColumn->setPlotType(plottype);
171 return newColumn !=
nullptr;
184bool addColumnSimple(
ITableWorkspace &self,
const std::string &type,
const std::string &
name) {
198bool addReadOnlyColumn(
ITableWorkspace &self,
const std::string &type,
const std::string &
name) {
200 newColumn->setReadOnly(
true);
214 colptr = self.
getColumn(extract<std::string>(column)());
216 colptr = self.
getColumn(extract<int>(column)());
219 return colptr->getPlotType();
230void setPlotType(
ITableWorkspace &self,
const object &column,
int ptype,
int linkedCol = -1) {
234 colptr = self.
getColumn(extract<std::string>(column)());
236 colptr = self.
getColumn(extract<int>(column)());
238 colptr->setPlotType(ptype);
239 if (linkedCol >= 0) {
240 colptr->setLinkedYCol(linkedCol);
250BOOST_PYTHON_FUNCTION_OVERLOADS(setPlotType_overloads, setPlotType, 3, 4)
264 colptr = self.getColumn(extract<std::string>(column)());
266 colptr = self.getColumn(extract<int>(column)());
269 return colptr->getLinkedYCol();
278void setLinkedYCol(
ITableWorkspace &self,
const object &errColumn,
const int dataColomn) {
282 colptr = self.
getColumn(extract<std::string>(errColumn)());
284 colptr = self.
getColumn(extract<int>(errColumn)());
287 colptr->setLinkedYCol(dataColomn);
304 const std::type_info &typeID = column->get_type_info();
305 const auto numRows =
static_cast<int>(column->size());
307 PyObject *result = PyList_New(numRows);
308 for (
int i = 0; i < numRows; i++) {
309 if (PyList_SetItem(result, i, getValue(column, typeID, i)))
310 throw std::runtime_error(
"Error while building list");
323 for (
size_t i = 0; i < column->size(); ++i) {
324 maxlen = std::max(maxlen, column->cell<std::string>(i).size());
328 PyObject *
dtype = Py_BuildValue(
"s", dtype_str.c_str());
330 if (!PyArray_DescrConverter(dtype, &descr)) {
332 throw std::runtime_error(
"Failed to create Unicode dtype");
335 npy_intp arrayDims[1] = {
static_cast<npy_intp
>(column->size())};
336 PyObject *nparray = PyArray_NewFromDescr(&PyArray_Type, descr, 1, arrayDims,
nullptr,
nullptr, 0,
nullptr);
338 for (
size_t i = 0; i < column->size(); ++i) {
340 PyObject *pystr = PyUnicode_FromString(column->cell<std::string>(i).c_str());
341 PyArray_SETITEM(dest,
static_cast<char *
>(PyArray_GETPTR1(dest, i)), pystr);
348 const std::type_info &typeID = column->get_type_info();
352 if (typeID.hash_code() ==
typeid(std::vector<int>).hash_code()) {
353 return column->cell<std::vector<int>>(0).size();
355 if (typeID.hash_code() ==
typeid(std::vector<double>).hash_code()) {
356 return column->cell<std::vector<double>>(0).size();
362 auto dest_p =
static_cast<T *
>(dest);
363 for (
size_t i = 0; i < column->size(); ++i) {
364 dest_p[i] = column->cell<U>(i);
369 auto dest_p =
static_cast<T *
>(dest);
370 size_t stride = getStride(column);
371 for (
size_t i = 0; i < column->size(); ++i) {
372 const auto src =
static_cast<std::vector<T>
>(column->cell<U>(i));
373 if (src.size() != stride) {
374 throw std::runtime_error(
375 "All rows in a vector column must have the same length to be converted into a numpy array");
377 std::copy(src.begin(), src.end(), dest_p + i * stride);
384 {
typeid(int).hash_code(), {NPY_INT, copyTo1DArray<int, int>}},
385 {
typeid(
Mantid::API::Boolean).hash_code(), {NPY_BOOL, copyTo1DArray<bool, Mantid::API::Boolean>}},
386 {
typeid(int32_t).hash_code(), {NPY_INT32, copyTo1DArray<int32_t, int32_t>}},
387 {
typeid(uint32_t).hash_code(), {NPY_UINT32, copyTo1DArray<uint32_t, uint32_t>}},
388 {
typeid(int64_t).hash_code(), {NPY_INT64, copyTo1DArray<int64_t, int64_t>}},
389 {
typeid(uint64_t).hash_code(), {NPY_UINT64, copyTo1DArray<uint64_t, uint64_t>}},
390 {
typeid(double).hash_code(), {NPY_DOUBLE, copyTo1DArray<double, double>}},
391 {
typeid(float).hash_code(), {NPY_FLOAT, copyTo1DArray<float, float>}},
392 {
typeid(size_t).hash_code(),
393 {(
sizeof(size_t) == 4) ? NPY_UINT32 : NPY_UINT64,
394 (
sizeof(size_t) == 4) ? copyTo1DArray<uint32_t, uint32_t> : copyTo1DArray<uint64_t, uint64_t>}},
395 {
typeid(std::vector<int>).hash_code(), {NPY_INT, copyTo2DArray<int, std::vector<int>>}},
396 {
typeid(std::vector<double>).hash_code(), {NPY_DOUBLE, copyTo2DArray<double, std::vector<double>>}},
397 {
typeid(
Mantid::Kernel::V3D).hash_code(), {NPY_DOUBLE, copyTo2DArray<double, Mantid::Kernel::V3D>}}};
412 const std::type_info &typeID = column->get_type_info();
413 if (typeID.hash_code() ==
typeid(std::string).hash_code()) {
414 return stringNumpyArray(column);
417 auto it = array_handlers.find(typeID.hash_code());
418 if (it == array_handlers.end()) {
419 throw std::invalid_argument(std::string(
"Column type not yet supported for array: ") + std::string(typeID.name()));
421 const size_t numRows = self.
rowCount();
422 const size_t stride = getStride(column);
423 const bool isVectorLike = (stride != 1);
424 npy_intp arrayDims[2] = {
static_cast<npy_intp
>(numRows),
static_cast<npy_intp
>(isVectorLike ? stride : 0)};
425 const int nDims = isVectorLike ? 2 : 1;
426 auto &[npy_type, copy_func] = it->second;
427 PyObject *nparray = PyArray_NewFromDescr(&PyArray_Type, PyArray_DescrFromType(npy_type), nDims, arrayDims,
nullptr,
428 nullptr, 0,
nullptr);
430 throw std::runtime_error(
"Failed to allocate NumPy array");
432 void *dest = PyArray_DATA(
reinterpret_cast<PyArrayObject *
>(nparray));
433 copy_func(dest, std::move(column));
445 throw std::invalid_argument(
"Cannot specify negative row number");
446 if (row >=
static_cast<int>(self.
rowCount()))
447 throw std::invalid_argument(
"Cannot specify row larger than number of rows");
449 auto numCols =
static_cast<int>(self.
columnCount());
451 PyObject *result = PyDict_New();
453 for (
int columnIndex = 0; columnIndex < numCols; columnIndex++) {
455 const std::type_info &typeID = col->get_type_info();
457 if (PyDict_SetItemString(result, col->name().c_str(), getValue(col, typeID, row)))
458 throw std::runtime_error(
"Error while building dict");
470 auto numCols =
static_cast<int>(self.
columnCount());
472 boost::python::list types;
474 for (
int colIndex = 0; colIndex < numCols; colIndex++) {
475 const auto col = self.
getColumn(colIndex);
476 const auto &type = col->type();
493 auto nitems = boost::python::len(rowItems);
494 if (nitems !=
static_cast<decltype(nitems)
>(self.
columnCount())) {
495 throw std::invalid_argument(
"Number of values given does not match the number of columns. "
501 const auto rowIndex =
static_cast<int>(self.
rowCount());
511 for (
auto &iter : columns) {
513 value = rowItems[iter];
514 setValue(col, rowIndex,
value);
516 }
catch (error_already_set &) {
518 if (PyErr_ExceptionMatches(PyExc_KeyError)) {
519 std::ostringstream msg;
520 msg <<
"Missing key-value entry for column ";
521 msg <<
"<" << col->name() <<
">";
522 PyErr_SetString(PyExc_KeyError, msg.str().c_str());
526 if (PyErr_ExceptionMatches(PyExc_TypeError)) {
527 std::ostringstream msg;
528 msg <<
"Wrong datatype for column <" << col->name() <<
"> ";
529 msg <<
"(expected <" << col->type() <<
">)";
530 PyErr_SetString(PyExc_TypeError, msg.str().c_str());
548void addRowFromSequence(
ITableWorkspace &self,
const object &rowItems) {
550 auto nitems = boost::python::len(rowItems);
551 if (nitems !=
static_cast<decltype(nitems)
>(self.
columnCount())) {
552 throw std::invalid_argument(
"Number of values given does not match the number of columns. "
558 const auto rowIndex =
static_cast<int>(self.
rowCount());
562 for (
decltype(nitems) i = 0; i < nitems; ++i) {
564 auto value = rowItems[i];
567 setValue(col, rowIndex,
value);
568 }
catch (error_already_set &) {
570 if (PyErr_ExceptionMatches(PyExc_TypeError)) {
571 std::ostringstream msg;
572 msg <<
"Wrong datatype for column <" << col->name() <<
"> ";
573 msg <<
"(expected <" << col->type() <<
">)";
574 PyErr_SetString(PyExc_TypeError, msg.str().c_str());
597 column = self.
getColumn(extract<std::string>(col_or_row)());
598 rowIndex = row_or_col;
600 rowIndex = extract<int>(col_or_row)();
617 getCellLoc(self,
value, row_or_col, col, rowIndex);
618 const std::type_info &typeID = col->get_type_info();
619 return getValue(col, typeID, rowIndex);
630void setCell(
ITableWorkspace &self,
const object &col_or_row,
const int row_or_col,
const object &
value,
631 const bool ¬ify_replace) {
634 getCellLoc(self, col_or_row, row_or_col, col, rowIndex);
635 setValue(col, rowIndex,
value);
637 if (notify_replace) {
652 colptr = self.
getColumn(extract<std::string>(column)());
654 colptr = self.
getColumn(extract<int>(column)());
656 return colptr->getReadOnly();
665void setColumnReadOnly(
ITableWorkspace &self,
const object &column,
const bool readOnly) {
669 colptr = self.
getColumn(extract<std::string>(column)());
671 colptr = self.
getColumn(extract<int>(column)());
673 colptr->setReadOnly(readOnly);
690 handle<> handle(column(self,
object(
name)));
691 object values(handle);
692 result[
name] = values;
702 data[
"data"] =
toDict(ws);
703 data[
"meta_data"] = writeMetaData(ws);
708 readMetaData(ws, state);
722 for (
const auto &
name : names) {
724 columnNames.append(
name);
725 columnTypes.append(column->type());
729 metaData[
"column_names"] = columnNames;
730 metaData[
"column_types"] = columnTypes;
743 const auto &metaData = state[
"meta_data"];
744 const auto &columnNames = metaData[
"column_names"];
745 const auto &columnTypes = metaData[
"column_types"];
747 auto numColumns = len(columnNames);
748 for (
decltype(numColumns) colIndex = 0; colIndex < numColumns; ++colIndex) {
749 const auto &key = columnNames[colIndex];
750 const auto &
value = columnTypes[colIndex];
751 const auto &
name = extract<std::string>(key);
752 const auto &type = extract<std::string>(
value);
763 const auto &data = state[
"data"];
770 auto numRows = len(data[names[0]]);
771 for (
int rowIndex = 0; rowIndex < numRows; ++rowIndex) {
773 for (
const auto &
name : names) {
783 std::string iTableWorkspace_docstring =
"Most of the information from a table workspace is returned ";
784 iTableWorkspace_docstring +=
"as native copies. All of the column accessors return lists while the ";
785 iTableWorkspace_docstring +=
"rows return dicts. This object does support the idom 'for row in ";
786 iTableWorkspace_docstring +=
"ITableWorkspace'.";
788 class_<ITableWorkspace, bases<Workspace>, boost::noncopyable>(
"ITableWorkspace", iTableWorkspace_docstring.c_str(),
791 .def(
"addColumn", &addColumnSimple, (arg(
"self"), arg(
"type"), arg(
"name")),
792 "Add a named column with the given type. Recognized types are: "
793 "int,float,double,bool,str,V3D,long64")
795 .def(
"addColumn", &addColumnPlotType, (arg(
"self"), arg(
"type"), arg(
"name"), arg(
"plottype")),
796 "Add a named column with the given datatype "
797 "(int,float,double,bool,str,V3D,long64) "
799 "(0 = None, 1 = X, 2 = Y, 3 = Z, 4 = xErr, 5 = yErr, 6 = Label).")
801 .def(
"addReadOnlyColumn", &addReadOnlyColumn, (arg(
"self"), arg(
"type"), arg(
"name")),
802 "Add a read-only, named column with the given type. Recognized types are: "
803 "int,float,double,bool,str,V3D,long64")
805 .def(
"getPlotType", &getPlotType, (arg(
"self"), arg(
"column")),
806 "Get the plot type of given column as an integer. "
807 "Accepts column name or index. \nPossible return values: "
808 "(0 = None, 1 = X, 2 = Y, 3 = Z, 4 = xErr, 5 = yErr, 6 = Label).")
810 .def(
"setPlotType", setPlotType,
811 setPlotType_overloads((arg(
"self"), arg(
"column"), arg(
"ptype"), arg(
"linkedCol") = -1),
812 "Set the plot type of given column. "
813 "Accepts column name or index. \nPossible type values: "
814 "(0 = None, 1 = X, 2 = Y, 3 = Z, 4 = xErr, 5 = yErr, 6 = "
817 .def(
"getLinkedYCol", &getLinkedYCol, (arg(
"self"), arg(
"column")),
818 "Get the data column associated with a given error column. ")
820 .def(
"setLinkedYCol", &setLinkedYCol, (arg(
"self"), arg(
"errColumn"), arg(
"dataColumn")),
821 "Set the data column associated with a given error column. ")
830 "Resize the table to contain count rows.")
835 boost::python::return_value_policy<VectorToNumpy>(),
"Return a list of the column names.")
838 "Return a list of the column names.")
840 .def(
"column", &column, (arg(
"self"), arg(
"column")),
"Return all values of a specific column as a list.")
842 .def(
"columnArray", &columnArray, (arg(
"self"), arg(
"column")),
843 "Return all values of a specific column (either index or name) as a numpy array.")
845 .def(
"row", &row, (arg(
"self"), arg(
"row")),
"Return all values of a specific row as a dict.")
847 .def(
"columnTypes", &columnTypes, arg(
"self"),
"Return the types of the columns as a list")
851 .def(
"addRow", &addRowFromSequence, (arg(
"self"), arg(
"row_items_seq")),
852 "Appends a row with the values from the given sequence. "
853 "It it assumed that the items are in the correct order for the "
856 .def(
"addRow", &addRowFromDict, (arg(
"self"), arg(
"row_items_dict")),
857 "Appends a row with the values from the dictionary.")
859 .def(
"cell", &cell, (arg(
"self"), arg(
"value"), arg(
"row_or_column")),
860 "Return the value in the given cell. If the value argument is a "
861 "number then it is interpreted as a row otherwise it "
862 "is interpreted as a column name.")
864 .def(
"setCell", &setCell,
865 (arg(
"self"), arg(
"row_or_column"), arg(
"column_or_row"), arg(
"value"), arg(
"notify_replace") =
true),
866 "Sets the value of a given cell. If the row_or_column argument is a "
867 "number then it is interpreted as a row otherwise it "
868 "is interpreted as a column name. If notify replace is false, then "
869 "the replace workspace event is not triggered.")
871 .def(
"toDict", &
toDict, (arg(
"self")),
872 "Gets the values of this workspace as a dictionary. The keys of the "
873 "dictionary will be the names of the columns of the table. The "
874 "values of the entries will be lists of values for each column.")
876 .def(
"setColumnReadOnly", &setColumnReadOnly, (arg(
"self"), arg(
"column"), arg(
"read_only")),
877 "Sets whether or not a given column of this workspace should be read-only. Columns can be "
878 "selected by name or by index")
880 .def(
"isColumnReadOnly", &isColumnReadOnly, (arg(
"self"), arg(
"column")),
881 "Gets whether or not a given column of this workspace is be read-only. Columns can be "
882 "selected by name or by index");
double value
The value of the point.
#define GET_POINTER_SPECIALIZATION(TYPE)
tagPyArrayObject PyArrayObject
_PyArray_Descr PyArray_Descr
void export_ITableWorkspace()
#define SET_VECTOR_CELL(R, _, T)
#define GET_BUILTIN(R, _, T)
#define GET_ARRAY(R, _, T)
dict toDict(const ITableWorkspace &self)
Get the contents of the workspace as a python dictionary.
#define GET_USER(R, _, T)
#define SET_CELL(R, _, T)
#define BUILTIN_TYPES
Boost macro for "looping" over builtin types.
#define IS_ARRAY_INTEGER(obj)
#define GNU_DIAG_OFF(x)
This is a collection of macros for turning compiler warnings off in a controlled manner.
static void readMetaData(ITableWorkspace &ws, const dict &state)
Read the meta data from a python dict into the table workspace.
static void setstate(ITableWorkspace &ws, const dict &state)
static dict writeMetaData(const ITableWorkspace &ws)
Write the meta data from a table workspace to a python dict.
static dict getstate(const ITableWorkspace &ws)
static void readData(ITableWorkspace &ws, const dict &state)
Read the data from a python dict into the table workspace.
ITableWorkspace is an implementation of Workspace in which the data are organised in columns of same ...
virtual void removeColumn(const std::string &name)=0
Removes a column.
void modified()
If the workspace is the AnalysisDataService sends AfterReplaceNotification.
virtual void setRowCount(size_t count)=0
Resizes the workspace.
virtual Column_sptr getColumn(const std::string &name)=0
Gets the shared pointer to a column by name.
TableRowHelper appendRow()
Appends a row.
virtual void removeRow(size_t index)=0
Delets a row if it exists.
virtual Column_sptr addColumn(const std::string &type, const std::string &name)=0
Creates a new column.
virtual size_t columnCount() const =0
Number of columns in the workspace.
virtual std::vector< std::string > getColumnNames() const =0
Returns a vector of all column names.
virtual size_t rowCount() const =0
Number of rows in the workspace.
Thin object wrapper around a numpy array.
std::shared_ptr< Column > Column_sptr
std::shared_ptr< const Column > Column_const_sptr
std::string dtype(const Container< HeldType > &)
std::string to_string(const wide_integer< Bits, Signed > &n)
As TableColumn stores its data in a std::vector bool type cannot be used in the same way as the other...
Implements a return value policy that returns a numpy array from a function returning a std::vector b...
Encapsulates the registration required for an interface type T that sits on top of a Kernel::DataItem...