diff --git a/protomotions/simulator/newton/simulator.py b/protomotions/simulator/newton/simulator.py index 1077048ea..6ca5d49cf 100644 --- a/protomotions/simulator/newton/simulator.py +++ b/protomotions/simulator/newton/simulator.py @@ -367,7 +367,7 @@ def _setup_robot(self) -> None: self.robot_view = ArticulationView( self.model, pattern="robot", - include_joints=self._newton_dof_names.keys(), + include_joints=list(self._newton_dof_names.keys()), include_links=self._body_names, ) diff --git a/protomotions/tests/test_newton_python3_compat.py b/protomotions/tests/test_newton_python3_compat.py new file mode 100644 index 000000000..cab4b8005 --- /dev/null +++ b/protomotions/tests/test_newton_python3_compat.py @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +import ast +from pathlib import Path + + +NEWTON_SIMULATOR = ( + Path(__file__).resolve().parents[1] / "simulator" / "newton" / "simulator.py" +) + + +def test_newton_articulation_view_receives_joint_name_sequence(): + tree = ast.parse(NEWTON_SIMULATOR.read_text(), filename=str(NEWTON_SIMULATOR)) + include_joints_values = [ + keyword.value + for node in ast.walk(tree) + if isinstance(node, ast.Call) + and isinstance(node.func, ast.Name) + and node.func.id == "ArticulationView" + for keyword in node.keywords + if keyword.arg == "include_joints" + ] + + assert len(include_joints_values) == 1 + value = include_joints_values[0] + assert isinstance(value, ast.Call) + assert isinstance(value.func, ast.Name) + assert value.func.id == "list" diff --git a/protomotions/train_agent.py b/protomotions/train_agent.py index 0eca32d9c..b091dd063 100644 --- a/protomotions/train_agent.py +++ b/protomotions/train_agent.py @@ -290,6 +290,10 @@ def create_parser(): # Parse arguments first (argparse is safe, doesn't import torch) import argparse # noqa: E402 +import faulthandler # noqa: E402 + +faulthandler.enable() + from protomotions.utils.cli_utils import parse_bool # noqa: E402 parser = create_parser() @@ -304,6 +308,9 @@ def create_parser(): # Now safe to import everything else including torch from pathlib import Path # noqa: E402 import logging # noqa: E402 + +logging.basicConfig(level=logging.INFO, format="%(levelname)s:%(name)s: %(message)s") + from protomotions.utils.hydra_replacement import get_class # noqa: E402 import importlib.util # noqa: E402 import shutil # noqa: E402