mujoco-mojo 2.4.2__tar.gz → 2.4.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/PKG-INFO +2 -2
  2. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/pyproject.toml +1 -1
  3. mujoco_mojo-2.4.4/scripts/check_mjcf_spec_coverage.py +650 -0
  4. mujoco_mojo-2.4.4/scripts/mjcf_ignore.txt +96 -0
  5. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/__init__.py +52 -0
  6. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator.py +1 -0
  7. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator_attr/adhesion.py +24 -1
  8. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator_attr/base.py +18 -1
  9. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator_attr/motor.py +2 -0
  10. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator_attr/plugin.py +2 -1
  11. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator_attr/velocity.py +1 -1
  12. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/asset_attr/mesh.py +5 -4
  13. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/asset_attr/texture.py +2 -1
  14. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body.py +4 -0
  15. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/camera.py +1 -1
  16. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/composite.py +3 -3
  17. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/composite_attr/geom.py +23 -0
  18. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/composite_attr/joint.py +2 -2
  19. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/composite_attr/site.py +3 -0
  20. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/composite_attr/skin.py +4 -0
  21. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/flexcomp.py +1 -1
  22. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/flexcomp_attr/contact.py +4 -1
  23. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/free_joint.py +4 -0
  24. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/geom.py +2 -1
  25. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/inertial.py +21 -19
  26. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/joint.py +29 -4
  27. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/site.py +2 -1
  28. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/contact_attr/exclude.py +1 -1
  29. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/contact_attr/pair.py +1 -1
  30. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/deformable_attr/flex_attr/contact.py +2 -2
  31. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/equality_attr/connect.py +1 -0
  32. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/equality_attr/equality_base.py +2 -1
  33. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/equality_attr/tendon.py +2 -3
  34. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/equality_attr/weld.py +6 -6
  35. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/option_attr/flag.py +4 -0
  36. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/base.py +2 -1
  37. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/base_collision.py +3 -1
  38. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/contact.py +6 -6
  39. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/insidesite.py +1 -1
  40. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/plugin.py +3 -1
  41. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/tactile.py +6 -2
  42. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/tendon_attr/spatial_attr/geom.py +1 -1
  43. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/tendon_attr/tendon_base.py +2 -1
  44. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/orientation.py +7 -0
  45. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/pose.py +2 -1
  46. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/xml_model.py +36 -0
  47. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/runtime/video_recorder.py +25 -0
  48. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/stochas/__init__.py +42 -2
  49. mujoco_mojo-2.4.4/src/mujoco_mojo/templates/dojo.sh +8 -0
  50. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/typing.py +3 -3
  51. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/cli.py +6 -6
  52. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/routers/mosaic.py +52 -18
  53. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/partials/trial_viewer/_distributions.html +159 -113
  54. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/partials/trial_viewer/_series_panel.html +10 -10
  55. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/partials/trial_viewer/_trial_logs.html +37 -37
  56. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/js/trial-viewer.js +130 -55
  57. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/lib/format.ts +6 -0
  58. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/models.ts +3 -1
  59. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/trial-viewer.ts +156 -58
  60. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/reloaded.py +1 -1
  61. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/proximity.py +40 -16
  62. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/runner.py +26 -1
  63. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/runtime/monte_carlo_bumper.py +288 -0
  64. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/utils/proximity_test.py +36 -0
  65. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/utils/runner_test.py +47 -0
  66. mujoco_mojo-2.4.2/tests/one_off/proximity/dojo.sh +0 -4
  67. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/.gitattributes +0 -0
  68. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/.github/workflows/docs.yml +0 -0
  69. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/.github/workflows/release.yml +0 -0
  70. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/.gitignore +0 -0
  71. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/.pre-commit-config.yaml +0 -0
  72. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/.python-version +0 -0
  73. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/.vscode/settings.json +0 -0
  74. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/LICENSE +0 -0
  75. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/README.md +0 -0
  76. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/TODO.md +0 -0
  77. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/about.md +0 -0
  78. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/dark-hero-logo.png +0 -0
  79. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/dark-hero-logo.svg +0 -0
  80. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/dark-logo.svg +0 -0
  81. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/light-hero-logo.svg +0 -0
  82. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/light-logo.svg +0 -0
  83. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/logo.svg +0 -0
  84. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/mesh/bunny_original.jpg +0 -0
  85. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/mesh/bunny_with_coacd.jpg +0 -0
  86. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/mesh/bunny_without_coacd.jpg +0 -0
  87. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/mesh/frustums.jpg +0 -0
  88. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/mesh/proximity/convex_hull_wireframe.jpg +0 -0
  89. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/mesh/proximity/real_wireframe.jpg +0 -0
  90. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/mesh/with_coacd.jpg +0 -0
  91. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/mesh/without_coacd.jpg +0 -0
  92. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/dark-export-options.jpg +0 -0
  93. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/dark-monitor-view.jpg +0 -0
  94. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/dark-mosaic-view.jpg +0 -0
  95. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/dark-notes-editor.jpg +0 -0
  96. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/dark-plot-editor.jpg +0 -0
  97. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/dark-selector-bar.jpg +0 -0
  98. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/dark-shapes-editor.jpg +0 -0
  99. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/generate-result.jpg +0 -0
  100. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/light-export-options.jpg +0 -0
  101. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/light-monitor-view.jpg +0 -0
  102. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/light-mosaic-view.jpg +0 -0
  103. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/light-notes-editor.jpg +0 -0
  104. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/light-plot-editor.jpg +0 -0
  105. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/light-selector-bar.jpg +0 -0
  106. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/light-shapes-editor.jpg +0 -0
  107. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/assets/user-guides/runtime-anim.gif +0 -0
  108. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/index.md +0 -0
  109. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/javascripts/favicon-switcher.js +0 -0
  110. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/javascripts/mojo-highlighter.js +0 -0
  111. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/javascripts/tablesort.js +0 -0
  112. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/stylesheets/extra.css +0 -0
  113. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/MOJO_RUNTIME_REPORT.md +0 -0
  114. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/dojo.md +0 -0
  115. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/generate-script.md +0 -0
  116. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/getting-started.md +0 -0
  117. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/init.md +0 -0
  118. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/monitor.md +0 -0
  119. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/monte_carlo_example.py +0 -0
  120. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/mosaic.md +0 -0
  121. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/optimization.md +0 -0
  122. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/optimization_example.py +0 -0
  123. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/pose-context.md +0 -0
  124. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/pose_context_example.py +0 -0
  125. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/reloaded.md +0 -0
  126. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/running-jobs.md +0 -0
  127. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/docs/user-guides/runtime-script.md +0 -0
  128. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/examples/boxes_and_springs_monte_carlo.py +0 -0
  129. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/examples/boxes_and_springs_optimization.py +0 -0
  130. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/examples/tennis_racket_theorem.py +0 -0
  131. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/mkdocs.yml +0 -0
  132. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/requirements.txt +0 -0
  133. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/scripts/copy_docs_examples_to_examples.py +0 -0
  134. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/scripts/fix_smart_quotes.py +0 -0
  135. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/scripts/gen_ref_pages.py +0 -0
  136. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/scripts/gen_ts_models.py +0 -0
  137. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/scripts/templating/filestomake.txt +0 -0
  138. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/scripts/templating/make_sensors.py +0 -0
  139. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/scripts/templating/sensor_template.py +0 -0
  140. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/__about__.py +0 -0
  141. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/base.py +0 -0
  142. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/meta.py +0 -0
  143. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mj_state.py +0 -0
  144. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/__init__.py +0 -0
  145. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/defaults.py +0 -0
  146. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/dependency_path.py +0 -0
  147. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/extension.py +0 -0
  148. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/meta/__init__.py +0 -0
  149. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/meta/frame.py +0 -0
  150. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/meta/include.py +0 -0
  151. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/meta/replicate.py +0 -0
  152. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco.py +0 -0
  153. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/__init__.py +0 -0
  154. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator_attr/__init__.py +0 -0
  155. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator_attr/cylinder.py +0 -0
  156. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator_attr/damper.py +0 -0
  157. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator_attr/dcmotor.py +0 -0
  158. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator_attr/general.py +0 -0
  159. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator_attr/intvelocity.py +0 -0
  160. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator_attr/muscle.py +0 -0
  161. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/actuator_attr/position.py +0 -0
  162. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/asset.py +0 -0
  163. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/asset_attr/__init__.py +0 -0
  164. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/asset_attr/hfield.py +0 -0
  165. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/asset_attr/material.py +0 -0
  166. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/asset_attr/material_attr/__init__.py +0 -0
  167. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/asset_attr/material_attr/layer.py +0 -0
  168. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/asset_attr/model.py +0 -0
  169. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/__init__.py +0 -0
  170. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/attach.py +0 -0
  171. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/composite_attr/__init__.py +0 -0
  172. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/flexcomp_attr/__init__.py +0 -0
  173. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/flexcomp_attr/edge.py +0 -0
  174. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/flexcomp_attr/elasticity.py +0 -0
  175. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/flexcomp_attr/pin.py +0 -0
  176. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/light.py +0 -0
  177. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/body_attr/plugin.py +0 -0
  178. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/compiler.py +0 -0
  179. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/compiler_attr/__init__.py +0 -0
  180. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/compiler_attr/lengthrange.py +0 -0
  181. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/contact.py +0 -0
  182. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/contact_attr/__init__.py +0 -0
  183. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/deformable.py +0 -0
  184. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/deformable_attr/__init__.py +0 -0
  185. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/deformable_attr/flex.py +0 -0
  186. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/deformable_attr/flex_attr/__init__.py +0 -0
  187. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/deformable_attr/flex_attr/edge.py +0 -0
  188. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/deformable_attr/flex_attr/elasticity.py +0 -0
  189. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/deformable_attr/skin.py +0 -0
  190. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/deformable_attr/skin_attr/__init__.py +0 -0
  191. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/deformable_attr/skin_attr/bone.py +0 -0
  192. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/equality.py +0 -0
  193. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/equality_attr/__init__.py +0 -0
  194. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/equality_attr/flex.py +0 -0
  195. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/equality_attr/flexstrain.py +0 -0
  196. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/equality_attr/flexvert.py +0 -0
  197. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/equality_attr/joint.py +0 -0
  198. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/keyframe.py +0 -0
  199. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/keyframe_attr/__init__.py +0 -0
  200. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/keyframe_attr/key.py +0 -0
  201. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/option.py +0 -0
  202. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/option_attr/__init__.py +0 -0
  203. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor.py +0 -0
  204. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/__init__.py +0 -0
  205. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/accelerometer.py +0 -0
  206. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/actuatorfrc.py +0 -0
  207. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/actuatorpos.py +0 -0
  208. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/actuatorvel.py +0 -0
  209. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/ballangvel.py +0 -0
  210. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/ballquat.py +0 -0
  211. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/camprojection.py +0 -0
  212. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/clock.py +0 -0
  213. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/distance.py +0 -0
  214. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/e_kinetic.py +0 -0
  215. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/e_potential.py +0 -0
  216. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/force.py +0 -0
  217. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/frameangacc.py +0 -0
  218. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/frameangvel.py +0 -0
  219. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/framelinacc.py +0 -0
  220. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/framelinvel.py +0 -0
  221. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/framepos.py +0 -0
  222. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/framequat.py +0 -0
  223. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/framexaxis.py +0 -0
  224. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/frameyaxis.py +0 -0
  225. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/framezaxis.py +0 -0
  226. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/fromto.py +0 -0
  227. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/gyro.py +0 -0
  228. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/jointactuatorfrc.py +0 -0
  229. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/jointlimitfrc.py +0 -0
  230. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/jointlimitpos.py +0 -0
  231. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/jointlimitvel.py +0 -0
  232. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/jointpos.py +0 -0
  233. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/jointvel.py +0 -0
  234. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/magnetometer.py +0 -0
  235. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/normal.py +0 -0
  236. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/rangefinder.py +0 -0
  237. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/subtreeangmom.py +0 -0
  238. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/subtreecom.py +0 -0
  239. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/subtreelinvel.py +0 -0
  240. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/tendonactuatorfrc.py +0 -0
  241. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/tendonlimitfrc.py +0 -0
  242. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/tendonlimitpos.py +0 -0
  243. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/tendonlimitvel.py +0 -0
  244. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/tendonpos.py +0 -0
  245. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/tendonvel.py +0 -0
  246. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/torque.py +0 -0
  247. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/touch.py +0 -0
  248. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/user.py +0 -0
  249. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/sensor_attr/velocimeter.py +0 -0
  250. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/size.py +0 -0
  251. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/statistic.py +0 -0
  252. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/tendon.py +0 -0
  253. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/tendon_attr/__init__.py +0 -0
  254. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/tendon_attr/fixed.py +0 -0
  255. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/tendon_attr/fixed_attr/__init__.py +0 -0
  256. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/tendon_attr/fixed_attr/joint.py +0 -0
  257. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/tendon_attr/spatial.py +0 -0
  258. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/tendon_attr/spatial_attr/__init__.py +0 -0
  259. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/tendon_attr/spatial_attr/pulley.py +0 -0
  260. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/tendon_attr/spatial_attr/site.py +0 -0
  261. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/visual.py +0 -0
  262. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/visual_attr/__init__.py +0 -0
  263. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/visual_attr/global_.py +0 -0
  264. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/visual_attr/headlight.py +0 -0
  265. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/visual_attr/map.py +0 -0
  266. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/visual_attr/quality.py +0 -0
  267. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/visual_attr/rgba.py +0 -0
  268. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/mujoco_attr/visual_attr/scale.py +0 -0
  269. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/plugin.py +0 -0
  270. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/pose_context.py +0 -0
  271. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mjcf/position.py +0 -0
  272. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/mojo_model.py +0 -0
  273. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/runtime/__init__.py +0 -0
  274. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/runtime/load.py +0 -0
  275. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/runtime/runtime_manager.py +0 -0
  276. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/runtime/signal_manager.py +0 -0
  277. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/settings.py +0 -0
  278. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/templates/__init__.py +0 -0
  279. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/templates/monte_carlo.py +0 -0
  280. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/templates/optimization.py +0 -0
  281. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/templates/reloaded.sh +0 -0
  282. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/templates/run_mc.sh +0 -0
  283. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/templates/run_opt.sh +0 -0
  284. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/__init__.py +0 -0
  285. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/color.py +0 -0
  286. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/dataframe.py +0 -0
  287. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/defaults.py +0 -0
  288. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/filters/__init__.py +0 -0
  289. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/filters/filters.py +0 -0
  290. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/interp.py +0 -0
  291. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/__init__.py +0 -0
  292. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/__init__.py +0 -0
  293. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/lab_executor.py +0 -0
  294. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/main.py +0 -0
  295. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/plot_config.py +0 -0
  296. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/routers/__init__.py +0 -0
  297. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/routers/monitor.py +0 -0
  298. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/routers/morph.py +0 -0
  299. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/routers/sensai.py +0 -0
  300. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/sensai/__init__.py +0 -0
  301. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/sensai/agent.py +0 -0
  302. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/shared.py +0 -0
  303. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/__init__.py +0 -0
  304. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/base.html +0 -0
  305. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/error.html +0 -0
  306. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/monitor.html +0 -0
  307. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/morph.html +0 -0
  308. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/mosaic.html +0 -0
  309. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/partials/_sensai.html +0 -0
  310. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/partials/trial_viewer/_chart.html +0 -0
  311. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/partials/trial_viewer/_header.html +0 -0
  312. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/partials/trial_viewer/_json_editor.html +0 -0
  313. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/partials/trial_viewer/_lab_trigger.html +0 -0
  314. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/partials/trial_viewer/_macros.html +0 -0
  315. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/partials/trial_viewer/_media_player.html +0 -0
  316. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/partials/trial_viewer/_overlays.html +0 -0
  317. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/partials/trial_viewer/_signal_lab.html +0 -0
  318. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/partials/trial_viewer/_trial_status.html +0 -0
  319. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/__init__.py +0 -0
  320. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/chime.mp3 +0 -0
  321. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/dark-logo.svg +0 -0
  322. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/js/__init__.py +0 -0
  323. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/js/main.js +0 -0
  324. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/js/monitor.js +0 -0
  325. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/js/mosaic.js +0 -0
  326. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/js/sensai.js +0 -0
  327. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/js/toast.d.ts +0 -0
  328. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/js/toast.d.ts.map +0 -0
  329. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/js/toast.js +0 -0
  330. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/light-logo.svg +0 -0
  331. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/main.css +0 -0
  332. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/build.mjs +0 -0
  333. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/package-lock.json +0 -0
  334. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/package.json +0 -0
  335. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/cm-bundle.ts +0 -0
  336. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/lib/options.ts +0 -0
  337. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/lib/plot-config.generated.ts +0 -0
  338. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/lib/resize.ts +0 -0
  339. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/lib/schema-validate.ts +0 -0
  340. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/lib/toast.ts +0 -0
  341. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/monitor.ts +0 -0
  342. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/mosaic.ts +0 -0
  343. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/sensai.ts +0 -0
  344. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/store.ts +0 -0
  345. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/src/types/global.d.ts +0 -0
  346. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/ts/tsconfig.json +0 -0
  347. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/vendored/__init__.py +0 -0
  348. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/vendored/alpine.min.js +0 -0
  349. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/vendored/codemirror.bundle.js +0 -0
  350. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/vendored/confetti.browser.min.js +0 -0
  351. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/vendored/iro.min.js +0 -0
  352. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/vendored/litegraph.min.css +0 -0
  353. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/vendored/litegraph.min.js +0 -0
  354. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/vendored/lz-string.min.js +0 -0
  355. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/vendored/plotly-3.4.0.min.js +0 -0
  356. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/static/vendored/tailwind.min.js +0 -0
  357. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/layers/dojo/templates/trial_viewer.html +0 -0
  358. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/log.py +0 -0
  359. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/proximity_mixin.py +0 -0
  360. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/statusing.py +0 -0
  361. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/utils/utils.py +0 -0
  362. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/src/mujoco_mojo/visualization.py +0 -0
  363. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/.gitignore +0 -0
  364. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/make_skybox.py +0 -0
  365. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/meshes/ball.stl +0 -0
  366. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/meshes/bunny2.stl +0 -0
  367. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/meshes/cup.stl +0 -0
  368. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/mjcf/body_test.py +0 -0
  369. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/mjcf/equality_test.py +0 -0
  370. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/mjcf/inertial_test.py +0 -0
  371. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/mjcf/joint_test.py +0 -0
  372. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/mjcf/mesh.py +0 -0
  373. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/mjcf/mujoco_test.py +0 -0
  374. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/mjcf/orientation_test.py +0 -0
  375. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/mjcf/pos_test.py +0 -0
  376. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/mjcf/pose_graph_test.py +0 -0
  377. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/mjcf/pose_test.py +0 -0
  378. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/mjcf/site_test.py +0 -0
  379. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/mjcf/visualization_test.py +0 -0
  380. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/mjcf/xml_model_test.py +0 -0
  381. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/model_with_skybox.xml +0 -0
  382. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/monte_carlo_test/asset_image.png +0 -0
  383. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/monte_carlo_test/monte_carlo.py +0 -0
  384. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/monte_carlo_test/overrides.json +0 -0
  385. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/monte_carlo_test/running_with_slurm.sh +0 -0
  386. {mujoco_mojo-2.4.2/src/mujoco_mojo/templates → mujoco_mojo-2.4.4/tests/one_off/proximity}/dojo.sh +0 -0
  387. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/one_off/proximity/reloaded.sh +0 -0
  388. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/one_off/proximity/run.sh +0 -0
  389. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/one_off/proximity/simulation.py +0 -0
  390. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/runtime/conftest.py +0 -0
  391. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/runtime/loads_test.py +0 -0
  392. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/runtime/mojo_model_test.py +0 -0
  393. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/runtime/optimizer_bumper.py +0 -0
  394. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/runtime/runtime_manager_test.py +0 -0
  395. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/runtime/settings_test.py +0 -0
  396. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/runtime/signal_manager_test.py +0 -0
  397. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/smoke_test.py +0 -0
  398. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/test_writer.py +0 -0
  399. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/utils/color_test.py +0 -0
  400. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/utils/dataframe_test.py +0 -0
  401. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/utils/filter_test.py +0 -0
  402. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/utils/interp_test.py +0 -0
  403. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/utils/load_func_test.py +0 -0
  404. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/tests/utils/utils_test.py +0 -0
  405. {mujoco_mojo-2.4.2 → mujoco_mojo-2.4.4}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mujoco-mojo
3
- Version: 2.4.2
3
+ Version: 2.4.4
4
4
  Summary: A complete MJCF lifecycle and trial orchestration suite for MuJoCo, powered by Pydantic v2.
5
5
  Project-URL: Homepage, https://github.com/Hydrowelder/mujoco-mojo
6
6
  Project-URL: Documentation, https://hydrowelder.github.io/mujoco-mojo/
@@ -51,7 +51,7 @@ Requires-Dist: rtree>=1.4.1
51
51
  Requires-Dist: scipy-stubs[scipy]>=1.17.0.2
52
52
  Requires-Dist: scipy>=1.17.0
53
53
  Requires-Dist: sse-starlette>=3.3.4
54
- Requires-Dist: stochas>=2.1.4
54
+ Requires-Dist: stochas>=2.1.6
55
55
  Requires-Dist: tabulate>=0.9.0
56
56
  Requires-Dist: tomli-w>=1.2.0
57
57
  Requires-Dist: trimesh>=4.11.2
@@ -27,7 +27,7 @@ dependencies = [
27
27
  "rich>=14.3.3",
28
28
  "scipy>=1.17.0",
29
29
  "scipy-stubs[scipy]>=1.17.0.2",
30
- "stochas>=2.1.4",
30
+ "stochas>=2.1.6",
31
31
  "sse-starlette>=3.3.4",
32
32
  "tabulate>=0.9.0",
33
33
  "trimesh>=4.11.2",
@@ -0,0 +1,650 @@
1
+ """
2
+ One-off script that cross-references the MJCF XML reference against the XMLModel
3
+ subclasses implemented in mujoco_mojo. By default it fetches the latest reference
4
+ directly from the MuJoCo docs; pass a local path to check against a specific copy
5
+ instead (e.g. a pinned XMLreference.rst.txt).
6
+
7
+ For every MJCF element it reports:
8
+ - whether mujoco_mojo implements a class for it (matched by `tag`)
9
+ - attributes documented in the spec but not covered by any matching class's
10
+ `attributes`/`non_xml_fields` (likely a missed field)
11
+ - attributes whose spec default doesn't match the Python field's default value
12
+ - attributes with no per-field docstring, or one that has drifted from the spec
13
+ - a class-level docstring similarity score, to flag descriptions that may have
14
+ drifted from the spec
15
+
16
+ This is advisory, not a hard gate: false positives are expected (the spec groups
17
+ several distinct elements, e.g. body/joint vs tendon/fixed/joint, under the same bare
18
+ tag name) and docstrings are deliberately reworded in places, so review the output
19
+ rather than treating it as pass/fail.
20
+
21
+ Reviewed, accepted findings can be silenced by adding them to `mjcf_ignore.txt`
22
+ (next to this script) -- see that file's header for the format. This only applies to
23
+ the "Spec elements with no matching implemented tag", "Implemented tags with no
24
+ matching spec element", "Attribute coverage gaps", "Default value mismatches", and
25
+ "Docstring similarity warnings" sections; "Attribute docstring warnings" always shows
26
+ everything, since per-field docstrings are easy to add and shouldn't be silenced.
27
+
28
+ Usage:
29
+ python scripts/check_mjcf_spec_coverage.py # fetch the spec live
30
+ python scripts/check_mjcf_spec_coverage.py path/to/XMLreference.rst.txt # use a local copy
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import ast
36
+ import functools
37
+ import inspect
38
+ import math
39
+ import re
40
+ import sys
41
+ import textwrap
42
+ from dataclasses import dataclass, field
43
+ from difflib import SequenceMatcher
44
+ from pathlib import Path
45
+ from typing import Literal, get_args, get_origin
46
+
47
+ import requests
48
+ from pydantic_core import PydanticUndefined
49
+ from rich.console import Console
50
+ from rich.markup import escape
51
+
52
+ SPEC_URL = "https://mujoco.readthedocs.io/en/stable/_sources/XMLreference.rst.txt"
53
+
54
+ IGNORE_FILE = Path(__file__).with_name("mjcf_ignore.txt")
55
+
56
+ # below this difflib ratio, a description is flagged as likely drifted from the spec
57
+ DOCSTRING_SIMILARITY_THRESHOLD = 0.2
58
+
59
+ # what's left of a spec attribute description after `_clean` strips a bare
60
+ # `:ref:`Something`` cross-reference with no other content (e.g. most sensors'
61
+ # shared name/noise/cutoff/... attributes just say "See CSensor."); comparing this
62
+ # placeholder against a real python docstring would always read as drift, so it's
63
+ # treated the same as having no spec description to compare against
64
+ SPEC_DESC_PLACEHOLDER = "See ."
65
+
66
+ # soft_wrap avoids breaking long file-path lines mid-string, which would make
67
+ # them harder to click through to from a terminal or IDE
68
+ console = Console(soft_wrap=True)
69
+
70
+ HEADER_RE = re.compile(
71
+ r"^(?:\:el-prefix:`(?P<prefix>\w+)/`\s*\|-\|\s*)?\*\*(?P<name>.+?)\*\*\s*\|[!@?*-]\|\s*$"
72
+ )
73
+ ANCHOR_RE = re.compile(r"^\.\. _[\w-]+:\s*$")
74
+ # plain RST subsection titles (e.g. "collision sensors") use the same `^^^^`/`~~~~` underline
75
+ # convention as real element headers but don't match HEADER_RE; without detecting them, `current`
76
+ # stays pinned to whatever element preceded them, and any shared `:at:` blocks inside get
77
+ # misattributed to that element instead of being ignored
78
+ GENERIC_UNDERLINE_RE = re.compile(r"^[\^~]{3,}\s*$")
79
+ ATTR_LINE_RE = re.compile(r"^:at:`(?P<rest>.+)`\s*(?::\s*:at-val:`.*`)?\s*$")
80
+ # only match names immediately preceded by the `:at:` role marker, so prose lines that
81
+ # happen to start with `:at:`something`` but reference other roles later (e.g. `:el:`general``)
82
+ # don't get misread as also declaring an attribute named "general"
83
+ AT_NAME_RE = re.compile(r":at:`(\w+)`")
84
+ RST_NOISE_RE = re.compile(
85
+ r":(?:ref|el|at|at-val|el-prefix|math):`[^`]*`|``|\|br\||\.\. .*"
86
+ )
87
+
88
+ # attribute names that get merged into a single python field via PoseBase/OrientationBase: `pose`
89
+ # (position + orientation) on most elements, or a bare `orientation` field where pos is separate
90
+ # (e.g. Inertial, which has its own `pos` field alongside `orientation`)
91
+ ORIENTATION_GROUP = {"pos", "quat", "axisangle", "xyaxes", "zaxis", "euler"}
92
+ ORIENTATION_COVERING_FIELDS = {"pose", "orientation"}
93
+
94
+
95
+ @dataclass
96
+ class SpecAttr:
97
+ description: str = ""
98
+ # raw text trailing the first top-level comma in the `:at-val:` body, e.g.
99
+ # '"0 0 1"', "optional", "required", or unparsed prose for the rare multi-clause cases
100
+ default_raw: str = ""
101
+
102
+
103
+ @dataclass
104
+ class SpecElement:
105
+ name: str
106
+ description: str = ""
107
+ attrs: dict[str, SpecAttr] = field(default_factory=dict)
108
+
109
+
110
+ def _clean(text: str) -> str:
111
+ text = RST_NOISE_RE.sub(" ", text)
112
+ return re.sub(r"\s+", " ", text).strip()
113
+
114
+
115
+ def _expand_names(raw_name: str) -> list[str]:
116
+ m = re.match(r"\((\w+)\)(\w+)$", raw_name)
117
+ if m:
118
+ return [m.group(2), m.group(1) + m.group(2)]
119
+ return [raw_name]
120
+
121
+
122
+ def _parse_attr_default(val_text: str) -> str:
123
+ """
124
+ Splits an `:at-val:` body like `[false, true], "false"` or `real(3), "0 0 0"` into
125
+ its trailing default-value text, by finding the first top-level comma (ignoring
126
+ commas nested inside the type spec's brackets/parens, e.g. `[false, true]`).
127
+
128
+ Returns "" if no top-level comma is found (e.g. shared `:at:` lines whose `:at-val:`
129
+ is empty or absent).
130
+ """
131
+ depth = 0
132
+ for i, ch in enumerate(val_text):
133
+ if ch in "[(":
134
+ depth += 1
135
+ elif ch in "])":
136
+ depth -= 1
137
+ elif ch == "," and depth == 0:
138
+ return val_text[i + 1 :].strip()
139
+ return ""
140
+
141
+
142
+ def parse_spec(text: str) -> list[SpecElement]:
143
+ lines = text.splitlines()
144
+ elements: list[SpecElement] = []
145
+ # a header can expand to more than one name (e.g. "(world)body" -> "body" and
146
+ # "worldbody"); every name in the group shares the same attrs/description, since
147
+ # they're documenting the same element under different aliases
148
+ current_group: list[SpecElement] = []
149
+ pending_attr_names: list[str] = []
150
+ pending_default_raw = ""
151
+ buffer: list[str] = []
152
+
153
+ def flush_attr() -> None:
154
+ if current_group and pending_attr_names:
155
+ desc = _clean(" ".join(buffer))
156
+ for el in current_group:
157
+ for name in pending_attr_names:
158
+ el.attrs[name] = SpecAttr(
159
+ description=desc, default_raw=pending_default_raw
160
+ )
161
+
162
+ def flush_description() -> None:
163
+ # first non-empty buffer wins; later prose between attribute blocks (e.g. trailing
164
+ # notes before the next anchor) shouldn't clobber the element's intro description
165
+ if current_group and not pending_attr_names and buffer:
166
+ desc = _clean(" ".join(buffer))
167
+ for el in current_group:
168
+ if not el.description:
169
+ el.description = desc
170
+
171
+ i = 0
172
+ while i < len(lines):
173
+ line = lines[i]
174
+ header = HEADER_RE.match(line)
175
+ if header:
176
+ flush_attr()
177
+ flush_description()
178
+ pending_attr_names = []
179
+ pending_default_raw = ""
180
+ buffer = []
181
+
182
+ current_group = []
183
+ for name in _expand_names(header.group("name")):
184
+ el = SpecElement(name=name)
185
+ elements.append(el)
186
+ current_group.append(el)
187
+ i += 1
188
+ continue
189
+
190
+ if (
191
+ line.strip()
192
+ and i + 1 < len(lines)
193
+ and GENERIC_UNDERLINE_RE.match(lines[i + 1])
194
+ and not ANCHOR_RE.match(line)
195
+ and not ATTR_LINE_RE.match(line)
196
+ ):
197
+ flush_attr()
198
+ flush_description()
199
+ current_group = []
200
+ pending_attr_names = []
201
+ pending_default_raw = ""
202
+ buffer = []
203
+ i += 2
204
+ continue
205
+
206
+ if ANCHOR_RE.match(line):
207
+ flush_attr()
208
+ flush_description()
209
+ pending_attr_names = []
210
+ pending_default_raw = ""
211
+ buffer = []
212
+ i += 1
213
+ continue
214
+
215
+ attr_match = ATTR_LINE_RE.match(line)
216
+ if attr_match:
217
+ flush_attr()
218
+ # only look for `name` tokens before ":at-val:" so type/default values
219
+ # (e.g. :at-val:`string`) aren't mistaken for additional attribute names
220
+ name_part, _, val_part = line.partition(":at-val:")
221
+ pending_attr_names = AT_NAME_RE.findall(name_part)
222
+ # val_part looks like "`real(3), "0 0 1"`" when an :at-val: is present,
223
+ # or "" when this line only lists attribute names with no value of its own
224
+ # (e.g. a line that just cross-references attributes documented elsewhere)
225
+ val_text = val_part.strip()
226
+ if val_text.startswith("`") and val_text.endswith("`"):
227
+ val_text = val_text[1:-1]
228
+ pending_default_raw = _parse_attr_default(val_text)
229
+ buffer = []
230
+ i += 1
231
+ continue
232
+
233
+ buffer.append(line)
234
+ i += 1
235
+
236
+ flush_attr()
237
+ flush_description()
238
+ # keep every real header match, even ones with zero attributes of their own (e.g.
239
+ # pure container elements like <actuator>/<asset>, or elements like actuator/motor
240
+ # whose attributes are documented by reference under a shared element instead of
241
+ # being restated) -- dropping them caused them to be falsely reported as unimplemented
242
+ return elements
243
+
244
+
245
+ def merge_by_bare_name(elements: list[SpecElement]) -> dict[str, SpecElement]:
246
+ merged: dict[str, SpecElement] = {}
247
+ for el in elements:
248
+ bucket = merged.setdefault(el.name, SpecElement(name=el.name))
249
+ bucket.attrs.update(el.attrs)
250
+ if not bucket.description:
251
+ bucket.description = el.description
252
+ return merged
253
+
254
+
255
+ def normalize_field_name(name: str) -> str:
256
+ if name.endswith("_") and len(name) > 1:
257
+ return name[:-1]
258
+ return name
259
+
260
+
261
+ def load_ignore_file(path: Path = IGNORE_FILE) -> dict[str, set[str]]:
262
+ """
263
+ Parses `mjcf_ignore.txt`-style lines of the form `<category>:<key>` into
264
+ {category: {key, ...}}. Blank lines and lines starting with `#` are skipped.
265
+ Missing file just means nothing is ignored.
266
+ """
267
+ ignored: dict[str, set[str]] = {}
268
+ if not path.exists():
269
+ return ignored
270
+ for line in path.read_text(encoding="utf-8").splitlines():
271
+ line = line.strip()
272
+ if not line or line.startswith("#"):
273
+ continue
274
+ category, _, key = line.partition(":")
275
+ ignored.setdefault(category, set()).add(key)
276
+ return ignored
277
+
278
+
279
+ @functools.cache
280
+ def _extract_field_docstrings(cls: type) -> dict[str, str]:
281
+ """
282
+ Parses the class's source to recover the bare string-literal "docstrings" this
283
+ project writes directly after each field assignment. These are a convention for
284
+ human/doc-tool readers only -- the interpreter discards them as statement
285
+ expressions, so they aren't reachable via normal runtime introspection.
286
+
287
+ Only looks at `cls`'s own body; fields inherited from a base class (e.g. the
288
+ `name`/`noise`/`cutoff`/... fields most sensor subclasses get from `SensorBase`)
289
+ are not found here -- see `_field_docstrings_for_class` for the MRO-aware version.
290
+ """
291
+ try:
292
+ source = inspect.getsource(cls)
293
+ except (OSError, TypeError):
294
+ return {}
295
+ try:
296
+ tree = ast.parse(textwrap.dedent(source))
297
+ except SyntaxError:
298
+ return {}
299
+ if not tree.body or not isinstance(tree.body[0], ast.ClassDef):
300
+ return {}
301
+
302
+ body = tree.body[0].body
303
+ docs: dict[str, str] = {}
304
+ for i, node in enumerate(body):
305
+ name = None
306
+ if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
307
+ name = node.target.id
308
+ elif (
309
+ isinstance(node, ast.Assign)
310
+ and len(node.targets) == 1
311
+ and isinstance(node.targets[0], ast.Name)
312
+ ):
313
+ name = node.targets[0].id
314
+ if name is None or i + 1 >= len(body):
315
+ continue
316
+ following = body[i + 1]
317
+ if (
318
+ isinstance(following, ast.Expr)
319
+ and isinstance(following.value, ast.Constant)
320
+ and isinstance(following.value.value, str)
321
+ ):
322
+ docs[name] = following.value.value
323
+ return docs
324
+
325
+
326
+ def _field_docstrings_for_class(cls: type) -> dict[str, str]:
327
+ """
328
+ Merges `_extract_field_docstrings` across the whole MRO, so fields defined on a
329
+ base class (e.g. `SensorBase.name`) are attributed to every concrete subclass that
330
+ inherits them. Walking base-to-derived means a subclass's own docstring for a
331
+ field wins over an inherited one of the same name, if it redefines it.
332
+ """
333
+ docs: dict[str, str] = {}
334
+ for base in reversed(cls.__mro__):
335
+ docs.update(_extract_field_docstrings(base))
336
+ return docs
337
+
338
+
339
+ def collect_xml_model_classes():
340
+ import mujoco_mojo # noqa: F401
341
+ from mujoco_mojo.mjcf.xml_model import XMLModel
342
+
343
+ seen: set[type] = set()
344
+ stack = [XMLModel]
345
+ classes: list[type] = []
346
+ while stack:
347
+ cls = stack.pop()
348
+ for sub in cls.__subclasses__():
349
+ if sub not in seen:
350
+ seen.add(sub)
351
+ classes.append(sub)
352
+ stack.append(sub)
353
+ return classes
354
+
355
+
356
+ def _class_location(cls: type) -> str:
357
+ try:
358
+ file = inspect.getsourcefile(cls)
359
+ _, line = inspect.getsourcelines(cls)
360
+ except (OSError, TypeError):
361
+ return "<unknown location>"
362
+ if not file:
363
+ return "<unknown location>"
364
+ # show a path relative to the cwd when the file is underneath it, so the
365
+ # output stays clickable/short when run from within the repo
366
+ path = Path(file)
367
+ try:
368
+ path = path.relative_to(Path.cwd())
369
+ except ValueError:
370
+ pass
371
+ return f"{path}:{line}"
372
+
373
+
374
+ def build_python_registry() -> dict[str, dict]:
375
+ registry: dict[str, dict] = {}
376
+ for cls in collect_xml_model_classes():
377
+ tag = getattr(cls, "tag", "")
378
+ if not tag:
379
+ continue
380
+ bucket = registry.setdefault(
381
+ tag,
382
+ {
383
+ "classes": [],
384
+ "covered": set(),
385
+ "docstrings": [],
386
+ # field name -> list of per-class docstrings (collected even for
387
+ # fields outside `attributes`/`non_xml_fields`; harmless since
388
+ # only the ones matching a spec attribute name are ever read)
389
+ "field_docs": {},
390
+ # fields any class declares as a single-value `Literal[...]`
391
+ # discriminator (e.g. MeshSphere.builtin = Literal["sphere"]); these
392
+ # are fixed tag values, not documentable XML attributes, so they're
393
+ # exempt from the missing-docstring check below. A multi-value Literal
394
+ # (e.g. `condim: Literal[1, 3, 4, 6]`) is a real constrained attribute
395
+ # and still needs a docstring, so it doesn't count here
396
+ "literal_fields": set(),
397
+ },
398
+ )
399
+ bucket["classes"].append((cls.__name__, _class_location(cls), cls))
400
+ names = {
401
+ normalize_field_name(n) for n in (*cls.attributes, *cls.non_xml_fields)
402
+ }
403
+ bucket["covered"] |= names
404
+ if cls.__doc__:
405
+ bucket["docstrings"].append(cls.__doc__)
406
+ for raw_name, doc in _field_docstrings_for_class(cls).items():
407
+ bucket["field_docs"].setdefault(normalize_field_name(raw_name), []).append(
408
+ doc
409
+ )
410
+ for raw_name, field_info in cls.model_fields.items():
411
+ annotation = field_info.annotation
412
+ if get_origin(annotation) is Literal and len(get_args(annotation)) == 1:
413
+ bucket["literal_fields"].add(normalize_field_name(raw_name))
414
+ return registry
415
+
416
+
417
+ def _python_field_default(cls: type, field_name: str) -> tuple[str, str | None]:
418
+ """
419
+ Classifies a Python field's default into ("required", None) when no default is
420
+ set, ("optional", None) when the default is None, ("composite", None) when the
421
+ default is a nested XMLModel (e.g. `pos: Pos = Pos(...)`, flattened into XML
422
+ attributes by a separate code path that's out of scope for this comparison),
423
+ ("discriminator", formatted_value) when the field is a single-value `Literal[...]`
424
+ (e.g. `type: Literal[GeomType.SPHERE]` on a discriminated geom subclass -- its
425
+ fixed value isn't meant to match the spec's generic default for the merged
426
+ element), or ("literal", formatted_value) otherwise, formatted the same way
427
+ `XMLModel.to_xml` formats it for the real XML output.
428
+ """
429
+ from mujoco_mojo.mjcf.xml_model import XMLModel, _format_value
430
+
431
+ field_info = cls.model_fields[field_name]
432
+ default = field_info.get_default(call_default_factory=True)
433
+ if default is PydanticUndefined:
434
+ return "required", None
435
+ if default is None:
436
+ return "optional", None
437
+ if isinstance(default, XMLModel):
438
+ return "composite", None
439
+ formatted = _format_value(default)
440
+ annotation = field_info.annotation
441
+ if get_origin(annotation) is Literal and len(get_args(annotation)) == 1:
442
+ return "discriminator", formatted
443
+ return "literal", formatted
444
+
445
+
446
+ def _spec_attr_default(spec_attr: SpecAttr) -> tuple[str, str | None]:
447
+ """Mirrors `_python_field_default`'s (kind, value) shape for the spec side."""
448
+ stripped = spec_attr.default_raw.strip()
449
+ if stripped == "required":
450
+ return "required", None
451
+ if stripped == "optional":
452
+ return "optional", None
453
+ match = re.match(r'^"([^"]*)"', stripped)
454
+ if match:
455
+ return "literal", match.group(1)
456
+ # no `:at-val:` at all, or prose too irregular to parse as a literal/optional/required
457
+ return "unparsed", None
458
+
459
+
460
+ def _parse_float_list(text: str) -> list[float] | None:
461
+ try:
462
+ return [float(tok) for tok in text.split()]
463
+ except ValueError:
464
+ return None
465
+
466
+
467
+ def _literals_match(spec_value: str, python_value: str) -> bool:
468
+ if spec_value == python_value:
469
+ return True
470
+ # numeric defaults can be formatted differently (e.g. spec "1" vs python "1.0")
471
+ # while still being the same value; compare as numbers when both sides parse
472
+ spec_floats = _parse_float_list(spec_value)
473
+ python_floats = _parse_float_list(python_value)
474
+ if spec_floats is None or python_floats is None:
475
+ return False
476
+ return len(spec_floats) == len(python_floats) and all(
477
+ math.isclose(a, b, abs_tol=1e-9) for a, b in zip(spec_floats, python_floats)
478
+ )
479
+
480
+
481
+ def _default_mismatch(spec_attr: SpecAttr, cls: type, field_name: str) -> str | None:
482
+ """Returns a human-readable mismatch description, or None if the default checks out."""
483
+ spec_kind, spec_value = _spec_attr_default(spec_attr)
484
+ if spec_kind == "unparsed":
485
+ return None
486
+
487
+ python_kind, python_value = _python_field_default(cls, field_name)
488
+ if python_kind in ("composite", "discriminator"):
489
+ return None
490
+
491
+ if spec_kind == python_kind == "required":
492
+ return None
493
+ if spec_kind == python_kind == "optional":
494
+ return None
495
+ if spec_kind == "literal" and python_kind == "literal":
496
+ assert spec_value is not None and python_value is not None
497
+ if _literals_match(spec_value, python_value):
498
+ return None
499
+ return f'spec default "{spec_value}" != python default "{python_value}"'
500
+
501
+ python_desc = f' ("{python_value}")' if python_value is not None else ""
502
+ return f"spec marks attribute as {spec_kind} but python default is {python_kind}{python_desc}"
503
+
504
+
505
+ def _print_classes(classes: list[tuple[str, str, type]]) -> None:
506
+ for name, location, *_ in classes:
507
+ console.print(
508
+ f" [bold cyan]{escape(name)}[/bold cyan] [dim]{escape(location)}[/dim]"
509
+ )
510
+
511
+
512
+ def main() -> None:
513
+ if len(sys.argv) > 1:
514
+ text = Path(sys.argv[1]).read_text(encoding="utf-8")
515
+ else:
516
+ response = requests.get(SPEC_URL)
517
+ response.raise_for_status()
518
+ text = response.text
519
+
520
+ spec_elements = parse_spec(text)
521
+ spec_by_name = merge_by_bare_name(spec_elements)
522
+ python_registry = build_python_registry()
523
+ ignored = load_ignore_file()
524
+
525
+ spec_names = set(spec_by_name)
526
+ python_tags = set(python_registry)
527
+
528
+ unimplemented = sorted(
529
+ n
530
+ for n in (spec_names - python_tags)
531
+ if n not in ignored.get("unimplemented", ())
532
+ )
533
+ untagged = sorted(
534
+ n for n in (python_tags - spec_names) if n not in ignored.get("untagged", ())
535
+ )
536
+
537
+ console.rule(
538
+ f"[bold]Spec elements with no matching implemented tag[/bold] ({len(unimplemented)})"
539
+ )
540
+ for name in unimplemented:
541
+ console.print(f" [yellow]{escape(name)}[/yellow]")
542
+
543
+ console.print()
544
+ console.rule(
545
+ f"[bold]Implemented tags with no matching spec element[/bold] ({len(untagged)})"
546
+ )
547
+ for name in untagged:
548
+ console.print(f" [yellow]{escape(name)}[/yellow]:")
549
+ _print_classes(python_registry[name]["classes"])
550
+
551
+ console.print()
552
+ console.rule(
553
+ "[bold]Attribute coverage gaps[/bold] (spec attributes not covered by attributes/non_xml_fields)"
554
+ )
555
+ for name in sorted(spec_names & python_tags):
556
+ spec_el = spec_by_name[name]
557
+ covered = python_registry[name]["covered"]
558
+ missing = set()
559
+ for attr in spec_el.attrs:
560
+ if attr in covered:
561
+ continue
562
+ if attr in ORIENTATION_GROUP and covered & ORIENTATION_COVERING_FIELDS:
563
+ continue
564
+ if f"{name}.{attr}" in ignored.get("coverage_gap", ()):
565
+ continue
566
+ missing.add(attr)
567
+ if missing:
568
+ console.print(
569
+ f" [red]{escape(name)}[/red]: missing [bold yellow]{escape(str(sorted(missing)))}[/bold yellow]"
570
+ )
571
+ _print_classes(python_registry[name]["classes"])
572
+
573
+ console.print()
574
+ console.rule(
575
+ "[bold]Default value mismatches[/bold] (spec default doesn't match the Python field default)"
576
+ )
577
+ for name in sorted(spec_names & python_tags):
578
+ spec_el = spec_by_name[name]
579
+ for _, location, cls in python_registry[name]["classes"]:
580
+ for field_name in cls.attributes:
581
+ attr_name = normalize_field_name(field_name)
582
+ if f"{name}.{attr_name}" in ignored.get("default_mismatch", ()):
583
+ continue
584
+ spec_attr = spec_el.attrs.get(attr_name)
585
+ if spec_attr is None:
586
+ continue
587
+ mismatch = _default_mismatch(spec_attr, cls, field_name)
588
+ if mismatch:
589
+ console.print(
590
+ f" [red]{escape(name)}.{escape(attr_name)}[/red]: {escape(mismatch)}"
591
+ )
592
+ console.print(
593
+ f" [bold cyan]{escape(cls.__name__)}[/bold cyan] [dim]{escape(location)}[/dim]"
594
+ )
595
+
596
+ console.print()
597
+ console.rule(
598
+ "[bold]Attribute docstring warnings[/bold] (missing per-field docstring, or drifted from the spec)"
599
+ )
600
+ for name in sorted(spec_names & python_tags):
601
+ spec_el = spec_by_name[name]
602
+ covered = python_registry[name]["covered"]
603
+ field_docs = python_registry[name]["field_docs"]
604
+ literal_fields = python_registry[name]["literal_fields"]
605
+ for attr_name, spec_attr in spec_el.attrs.items():
606
+ if attr_name not in covered:
607
+ continue # already reported as a coverage gap above
608
+ if attr_name in ORIENTATION_GROUP and covered & ORIENTATION_COVERING_FIELDS:
609
+ continue # merged into a `pose`/`orientation` field with no 1:1 docstring
610
+ if attr_name in literal_fields:
611
+ continue # fixed discriminator value, not a documentable attribute
612
+ # dedupe before joining: a field defined once on a shared base class (e.g.
613
+ # GeomBase.name) is re-collected for every subclass sharing this tag, and
614
+ # comparing a short spec description against N copies of the same text
615
+ # would deflate the similarity ratio for no real reason
616
+ py_doc = _clean(" ".join(dict.fromkeys(field_docs.get(attr_name, []))))
617
+ if not py_doc:
618
+ console.print(
619
+ f" [magenta]{escape(name)}.{escape(attr_name)}[/magenta]: no docstring found"
620
+ )
621
+ continue
622
+ if spec_attr.description in ("", SPEC_DESC_PLACEHOLDER):
623
+ continue
624
+ ratio = SequenceMatcher(None, spec_attr.description, py_doc).ratio()
625
+ if ratio < DOCSTRING_SIMILARITY_THRESHOLD:
626
+ console.print(
627
+ f" [magenta]{escape(name)}.{escape(attr_name)}[/magenta]: similarity=[bold]{ratio:.2f}[/bold]"
628
+ )
629
+
630
+ console.print()
631
+ console.rule(
632
+ "[bold]Docstring similarity warnings[/bold] (ratio < 0.2, review for drift)"
633
+ )
634
+ for name in sorted(spec_names & python_tags):
635
+ if name in ignored.get("docstring_class", ()):
636
+ continue
637
+ spec_desc = spec_by_name[name].description
638
+ py_desc = _clean(" ".join(dict.fromkeys(python_registry[name]["docstrings"])))
639
+ if not spec_desc or not py_desc:
640
+ continue
641
+ ratio = SequenceMatcher(None, spec_desc, py_desc).ratio()
642
+ if ratio < DOCSTRING_SIMILARITY_THRESHOLD:
643
+ console.print(
644
+ f" [magenta]{escape(name)}[/magenta]: similarity=[bold]{ratio:.2f}[/bold]"
645
+ )
646
+ _print_classes(python_registry[name]["classes"])
647
+
648
+
649
+ if __name__ == "__main__":
650
+ main()