From ecfe65242fa405f9b3fcc5924d5e988a065ed310 Mon Sep 17 00:00:00 2001 From: Google DeepMind Date: Thu, 21 Aug 2025 12:33:58 -0700 Subject: [PATCH] Add `nullable` attribute to AST nodes for function parameters in API introspection. Fixes #309 This change introduces an `nullable` flag to `ArrayType`, `PointerType`, and `FunctionParameterDecl`. The code generation process now parses function comments from `mujoco.h` to identify parameters marked as "Nullable" and sets this flag accordingly. PiperOrigin-RevId: 797870883 Change-Id: Ic7cac5a7a9c14177fbf97b8655c5c4809d4ef683 --- python/mujoco/introspect/ast_nodes.py | 15 +++- python/mujoco/introspect/ast_nodes_test.py | 23 ++++++ .../introspect/codegen/generate_functions.py | 37 +++++++-- python/mujoco/introspect/functions.py | 81 +++++++++++++++++++ python/mujoco/introspect/functions_test.py | 2 +- 5 files changed, 148 insertions(+), 10 deletions(-) diff --git a/python/mujoco/introspect/ast_nodes.py b/python/mujoco/introspect/ast_nodes.py index 867f49ad..275e0afb 100644 --- a/python/mujoco/introspect/ast_nodes.py +++ b/python/mujoco/introspect/ast_nodes.py @@ -62,9 +62,11 @@ class ValueType: name: str is_const: bool = False is_volatile: bool = False + nullable: bool = False def __init__(self, name: str, is_const: bool = False, - is_volatile: bool = False): + is_volatile: bool = False, + nullable: bool = False): is_valid_type_name = ( name == 'void *(*)(void *)' or VALID_TYPE_NAME_PATTERN.fullmatch(name) or @@ -74,6 +76,7 @@ class ValueType: self.name = name self.is_const = is_const self.is_volatile = is_volatile + self.nullable = nullable def decl(self, name_or_decl: Optional[str] = None) -> str: parts = [] @@ -96,9 +99,11 @@ class ArrayType: inner_type: Union[ValueType, 'PointerType'] extents: Tuple[int, ...] + nullable: bool = False - def __init__(self, inner_type: Union[ValueType, 'PointerType'], - extents: Sequence[int]): + def __init__( + self, inner_type: Union[ValueType, 'PointerType'], extents: Sequence[int] + ): self.inner_type = inner_type self.extents = tuple(extents) @@ -119,6 +124,7 @@ class PointerType: """Represents a C pointer type.""" inner_type: Union[ValueType, ArrayType, 'PointerType'] + nullable: bool = False is_const: bool = False is_volatile: bool = False is_restrict: bool = False @@ -126,6 +132,8 @@ class PointerType: def decl(self, name_or_decl: Optional[str] = None) -> str: """Creates a string that declares an object of this type.""" parts = ['*'] + if self.nullable: + parts.append('nullable') if self.is_const: parts.append('const') if self.is_volatile: @@ -155,6 +163,7 @@ class FunctionParameterDecl: name: str type: Union[ValueType, ArrayType, PointerType] + nullable: bool = False def __str__(self): return self.type.decl(self.name) diff --git a/python/mujoco/introspect/ast_nodes_test.py b/python/mujoco/introspect/ast_nodes_test.py index 2c642c49..6a6c08b7 100644 --- a/python/mujoco/introspect/ast_nodes_test.py +++ b/python/mujoco/introspect/ast_nodes_test.py @@ -174,6 +174,29 @@ class AstNodesTest(absltest.TestCase): self.assertEqual(str(union), 'union {int foo; float bar[3];}') self.assertEqual(union.decl('var'), 'union {int foo; float bar[3];} var') + def test_function_parameter_decl_nullable(self): + param_ptr_nullable = ast_nodes.FunctionParameterDecl( + name='ptr_param', + type=ast_nodes.PointerType(ast_nodes.ValueType('int')), + nullable=True + ) + self.assertTrue(param_ptr_nullable.nullable) + self.assertEqual(str(param_ptr_nullable), 'int * ptr_param') + + param_ptr_not_nullable = ast_nodes.FunctionParameterDecl( + name='ptr_param', + type=ast_nodes.PointerType(ast_nodes.ValueType('int')), + ) + self.assertFalse(param_ptr_not_nullable.nullable) + self.assertEqual(str(param_ptr_not_nullable), 'int * ptr_param') + + param_array_nullable = ast_nodes.FunctionParameterDecl( + name='array_param', + type=ast_nodes.ArrayType(ast_nodes.ValueType('float'), (10,)), + nullable=True + ) + self.assertTrue(param_array_nullable.nullable) + self.assertEqual(str(param_array_nullable), 'float array_param[10]') if __name__ == '__main__': absltest.main() diff --git a/python/mujoco/introspect/codegen/generate_functions.py b/python/mujoco/introspect/codegen/generate_functions.py index d343962f..53112a53 100644 --- a/python/mujoco/introspect/codegen/generate_functions.py +++ b/python/mujoco/introspect/codegen/generate_functions.py @@ -58,21 +58,43 @@ class MjFunctionVisitor: node['type']['qualType']) parameters = [] comments = [] + nullable_params = set() + + for child in node['inner']: + child_kind = child.get('kind') + if child_kind == 'FullComment': + comments.append(self._make_comment(child)) + nullable_params.update(self._find_nullable_params(child)) + comment = ' '.join(comments).strip() + for child in node['inner']: child_kind = child.get('kind') if child_kind == 'ParmVarDecl': - parameters.append(self._make_parameter(child)) - if child_kind == 'FullComment': - comments.append(self._make_comment(child)) - comment = ' '.join(comments).strip() + parameters.append(self._make_parameter(child, nullable_params)) + return ast_nodes.FunctionDecl( name=name, return_type=return_type, parameters=parameters, doc=comment) + def _find_nullable_params(self, node: ClangJsonNode) -> set[str]: + """Finds the names of parameters that are marked as nullable.""" + nullable_params = set() + for child in node['inner']: + child_kind = child.get('kind') + if child_kind == 'ParagraphComment': + nullable_params.update(self._find_nullable_params(child)) + if child_kind == 'TextComment': + if 'Nullable' in child['text']: + for param in child['text'].split(':')[1].split(','): + nullable_params.add(param.strip()) + return nullable_params + def _make_parameter( - self, node: ClangJsonNode) -> ast_nodes.FunctionParameterDecl: + self, node: ClangJsonNode, nullable_params: set[str] + ) -> ast_nodes.FunctionParameterDecl: """Makes a ParameterDecl from a Clang AST ParmVarDecl node.""" name = node['name'] type_name = node['type']['qualType'] + nullable = name in nullable_params # For a pointer parameters, look up in the original header to see if # n array extent was declared there. @@ -85,7 +107,10 @@ class MjFunctionVisitor: type_name = decl[:name_begin] + decl[name_end:] return ast_nodes.FunctionParameterDecl( - name=name, type=type_parsing.parse_type(type_name)) + nullable=nullable, + name=name, + type=type_parsing.parse_type(type_name), + ) def _make_comment(self, node: ClangJsonNode) -> str: """Makes a comment string from a Clang AST FullComment node.""" diff --git a/python/mujoco/introspect/functions.py b/python/mujoco/introspect/functions.py index 8a2cf0d3..e925870b 100644 --- a/python/mujoco/introspect/functions.py +++ b/python/mujoco/introspect/functions.py @@ -148,12 +148,14 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjVFS', is_const=True), ), + nullable=True, ), FunctionParameterDecl( name='error', type=PointerType( inner_type=ValueType(name='char'), ), + nullable=True, ), FunctionParameterDecl( name='error_sz', @@ -180,12 +182,14 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjVFS', is_const=True), ), + nullable=True, ), FunctionParameterDecl( name='error', type=PointerType( inner_type=ValueType(name='char'), ), + nullable=True, ), FunctionParameterDecl( name='error_sz', @@ -212,12 +216,14 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjVFS', is_const=True), ), + nullable=True, ), FunctionParameterDecl( name='error', type=PointerType( inner_type=ValueType(name='char'), ), + nullable=True, ), FunctionParameterDecl( name='error_sz', @@ -244,6 +250,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjVFS', is_const=True), ), + nullable=True, ), ), doc='Compile spec to model.', @@ -284,6 +291,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjVFS', is_const=True), ), + nullable=True, ), FunctionParameterDecl( name='m', @@ -322,6 +330,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='char'), ), + nullable=True, ), FunctionParameterDecl( name='error_sz', @@ -363,6 +372,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='char'), ), + nullable=True, ), FunctionParameterDecl( name='error_sz', @@ -393,6 +403,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='char'), ), + nullable=True, ), FunctionParameterDecl( name='error_sz', @@ -657,12 +668,14 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='char', is_const=True), ), + nullable=True, ), FunctionParameterDecl( name='buffer', type=PointerType( inner_type=ValueType(name='void'), ), + nullable=True, ), FunctionParameterDecl( name='buffer_sz', @@ -689,6 +702,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjVFS', is_const=True), ), + nullable=True, ), ), doc='Load model from binary MJB file. If vfs is not NULL, look up file in vfs before reading from disk.', # pylint: disable=line-too-long @@ -1026,6 +1040,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='char'), ), + nullable=True, ), FunctionParameterDecl( name='error_sz', @@ -2206,6 +2221,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='mjtNum'), extents=(1,), ), + nullable=True, ), FunctionParameterDecl( name='flg_coneHessian', @@ -2470,12 +2486,14 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='jacr', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='point', @@ -2513,12 +2531,14 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='jacr', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='body', @@ -2549,12 +2569,14 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='jacr', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='body', @@ -2615,12 +2637,14 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='jacr', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='geom', @@ -2651,12 +2675,14 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='jacr', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='site', @@ -2687,12 +2713,14 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='jacAxis', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='point', @@ -2737,12 +2765,14 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='jacr', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='point', @@ -2954,18 +2984,21 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='int'), ), + nullable=True, ), FunctionParameterDecl( name='rowadr', type=PointerType( inner_type=ValueType(name='int'), ), + nullable=True, ), FunctionParameterDecl( name='colind', type=PointerType( inner_type=ValueType(name='int'), ), + nullable=True, ), ), doc='Add inertia matrix to destination matrix. Destination can be sparse or dense when all int* are NULL.', # pylint: disable=line-too-long @@ -2993,6 +3026,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='mjtNum', is_const=True), extents=(3,), ), + nullable=True, ), FunctionParameterDecl( name='torque', @@ -3000,6 +3034,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='mjtNum', is_const=True), extents=(3,), ), + nullable=True, ), FunctionParameterDecl( name='point', @@ -3134,6 +3169,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='mjtNum'), extents=(6,), ), + nullable=True, ), ), doc='Returns smallest signed distance between two geoms and optionally segment from geom1 to geom2.', # pylint: disable=line-too-long @@ -3529,6 +3565,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='int'), extents=(1,), ), + nullable=True, ), ), doc='Intersect ray (pnt+x*vec, x>=0) with visible geoms, except geoms in bodyexclude. Return distance (x) to nearest surface, or -1 if no intersection and output geomid. geomgroup, flg_static are as in mjvOption; geomgroup==NULL skips group exclusion.', # pylint: disable=line-too-long @@ -3715,6 +3752,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='int'), extents=(1,), ), + nullable=True, ), ), doc='Intersect ray with flex, return nearest distance or -1 if no intersection, and also output nearest vertex id.', # pylint: disable=line-too-long @@ -3764,6 +3802,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='int'), extents=(1,), ), + nullable=True, ), ), doc='Intersect ray with skin, return nearest distance or -1 if no intersection, and also output nearest vertex id.', # pylint: disable=line-too-long @@ -4296,6 +4335,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='int'), extents=(1,), ), + nullable=True, ), FunctionParameterDecl( name='flexid', @@ -4303,6 +4343,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='int'), extents=(1,), ), + nullable=True, ), FunctionParameterDecl( name='skinid', @@ -4310,6 +4351,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='int'), extents=(1,), ), + nullable=True, ), ), doc='Select geom, flex or skin with mouse, return bodyid; -1: none selected.', # pylint: disable=line-too-long @@ -4363,6 +4405,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='mjtNum', is_const=True), extents=(3,), ), + nullable=True, ), FunctionParameterDecl( name='pos', @@ -4370,6 +4413,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='mjtNum', is_const=True), extents=(3,), ), + nullable=True, ), FunctionParameterDecl( name='mat', @@ -4377,6 +4421,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='mjtNum', is_const=True), extents=(9,), ), + nullable=True, ), FunctionParameterDecl( name='rgba', @@ -4384,6 +4429,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='float', is_const=True), extents=(4,), ), + nullable=True, ), ), doc='Initialize given geom fields when not NULL, set the rest to their default values.', # pylint: disable=line-too-long @@ -4534,6 +4580,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjModel'), ), + nullable=True, ), FunctionParameterDecl( name='src', @@ -6858,6 +6905,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='mjtNum', is_const=True), extents=(9,), ), + nullable=True, ), ), doc='Coordinate transform of 6D motion or force vector in rotation:translation format. rotnew2old is 3-by-3, NULL means no rotation; flg_force specifies force or motion type.', # pylint: disable=line-too-long @@ -7789,6 +7837,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='int'), ), + nullable=True, ), FunctionParameterDecl( name='H', @@ -7811,12 +7860,14 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjtNum', is_const=True), ), + nullable=True, ), FunctionParameterDecl( name='upper', type=PointerType( inner_type=ValueType(name='mjtNum', is_const=True), ), + nullable=True, ), ), doc="minimize 0.5*x'*H*x + x'*g s.t. lower <= x <= upper, return rank or -1 if failed inputs: n - problem dimension H - SPD matrix n*n g - bias vector n lower - lower bounds n upper - upper bounds n res - solution warmstart n return value: nfree <= n - rank of unconstrained subspace, -1 if failure outputs (required): res - solution n R - subspace Cholesky factor nfree*nfree allocated: n*(n+7) outputs (optional): index - set of free dimensions nfree allocated: n notes: the initial value of res is used to warmstart the solver R must have allocatd size n*(n+7), but only nfree*nfree values are used in output index (if given) must have allocated size n, but only nfree values are used in output only the lower triangles of H and R and are read from and written to, respectively the convenience function mju_boxQPmalloc allocates the required data structures", # pylint: disable=line-too-long @@ -8553,24 +8604,28 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='B', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='C', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='D', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), ), doc='Finite differenced transition matrices (control theory notation) d(x_next) = A*dx + B*du d(sensor) = C*dx + D*du required output matrix dimensions: A: (2*nv+na x 2*nv+na) B: (2*nv+na x nu) D: (nsensordata x 2*nv+na) C: (nsensordata x nu)', # pylint: disable=line-too-long @@ -8605,42 +8660,49 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='DfDv', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='DfDa', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='DsDq', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='DsDv', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='DsDa', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), FunctionParameterDecl( name='DmDq', type=PointerType( inner_type=ValueType(name='mjtNum'), ), + nullable=True, ), ), doc='Finite differenced Jacobians of (force, sensors) = mj_inverse(state, acceleration) All outputs are optional. Output dimensions (transposed w.r.t Control Theory convention): DfDq: (nv x nv) DfDv: (nv x nv) DfDa: (nv x nv) DsDq: (nv x nsensordata) DsDv: (nv x nsensordata) DsDa: (nv x nsensordata) DmDq: (nv x nM) single-letter shortcuts: inputs: q=qpos, v=qvel, a=qacc outputs: f=qfrc_inverse, s=sensordata, m=qM notes: optionally computes mass matrix Jacobian DmDq flg_actuation specifies whether to subtract qfrc_actuator from qfrc_inverse', # pylint: disable=line-too-long @@ -8670,6 +8732,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='mjtNum'), extents=(9,), ), + nullable=True, ), FunctionParameterDecl( name='Db', @@ -8677,6 +8740,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='mjtNum'), extents=(9,), ), + nullable=True, ), ), doc='Derivatives of mju_subQuat.', @@ -8703,6 +8767,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='mjtNum'), extents=(9,), ), + nullable=True, ), FunctionParameterDecl( name='Dvel', @@ -8710,6 +8775,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='mjtNum'), extents=(9,), ), + nullable=True, ), FunctionParameterDecl( name='Dscale', @@ -8717,6 +8783,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ inner_type=ValueType(name='mjtNum'), extents=(3,), ), + nullable=True, ), ), doc='Derivatives of mju_quatIntegrate.', @@ -9005,6 +9072,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjsDefault', is_const=True), ), + nullable=True, ), ), doc='Add child body to body, return child.', @@ -9027,6 +9095,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjsDefault', is_const=True), ), + nullable=True, ), ), doc='Add site to body, return site spec.', @@ -9049,6 +9118,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjsDefault', is_const=True), ), + nullable=True, ), ), doc='Add joint to body.', @@ -9087,6 +9157,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjsDefault', is_const=True), ), + nullable=True, ), ), doc='Add geom to body.', @@ -9109,6 +9180,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjsDefault', is_const=True), ), + nullable=True, ), ), doc='Add camera to body.', @@ -9131,6 +9203,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjsDefault', is_const=True), ), + nullable=True, ), ), doc='Add light to body.', @@ -9195,6 +9268,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjsDefault', is_const=True), ), + nullable=True, ), ), doc='Add actuator.', @@ -9249,6 +9323,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjsDefault', is_const=True), ), + nullable=True, ), ), doc='Add contact pair.', @@ -9287,6 +9362,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjsDefault', is_const=True), ), + nullable=True, ), ), doc='Add equality.', @@ -9309,6 +9385,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjsDefault', is_const=True), ), + nullable=True, ), ), doc='Add tendon.', @@ -9513,6 +9590,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjsDefault', is_const=True), ), + nullable=True, ), ), doc='Add default.', @@ -9795,6 +9873,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjsDefault', is_const=True), ), + nullable=True, ), ), doc='Add mesh.', @@ -9865,6 +9944,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjsDefault', is_const=True), ), + nullable=True, ), ), doc='Add material.', @@ -10537,6 +10617,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ type=PointerType( inner_type=ValueType(name='int'), ), + nullable=True, ), ), doc='Get double array contents and optionally its size.', diff --git a/python/mujoco/introspect/functions_test.py b/python/mujoco/introspect/functions_test.py index 2e3985b9..bebf603d 100644 --- a/python/mujoco/introspect/functions_test.py +++ b/python/mujoco/introspect/functions_test.py @@ -57,7 +57,7 @@ class FunctionsTest(absltest.TestCase): name='oldpos', type=type_parsing.parse_type('const mjtNum[3]')), ast_nodes.FunctionParameterDecl( name='rotnew2old', - type=type_parsing.parse_type('const mjtNum[9]')))) + type=type_parsing.parse_type('const mjtNum[9]'), nullable=True))) self.assertEqual( func_decl.doc, 'Coordinate transform of 6D motion or force vector in ' + 'rotation:translation format. rotnew2old is 3-by-3, ' +