From 2c57b12d941f1632949b090dd97c2aba511a97ac Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Mon, 27 Jul 2026 12:04:26 -0700 Subject: [PATCH] python: migration of oslquery python bindings away from OpenImageIO types OpenImageIO is moving from pybind11 to nanobind. We'd like to do the same with OSL as well. But one thing that will be a big inconvenience if either one switches is that because OSL's OSLQuery API uses OIIO's TypeDesc, that makes it very awkward to mix an OIIO that uses nanobind with an OSL that uses pybind11, or vice versa. I have verified that mixing and matching on main (before this PR) DOES NOT WORK. This PR provides an `OSLQuery.Parameter.type_name` property that holds the parameter's type as a string, rather than a TypeDesc. This will allow Python programs using OSLQuery to not need TypeDesc to have a compatible representation in the Python bindings of both packages, and thus allowing them to not have a problem with mixed pybind11/nanobind usage by the two packages, whichever versions they are using. This is the one and only spot in which OSL's OSLQuery python API *requires* use of anything provided by the OIIO python bindings. I have verified that this change allows peaceful coexistence between a new nanobind-based OIIO and the still-pybind11 OSL (or vice versa, presumably) -- as long your use of OSLQuery in python uses the new `Parameter.type_name` and not the old `Parameter.type`. We also no longer force an import of OpenImageIO when OSL's python bindings initialize. Again, the only time that is needed is if users actually use the old `type` property (rather than the new, preferred `type_name`), and presumably in that case, the TypeDesc had to come from somewhere that needed the OpenImageIO module loaded, so it seems reasonable to not need us to do it unconditionally. This ONLY affects the OSLQuery python bindings. The C++ bindings are a different can of worms entirely, since it makes extensive use of ustring in addition to TypeDesc. Disentangling them on the C++ side is going to be more delicate and probably will involve breaking changes. I think this is safe to backport as well, which will allow people using OSL 1.15 (as long as it's new enough to have the fix) to start using the new type_name and future-proof their scripts so that no changes are needed when the future nanobind based bindings come to either or both packages. Signed-off-by: Larry Gritz --- src/include/OSL/oslquery.h | 5 +++++ src/liboslquery/oslquery.cpp | 16 ++++++++++++++++ src/liboslquery/py_osl.cpp | 7 ++++--- testsuite/python-oslquery/src/test_oslquery.py | 4 ++-- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/include/OSL/oslquery.h b/src/include/OSL/oslquery.h index a7960ac79d..75766374e0 100644 --- a/src/include/OSL/oslquery.h +++ b/src/include/OSL/oslquery.h @@ -97,6 +97,11 @@ class OSLQUERYPUBLIC OSLQuery { Parameter(Parameter&& src); const Parameter& operator=(const Parameter&); const Parameter& operator=(Parameter&&); + + /// Return the type as a string ("float", "color[7]", etc.) + std::string type_name() const; + /// Set the type as a string + void type_name(const std::string& typestring); }; /// OSLQuery methods diff --git a/src/liboslquery/oslquery.cpp b/src/liboslquery/oslquery.cpp index 1ea3effae2..9a741a5156 100644 --- a/src/liboslquery/oslquery.cpp +++ b/src/liboslquery/oslquery.cpp @@ -351,6 +351,22 @@ OSLQuery::Parameter::operator=(Parameter&& src) +std::string +OSLQuery::Parameter::type_name() const +{ + return type.c_str(); +} + + + +void +OSLQuery::Parameter::type_name(const std::string& typestring) +{ + type = TypeDesc(typestring); +} + + + OSLQuery::OSLQuery() {} diff --git a/src/liboslquery/py_osl.cpp b/src/liboslquery/py_osl.cpp index 56a2f70a2f..a4b904a913 100644 --- a/src/liboslquery/py_osl.cpp +++ b/src/liboslquery/py_osl.cpp @@ -26,6 +26,10 @@ declare_oslqueryparam(py::module& m) return PY_STR(p.name.string()); }) .def_readwrite("type", &Parameter::type) + .def_property( + "type_name", + [](const Parameter& p) { return PY_STR(p.type_name()); }, + [](Parameter& p, const std::string& t) { p.type_name(t); }) .def_readwrite("isoutput", &Parameter::isoutput) .def_readwrite("varlenarray", &Parameter::varlenarray) .def_readwrite("isstruct", &Parameter::isstruct) @@ -171,9 +175,6 @@ declare_oslquery(py::module& m) PYBIND11_MODULE(oslquery, m) { - // Force an OIIO module load so we have TypeDesc, among other things. - py::module oiio = py::module::import("OpenImageIO"); - // Global (OSL scope) functions and symbols m.attr("osl_version") = OSL_VERSION; m.attr("VERSION") = OSL_VERSION; diff --git a/testsuite/python-oslquery/src/test_oslquery.py b/testsuite/python-oslquery/src/test_oslquery.py index 720c9ad268..129155c05a 100755 --- a/testsuite/python-oslquery/src/test_oslquery.py +++ b/testsuite/python-oslquery/src/test_oslquery.py @@ -30,8 +30,8 @@ def printparam(p, indent=" ") : # as tuples. print (indent, "{}{} {} = {}".format( "output " if p.isoutput else "", - p.type, p.name, - "'{}'".format(p.value) if p.type == "string" else p.value)) + p.type_name, p.name, + "'{}'".format(p.value) if p.type_name == "string" else p.value)) if p.spacename : print (indent, " space:", p.spacename) # Metadata are themselves another tuple of Parameter objects hanging off