Skip to content

Commit

Permalink
Python: add binding for missing ParamValue constructors (#2417)
Browse files Browse the repository at this point in the history
We previously exposed the PV constructors from just a plain int, float,
or string, but there wasn't an easy way to construct from arbitrary data
like there is in C++.
  • Loading branch information
lgritz committed Dec 3, 2019
1 parent 4798059 commit 64bad0c
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 21 deletions.
17 changes: 7 additions & 10 deletions src/python/py_oiio.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,9 @@ C_to_tuple<TypeDesc>(cspan<TypeDesc> vals)
// Python tuple.
template<typename T>
inline py::object
C_to_val_or_tuple(const T* vals, TypeDesc type)
C_to_val_or_tuple(const T* vals, TypeDesc type, int nvalues = 1)
{
size_t n = type.numelements() * type.aggregate;
size_t n = type.numelements() * type.aggregate * nvalues;
if (n == 1 && !type.arraylen)
return typename PyTypeForCType<T>::type(vals[0]);
else
Expand Down Expand Up @@ -464,18 +464,15 @@ make_numpy_array(TypeDesc format, void* data, int dims, size_t chans,


inline py::object
ParamValue_getitem(const ParamValue& self, int n = 0)
ParamValue_getitem(const ParamValue& self, bool allitems = false)
{
if (n < 0 || n >= self.nvalues()) {
throw std::out_of_range(
Strutil::sprintf("ParamValue index out of range %d", n));
}

TypeDesc t = self.type();
int nvals = allitems ? self.nvalues() : 1;

#define ParamValue_convert_dispatch(TYPE) \
case TypeDesc::TYPE: \
return C_to_val_or_tuple((CType<TypeDesc::TYPE>::type*)self.data(), t)
return C_to_val_or_tuple((CType<TypeDesc::TYPE>::type*)self.data(), t, \
nvals)

switch (t.basetype) {
// ParamValue_convert_dispatch(UCHAR);
Expand All @@ -492,7 +489,7 @@ case TypeDesc::TYPE: \
ParamValue_convert_dispatch(FLOAT);
ParamValue_convert_dispatch(DOUBLE);
case TypeDesc::STRING:
return C_to_val_or_tuple((const char**)self.data(), t);
return C_to_val_or_tuple((const char**)self.data(), t, nvals);
default: return py::none();
}

Expand Down
50 changes: 48 additions & 2 deletions src/python/py_paramvalue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,41 @@
namespace PyOpenImageIO {


static ParamValue
ParamValue_from_tuple(string_view name, TypeDesc type, int nvalues,
ParamValue::Interp interp, const py::tuple& obj)
{
ParamValue pv;
if (type.basetype == TypeDesc::INT) {
std::vector<int> vals;
py_to_stdvector(vals, obj);
if (vals.size() == type.numelements() * type.aggregate * nvalues)
pv.init(name, type, nvalues, interp, &vals[0]);
} else if (type.basetype == TypeDesc::UINT) {
std::vector<unsigned int> vals;
py_to_stdvector(vals, obj);
if (vals.size() == type.numelements() * type.aggregate * nvalues)
pv.init(name, type, nvalues, interp, &vals[0]);
} else if (type.basetype == TypeDesc::FLOAT) {
std::vector<float> vals;
py_to_stdvector(vals, obj);
if (vals.size() == type.numelements() * type.aggregate * nvalues)
pv.init(name, type, nvalues, interp, &vals[0]);
} else if (type.basetype == TypeDesc::STRING) {
std::vector<std::string> vals;
py_to_stdvector(vals, obj);
if (vals.size() == type.numelements() * type.aggregate * nvalues) {
std::vector<ustring> u;
for (auto& val : vals)
u.emplace_back(val);
pv.init(name, type, nvalues, interp, &u[0]);
}
}
return pv;
}



void
declare_paramvalue(py::module& m)
{
Expand All @@ -29,13 +64,24 @@ declare_paramvalue(py::module& m)
})
.def_property_readonly("value",
[](const ParamValue& p) {
return ParamValue_getitem(p, 0);
return ParamValue_getitem(p, true);
})
// .def("__getitem__", &ParamValue_getitem)
.def_property_readonly("__len__", &ParamValue::nvalues)
.def(py::init<const std::string&, int>())
.def(py::init<const std::string&, float>())
.def(py::init<const std::string&, const std::string&>());
.def(py::init<const std::string&, const std::string&>())
.def(py::init([](const std::string& name, TypeDesc type,
const py::tuple& obj) {
return ParamValue_from_tuple(name, type, 1,
ParamValue::INTERP_CONSTANT, obj);
}),
"name"_a, "type"_a, "value"_a)
.def(py::init([](const std::string& name, TypeDesc type, int nvalues,
ParamValue::Interp interp, const py::tuple& obj) {
return ParamValue_from_tuple(name, type, nvalues, interp, obj);
}),
"name"_a, "type"_a, "nvalues"_a, "interp"_a, "value"_a);

py::class_<ParamValueList>(m, "ParamValueList")
.def(py::init<>())
Expand Down
8 changes: 8 additions & 0 deletions testsuite/python-paramlist/ref/out.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
Testing individual ParamValue:
item a int 42
item b float 3.5
item c string xyzpdq
item d float[4] (3.5, 4.5, 5.5, 6.5)
item e float (1.0, 3.0, 5.0, 7.0)

Testing ParamValueList:
pl length is 6
item i int 1
item s string Bob
Expand Down
31 changes: 22 additions & 9 deletions testsuite/python-paramlist/src/test_paramlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,36 @@
import OpenImageIO as oiio


def print_param_value(p) :
if type(p.value) == float :
print (" item {} {} {:.6}".format(p.name, p.type, p.value))
else :
print (" item {} {} {}".format(p.name, p.type, p.value))

def print_param_list(pl) :
for p in pl :
if type(p.value) == float :
print (" item {} {} {:.6}".format(p.name, p.type, p.value))
else :
print (" item {} {} {}".format(p.name, p.type, p.value))
print_param_value(p)



######################################################################
# main test starts here

try:
print ("Testing individual ParamValue:")
pv = oiio.ParamValue("a", 42)
print_param_value(pv)
pv = oiio.ParamValue("b", 3.5)
print_param_value(pv)
pv = oiio.ParamValue("c", "xyzpdq")
print_param_value(pv)
pv = oiio.ParamValue("d", "float[4]", (3.5, 4.5, 5.5, 6.5))
print_param_value(pv)
pv = oiio.ParamValue("e", "float", 4, oiio.Interp.INTERP_LINEAR, (1, 3, 5, 7))
print_param_value(pv)
print ("")

print ("Testing ParamValueList:")
pl = oiio.ParamValueList()
pl.attribute ("i", 1)
pl.attribute ("s", "Bob")
Expand All @@ -27,11 +44,7 @@ def print_param_list(pl) :
pl["pi"] = 3.141592653589793

print ("pl length is", len(pl))
for p in pl :
if type(p.value) == float :
print (" item {} {} {:.6}".format(p.name, p.type, p.value))
else :
print (" item {} {} {}".format(p.name, p.type, p.value))
print_param_list (pl)

print ("pl.contains('e') =", pl.contains('e'))
print ("pl.contains('f') =", pl.contains('f'))
Expand Down

0 comments on commit 64bad0c

Please sign in to comment.