Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ void bindPyDefinition(py::module& mod)
.def_readonly_static("CHANNEL_NODE_GROUP", &mx::NodeDef::CHANNEL_NODE_GROUP)
.def_readonly_static("ORGANIZATION_NODE_GROUP", &mx::NodeDef::ORGANIZATION_NODE_GROUP)
.def_readonly_static("TRANSLATION_NODE_GROUP", &mx::NodeDef::TRANSLATION_NODE_GROUP);
mod.attr("NodeDef").doc() = R"docstring(
A node definition element within a `Document`.

A `NodeDef` provides the declaration of a node interface, which may then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main concern here is introducing duplicate documentation, which would need to be kept in sync with the corresponding Doxygen comments in the C++ codebase:

https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/source/MaterialXCore/Definition.h#L79

Is there any potential solution where we can inherit these Python documentation strings from the existing Doxygen comments, or does the functionality of the Sphinx documentation system not support this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, that is a concern 🫤

Alternatively, following the Don't Repeat Yourself principle would require engineering to extract the docstrings from header files and assign them dynamically to Python objects as the Sphinx docs are generated.

This could potentially be achieved by running Doxygen and extracting the header documentation in a format that could be read in a Python script as part of generating the Sphinx docs.

On the other hand, a benefit of curating the docstrings separately in the way proposed here would be to accommodate language-specific documentation, e.g. to account for differences in the Python API compared to the C++ API, e.g. when it comes to mutable parameters in C++ vs. return values of functions in Python.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the past I have had some success using https://github.com/pybind/pybind11_mkdoc - which extracts the doxygen doc strings in a format that can be consumed by pybind11. Its been a while, but maybe its worth a look?

be instantiated as a `Node`.

:see: https://materialx.org/docs/api/class_node_def.html)docstring";

py::class_<mx::Implementation, mx::ImplementationPtr, mx::InterfaceElement>(mod, "Implementation")
.def("setFile", &mx::Implementation::setFile)
Expand All @@ -51,6 +58,14 @@ void bindPyDefinition(py::module& mod)
.def_readonly_static("CATEGORY", &mx::Implementation::CATEGORY)
.def_readonly_static("FILE_ATTRIBUTE", &mx::Implementation::FILE_ATTRIBUTE)
.def_readonly_static("FUNCTION_ATTRIBUTE", &mx::Implementation::FUNCTION_ATTRIBUTE);
mod.attr("Implementation").doc() = R"docstring(
An implementation element within a `Document`.

An `Implementation` is used to associate external source code with a specific
`NodeDef`, providing a definition for the node that may either be universal or
restricted to a specific target.

:see: https://materialx.org/docs/api/class_implementation.html)docstring";

py::class_<mx::TypeDef, mx::TypeDefPtr, mx::Element>(mod, "TypeDef")
.def("setSemantic", &mx::TypeDef::setSemantic)
Expand All @@ -67,12 +82,24 @@ void bindPyDefinition(py::module& mod)
.def_readonly_static("CATEGORY", &mx::TypeDef::CATEGORY)
.def_readonly_static("SEMANTIC_ATTRIBUTE", &mx::TypeDef::SEMANTIC_ATTRIBUTE)
.def_readonly_static("CONTEXT_ATTRIBUTE", &mx::TypeDef::CONTEXT_ATTRIBUTE);
mod.attr("TypeDef").doc() = R"docstring(
A type definition element within a `Document`.

:see: https://materialx.org/docs/api/class_type_def.html)docstring";

py::class_<mx::Member, mx::MemberPtr, mx::TypedElement>(mod, "Member")
.def_readonly_static("CATEGORY", &mx::TypeDef::CATEGORY);
mod.attr("Member").doc() = R"docstring(
A member element within a `TypeDef`.

:see: https://materialx.org/docs/api/class_member.html)docstring";

py::class_<mx::Unit, mx::UnitPtr, mx::Element>(mod, "Unit")
.def_readonly_static("CATEGORY", &mx::Unit::CATEGORY);
mod.attr("Unit").doc() = R"docstring(
A unit declaration element within a `UnitDef`.

:see: https://materialx.org/docs/api/class_unit.html)docstring";

py::class_<mx::UnitDef, mx::UnitDefPtr, mx::Element>(mod, "UnitDef")
.def("setUnitType", &mx::UnitDef::setUnitType)
Expand All @@ -83,10 +110,18 @@ void bindPyDefinition(py::module& mod)
.def("getUnits", &mx::UnitDef::getUnits)
.def_readonly_static("CATEGORY", &mx::UnitDef::CATEGORY)
.def_readonly_static("UNITTYPE_ATTRIBUTE", &mx::UnitDef::UNITTYPE_ATTRIBUTE);
mod.attr("UnitDef").doc() = R"docstring(
A unit definition element within a `Document`.

:see: https://materialx.org/docs/api/class_unit_def.html)docstring";

py::class_<mx::UnitTypeDef, mx::UnitTypeDefPtr, mx::Element>(mod, "UnitTypeDef")
.def("getUnitDefs", &mx::UnitTypeDef::getUnitDefs)
.def_readonly_static("CATEGORY", &mx::UnitTypeDef::CATEGORY);
mod.attr("UnitTypeDef").doc() = R"docstring(
A unit type definition element within a `Document`.

:see: https://materialx.org/docs/api/class_unit_type_def.html)docstring";

py::class_<mx::AttributeDef, mx::AttributeDefPtr, mx::TypedElement>(mod, "AttributeDef")
.def("setAttrName", &mx::AttributeDef::setAttrName)
Expand All @@ -98,8 +133,16 @@ void bindPyDefinition(py::module& mod)
.def("setExportable", &mx::AttributeDef::setExportable)
.def("getExportable", &mx::AttributeDef::getExportable)
.def_readonly_static("CATEGORY", &mx::AttributeDef::CATEGORY);
mod.attr("AttributeDef").doc() = R"docstring(
An attribute definition element within a `Document`.

:see: https://materialx.org/docs/api/class_attribute_def.html)docstring";

py::class_<mx::TargetDef, mx::TargetDefPtr, mx::TypedElement>(mod, "TargetDef")
.def("getMatchingTargets", &mx::TargetDef::getMatchingTargets)
.def_readonly_static("CATEGORY", &mx::TargetDef::CATEGORY);
mod.attr("TargetDef").doc() = R"docstring(
The definition of an implementation target as a `TypedElement`.

:see: https://materialx.org/docs/api/class_target_def.html)docstring";
}
6 changes: 6 additions & 0 deletions source/PyMaterialX/PyMaterialXCore/PyDocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,10 @@ void bindPyDocument(py::module& mod)
.def("setColorManagementConfig", &mx::Document::setColorManagementConfig)
.def("hasColorManagementConfig", &mx::Document::hasColorManagementConfig)
.def("getColorManagementConfig", &mx::Document::getColorManagementConfig);
mod.attr("Document").doc() = R"docstring(
A MaterialX document, which represents the top-level element in the MaterialX ownership hierarchy.

Use the factory function `createDocument()` to create a `Document` instance.

:see: https://materialx.org/docs/api/class_document.html)docstring";
}
81 changes: 81 additions & 0 deletions source/PyMaterialX/PyMaterialXCore/PyElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,18 @@ void bindPyElement(py::module& mod)
BIND_ELEMENT_FUNC_INSTANCE(Token)
BIND_ELEMENT_FUNC_INSTANCE(TypeDef)
BIND_ELEMENT_FUNC_INSTANCE(Visibility);
mod.attr("Element").doc() = R"docstring(
The base class for MaterialX elements.

An `Element` is a named object within a `Document`, which may possess any
number of child elements and attributes.

Inherited by: `TypedElement`, `GeomElement`, `Backdrop`, `Collection`,
`CommentElement`, `GenericElement`, `Look`, `LookGroup`, `NewlineElement`,
`PropertySet`, `TypeDef`, `Unit`, `UnitDef`, `UnitTypeDef`, `VariantSet`,
and `VariantAssign`.

:see: https://materialx.org/docs/api/class_element.html)docstring";

py::class_<mx::TypedElement, mx::TypedElementPtr, mx::Element>(mod, "TypedElement")
.def("setType", &mx::TypedElement::setType)
Expand All @@ -143,6 +155,13 @@ void bindPyElement(py::module& mod)
.def("isMultiOutputType", &mx::TypedElement::isMultiOutputType)
.def("getTypeDef", &mx::TypedElement::getTypeDef)
.def_readonly_static("TYPE_ATTRIBUTE", &mx::TypedElement::TYPE_ATTRIBUTE);
mod.attr("TypedElement").doc() = R"docstring(
The base class for typed elements.

Inherited by: `InterfaceElement`, `ValueElement`, `AttributeDef`,
`GeomPropDef`, `Member`, and `TargetDef`.

:see: https://materialx.org/docs/api/class_typed_element.html)docstring";

py::class_<mx::ValueElement, mx::ValueElementPtr, mx::TypedElement>(mod, "ValueElement")
.def("setValueString", &mx::ValueElement::setValueString)
Expand Down Expand Up @@ -198,18 +217,49 @@ void bindPyElement(py::module& mod)
BIND_VALUE_ELEMENT_FUNC_INSTANCE(booleanarray, mx::BoolVec)
BIND_VALUE_ELEMENT_FUNC_INSTANCE(floatarray, mx::FloatVec)
BIND_VALUE_ELEMENT_FUNC_INSTANCE(stringarray, mx::StringVec);
mod.attr("ValueElement").doc() = R"docstring(
The base class for elements that support typed values.

Inherited by: `PortElement`, `GeomProp`, `Property`, `PropertyAssign`, and
`Token`.

:see: https://materialx.org/docs/api/class_value_element.html)docstring";

py::class_<mx::Token, mx::TokenPtr, mx::ValueElement>(mod, "Token")
.def_readonly_static("CATEGORY", &mx::Token::CATEGORY);
mod.attr("Token").doc() = R"docstring(
A token element representing a string value.

Token elements are used to define input and output values for string
substitutions in image filenames.

:see: https://materialx.org/docs/api/class_token.html)docstring";

py::class_<mx::CommentElement, mx::CommentElementPtr, mx::Element>(mod, "CommentElement")
.def_readonly_static("CATEGORY", &mx::CommentElement::CATEGORY);
mod.attr("CommentElement").doc() = R"docstring(
An element representing a block of descriptive text within a `Document`,
which will be stored as a comment when the document is written out.

The comment text may be accessed with the methods `Element.getDocString()`
and `Element.setDocString()`.

:see: https://materialx.org/docs/api/class_comment_element.html)docstring";

py::class_<mx::NewlineElement, mx::NewlineElementPtr, mx::Element>(mod, "NewlineElement")
.def_readonly_static("CATEGORY", &mx::NewlineElement::CATEGORY);
mod.attr("NewlineElement").doc() = R"docstring(
An element representing a newline within a `Document`.

:see: https://materialx.org/docs/api/class_newline_element.html)docstring";

py::class_<mx::GenericElement, mx::GenericElementPtr, mx::Element>(mod, "GenericElement")
.def_readonly_static("CATEGORY", &mx::GenericElement::CATEGORY);
mod.attr("GenericElement").doc() = R"docstring(
A generic element subclass, for instantiating elements with unrecognized
categories.

:see: https://materialx.org/docs/api/class_generic_element.html)docstring";

py::class_<mx::ElementEquivalenceResult>(mod, "ElementEquivalenceResult")
.def_readonly_static("ATTRIBUTE", &mx::ElementEquivalenceResult::ATTRIBUTE)
Expand All @@ -222,13 +272,21 @@ void bindPyElement(py::module& mod)
.def_readwrite("path2", &mx::ElementEquivalenceResult::path2)
.def_readwrite("differenceType", &mx::ElementEquivalenceResult::differenceType)
.def_readwrite("attributeName", &mx::ElementEquivalenceResult::attributeName);
mod.attr("ElementEquivalenceResult").doc() = R"docstring(
A comparison result for the functional equivalence of two elements.

:see: https://materialx.org/docs/api/class_element_equivalence_result.html)docstring";

py::class_<mx::ElementEquivalenceOptions>(mod, "ElementEquivalenceOptions")
.def_readwrite("format", &mx::ElementEquivalenceOptions::format)
.def_readwrite("precision", &mx::ElementEquivalenceOptions::precision)
.def_readwrite("skipAttributes", &mx::ElementEquivalenceOptions::skipAttributes)
.def_readwrite("skipValueComparisons", &mx::ElementEquivalenceOptions::skipValueComparisons)
.def(py::init<>());
mod.attr("ElementEquivalenceOptions").doc() = R"docstring(
A set of options for comparing the functional equivalence of elements.

:see: https://materialx.org/docs/api/class_element_equivalence_options.html)docstring";

py::class_<mx::StringResolver, mx::StringResolverPtr>(mod, "StringResolver")
.def("setFilePrefix", &mx::StringResolver::setFilePrefix)
Expand All @@ -242,10 +300,33 @@ void bindPyElement(py::module& mod)
.def("setGeomNameSubstitution", &mx::StringResolver::setGeomNameSubstitution)
.def("getGeomNameSubstitutions", &mx::StringResolver::getGeomNameSubstitutions)
.def("resolve", &mx::StringResolver::resolve);
mod.attr("StringResolver").doc() = R"docstring(
A helper object for applying string modifiers to data values in the context
of a specific element and geometry.

A `StringResolver` may be constructed through the `Element.createStringResolver()`
method, which initializes it in the context of a specific `Element`, geometry,
and material.

Calling the `StringResolver.resolve()` method applies all modifiers to a
particular string value.

Methods such as `StringResolver.setFilePrefix()` may be used to edit the
stored string modifiers before calling `StringResolver.resolve()`.

:see: https://materialx.org/docs/api/class_string_resolver.html)docstring";

py::class_<mx::ElementPredicate>(mod, "ElementPredicate");
mod.attr("ElementPredicate").doc() = R"docstring(
A function that takes an `Element` and returns a `bool`,
to check whether some criteria has passed.)docstring";

py::register_exception<mx::ExceptionOrphanedElement>(mod, "ExceptionOrphanedElement");
mod.attr("ExceptionOrphanedElement").doc() = R"docstring(
A type of exception that is raised when an `Element` is used after its
owning `Document` has gone out of scope.

:see: https://materialx.org/docs/api/class_exception_orphaned_element.html)docstring";

mod.def("targetStringsMatch", &mx::targetStringsMatch);
mod.def("prettyPrint", &mx::prettyPrint);
Expand Down
5 changes: 5 additions & 0 deletions source/PyMaterialX/PyMaterialXCore/PyException.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ namespace mx = MaterialX;
void bindPyException(py::module& mod)
{
static py::exception<mx::Exception> pyException(mod, "Exception");
mod.attr("Exception").doc() = R"docstring(
The base class for exceptions that are propagated from the MaterialX
library to the client application.

:see: https://materialx.org/docs/api/class_exception.html)docstring";

py::register_exception_translator(
[](std::exception_ptr errPtr)
Expand Down
33 changes: 33 additions & 0 deletions source/PyMaterialX/PyMaterialXCore/PyGeom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ void bindPyGeom(py::module& mod)
.def("getCollectionString", &mx::GeomElement::getCollectionString)
.def("setCollection", &mx::GeomElement::setCollection)
.def("getCollection", &mx::GeomElement::getCollection);
mod.attr("GeomElement").doc() = R"docstring(
The base class for geometric elements, which support bindings to geometries
and geometric collections.

Inherited by: `GeomInfo`, `MaterialAssign`, `PropertySetAssign`, and
`Visibility`.

:see: https://materialx.org/docs/api/class_geom_element.html)docstring";

py::class_<mx::GeomInfo, mx::GeomInfoPtr, mx::GeomElement>(mod, "GeomInfo")
.def("addGeomProp", &mx::GeomInfo::addGeomProp)
Expand Down Expand Up @@ -52,9 +60,18 @@ void bindPyGeom(py::module& mod)
BIND_GEOMINFO_FUNC_INSTANCE(floatarray, mx::FloatVec)
BIND_GEOMINFO_FUNC_INSTANCE(stringarray, mx::StringVec)
.def_readonly_static("CATEGORY", &mx::GeomInfo::CATEGORY);
mod.attr("GeomInfo").doc() = R"docstring(
A geometry info element within a `Document`.

:see: https://materialx.org/docs/api/class_geom_info.html)docstring";

py::class_<mx::GeomProp, mx::GeomPropPtr, mx::ValueElement>(mod, "GeomProp")
.def_readonly_static("CATEGORY", &mx::GeomProp::CATEGORY);
mod.attr("GeomProp").doc() = R"docstring(
A geometric property element within a `GeomInfo`.

:see: https://materialx.org/docs/api/class_geom_prop.html
)docstring";

py::class_<mx::GeomPropDef, mx::GeomPropDefPtr, mx::TypedElement>(mod, "GeomPropDef")
.def("setGeomProp", &mx::GeomPropDef::setGeomProp)
Expand All @@ -70,6 +87,18 @@ void bindPyGeom(py::module& mod)
.def("hasGeomProp", &mx::GeomPropDef::hasGeomProp)
.def("getGeomProp", &mx::GeomPropDef::getGeomProp)
.def_readonly_static("CATEGORY", &mx::GeomPropDef::CATEGORY);
mod.attr("GeomPropDef").doc() = R"docstring(
An element representing a declaration of geometric property data.

A `GeomPropDef` element contains a reference to a geometric node and a set
of modifiers for that node. For example, a world-space normal can be
declared as a reference to the `"normal"` geometric node with a space
setting of `"world"`, or a specific set of texture coordinates can be
declared as a reference to the `"texcoord"` geometric node with an index
setting of `"1"`.

:see: https://materialx.org/docs/api/class_geom_prop_def.html
)docstring";

py::class_<mx::Collection, mx::CollectionPtr, mx::Element>(mod, "Collection")
.def("setIncludeGeom", &mx::Collection::setIncludeGeom)
Expand All @@ -87,6 +116,10 @@ void bindPyGeom(py::module& mod)
.def("hasIncludeCycle", &mx::Collection::hasIncludeCycle)
.def("matchesGeomString", &mx::Collection::matchesGeomString)
.def_readonly_static("CATEGORY", &mx::Collection::CATEGORY);
mod.attr("Collection").doc() = R"docstring(
A collection element within a `Document`.

:see: https://materialx.org/docs/api/class_collection.html)docstring";

mod.def("geomStringsMatch", &mx::geomStringsMatch);

Expand Down
29 changes: 29 additions & 0 deletions source/PyMaterialX/PyMaterialXCore/PyInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ void bindPyInterface(py::module& mod)
.def("getConnectedNode", &mx::PortElement::getConnectedNode)
.def("setConnectedOutput", &mx::PortElement::setConnectedOutput)
.def("getConnectedOutput", &mx::PortElement::getConnectedOutput);
mod.attr("PortElement").doc() = R"docstring(
The base class for port elements.

Port elements support spatially-varying upstream connections to nodes.

Inherited by: `Input` and `Output`.

:see: https://materialx.org/docs/api/class_port_element.html)docstring";

py::class_<mx::Input, mx::InputPtr, mx::PortElement>(mod, "Input")
.def("setDefaultGeomPropString", &mx::Input::setDefaultGeomPropString)
Expand All @@ -40,11 +48,22 @@ void bindPyInterface(py::module& mod)
.def("setConnectedInterfaceName", &mx::Input::setConnectedInterfaceName)
.def("getInterfaceInput", &mx::Input::getInterfaceInput)
.def_readonly_static("CATEGORY", &mx::Input::CATEGORY);
mod.attr("Input").doc() = R"docstring(
An input element within a `Node` or `NodeDef`.

An `Input` holds either a uniform value or a connection to a spatially-varying
`Output`, either of which may be modified within the scope of a `Material`.

:see: https://materialx.org/docs/api/class_input.html)docstring";

py::class_<mx::Output, mx::OutputPtr, mx::PortElement>(mod, "Output")
.def("hasUpstreamCycle", &mx::Output::hasUpstreamCycle)
.def_readonly_static("CATEGORY", &mx::Output::CATEGORY)
.def_readonly_static("DEFAULT_INPUT_ATTRIBUTE", &mx::Output::DEFAULT_INPUT_ATTRIBUTE);
mod.attr("Output").doc() = R"docstring(
A spatially-varying output element within a `NodeGraph` or `NodeDef`.

:see: https://materialx.org/docs/api/class_output.html)docstring";

py::class_<mx::InterfaceElement, mx::InterfaceElementPtr, mx::TypedElement>(mod, "InterfaceElement")
.def("setNodeDefString", &mx::InterfaceElement::setNodeDefString)
Expand Down Expand Up @@ -111,4 +130,14 @@ void bindPyInterface(py::module& mod)
BIND_INTERFACE_TYPE_INSTANCE(floatarray, mx::FloatVec)
BIND_INTERFACE_TYPE_INSTANCE(stringarray, mx::StringVec)
.def_readonly_static("NODE_DEF_ATTRIBUTE", &mx::InterfaceElement::NODE_DEF_ATTRIBUTE);
mod.attr("InterfaceElement").doc() = R"docstring(
The base class for interface elements.

An `InterfaceElement` supports a set of `Input` and `Output` elements, with
an API for setting their values.

Inherited by: `GraphElement`, `Implementation`, `Node`, `NodeDef`, and
`Variant`.

:see: https://materialx.org/docs/api/class_interface_element.html)docstring";
}
Loading