Refactor compiler warning handling.
Compiler warnings are now accumulated in a vector of strings within the mjSpec object. New API functions `mjs_numWarnings` and `mjs_getWarning` are added to access these warnings. The compiler's log handler now chains warnings to the global log handler, ensuring they are still displayed immediately. Call sites in `mj_loadXML`, `mj_compile`, and the Python and WASM bindings have been updated to use the new warning API. PiperOrigin-RevId: 933361650 Change-Id: I47cab98a460c57b0898c0a1a43fce2a5b9648eb1
This commit is contained in:
committed by
Copybara-Service
parent
55c6332f20
commit
6f8bb5ef55
@@ -6486,7 +6486,41 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
|
||||
),
|
||||
),
|
||||
),
|
||||
doc='Return 1 if compiler error is a warning.',
|
||||
doc='Return 1 if compiler error is a warning. Deprecated: use mjs_numWarnings(s) > 0.', # pylint: disable=line-too-long
|
||||
)),
|
||||
('mjs_numWarnings',
|
||||
FunctionDecl(
|
||||
name='mjs_numWarnings',
|
||||
return_type=ValueType(name='int'),
|
||||
parameters=(
|
||||
FunctionParameterDecl(
|
||||
name='spec',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjSpec', is_const=True),
|
||||
),
|
||||
),
|
||||
),
|
||||
doc='Get number of warnings accumulated in the spec.',
|
||||
)),
|
||||
('mjs_getWarning',
|
||||
FunctionDecl(
|
||||
name='mjs_getWarning',
|
||||
return_type=PointerType(
|
||||
inner_type=ValueType(name='char', is_const=True),
|
||||
),
|
||||
parameters=(
|
||||
FunctionParameterDecl(
|
||||
name='spec',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjSpec', is_const=True),
|
||||
),
|
||||
),
|
||||
FunctionParameterDecl(
|
||||
name='index',
|
||||
type=ValueType(name='int'),
|
||||
),
|
||||
),
|
||||
doc='Get the i-th warning message (returns nullptr if index out of bounds).', # pylint: disable=line-too-long
|
||||
)),
|
||||
('mju_zero3',
|
||||
FunctionDecl(
|
||||
|
||||
@@ -597,6 +597,18 @@ class SpecsTest(absltest.TestCase):
|
||||
with self.assertRaisesRegex(ValueError, expected_error):
|
||||
spec.compile()
|
||||
|
||||
def test_compile_warnings(self):
|
||||
xml = """
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<flexcomp name="my_flex" type="grid" count="3 3 1" spacing=".05 .05 .05" radius=".01" dim="2"/>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
"""
|
||||
spec = mujoco.MjSpec.from_string(xml)
|
||||
with self.assertWarnsRegex(UserWarning, 'is not rigid'):
|
||||
spec.compile()
|
||||
|
||||
def test_recompile(self):
|
||||
# Create a spec.
|
||||
spec = mujoco.MjSpec()
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "errors.h"
|
||||
#include "indexers.h" // IWYU pragma: keep
|
||||
#include "private.h"
|
||||
#include "raw.h"
|
||||
#include "structs.h" // IWYU pragma: keep
|
||||
#include <pybind11/cast.h>
|
||||
@@ -118,7 +119,14 @@ raw::MjModel* MjSpec::Compile(mjVFS* vfs) {
|
||||
raw::MjModel* m;
|
||||
{
|
||||
py::gil_scoped_release no_gil;
|
||||
|
||||
// Install a no-op handler to suppress stderr output from warnings.
|
||||
// Compile() installs its own (setjmp/longjmp) handler and then chains to
|
||||
// prev. We want to raise a `warnings.warn`, so pass in a no-op handler.
|
||||
mjfLogHandler prev =
|
||||
_mjPRIVATE_setTlsLogHandler([](const mjLogMessage*) {});
|
||||
m = mj_compile(ptr, vfs);
|
||||
_mjPRIVATE_setTlsLogHandler(prev);
|
||||
}
|
||||
|
||||
if (local_vfs.has_value()) {
|
||||
@@ -127,9 +135,17 @@ raw::MjModel* MjSpec::Compile(mjVFS* vfs) {
|
||||
local_vfs = std::nullopt;
|
||||
}
|
||||
|
||||
if (!m || mjs_isWarning(ptr)) {
|
||||
if (!m) {
|
||||
throw py::value_error(mjs_getError(ptr));
|
||||
}
|
||||
|
||||
int num_warnings = mjs_numWarnings(ptr);
|
||||
if (num_warnings > 0) {
|
||||
py::object warnings = py::module_::import("warnings");
|
||||
for (int i = 0; i < num_warnings; ++i) {
|
||||
warnings.attr("warn")(mjs_getWarning(ptr, i));
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user