From 6aa06d2f43da7c4d310a219e95a5ac368642f112 Mon Sep 17 00:00:00 2001 From: Erik Frey Date: Mon, 22 Sep 2025 10:26:22 -0700 Subject: [PATCH] Add `_all_fields` to MjOption, MjModel, MjData for struct introspection. PiperOrigin-RevId: 810066486 Change-Id: I807104fc955a505512287f63b7c184a29c0d150e --- python/mujoco/structs.cc | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/python/mujoco/structs.cc b/python/mujoco/structs.cc index 6f157d02..62ac789c 100644 --- a/python/mujoco/structs.cc +++ b/python/mujoco/structs.cc @@ -97,6 +97,19 @@ PYBIND11_MODULE(_structs, m) { mjOption.def("__deepcopy__", [](const MjOptionWrapper& other, py::dict) { return MjOptionWrapper(other); }); + mjOption.def_property_readonly_static("_all_fields", [](py::object) { + std::vector fields; +#define X(dtype, name) fields.push_back(#name); + MJOPTION_FLOATS +#undef X +#define X(name, dim0) fields.push_back(#name); + MJOPTION_VECTORS +#undef X +#define X(dtype, name) fields.push_back(#name); + MJOPTION_INTS +#undef X + return py::tuple(py::cast(fields)); + }); DefineStructFunctions(mjOption); #define X(type, var) \ @@ -417,6 +430,17 @@ This is useful for example when the MJB is not available as a file on disk.)")); return py::tuple(py::cast(fields)); }); + mjModel.def_property_readonly_static("_all_fields", [](py::object) { + std::vector fields; +#define X(var) fields.push_back(#var); + MJMODEL_INTS +#undef X +#define X(type, name, nr, nc) fields.push_back(#name); + MJMODEL_POINTERS +#undef X + return py::tuple(py::cast(fields)); + }); + #define X(dtype, var, dim0, dim1) \ if constexpr (std::string_view(#var) != "text_data" && \ std::string_view(#var) != "names" && \ @@ -748,6 +772,17 @@ This is useful for example when the MJB is not available as a file on disk.)")); py::cast(other.model()).attr("__deepcopy__")(memo); return MjDataWrapper(other, new_model_py.cast()); }); + mjData.def_property_readonly_static("_all_fields", [](py::object) { + std::vector fields; +#define X(dtype, name) fields.push_back(#name); + MJDATA_SCALAR +#undef X +#define X(dtype, name, dim0, dim1) fields.push_back(#name); + MJDATA_VECTOR + MJDATA_POINTERS +#undef X + return py::tuple(py::cast(fields)); + }); mjData.def(py::pickle( [](const MjDataWrapper& d) { // __getstate__ std::ostringstream output(std::ios::out | std::ios::binary);