Support OVPhysX 0.5.11 and 0.6 lifecycle APIs - #7551
Conversation
OVPhysX 0.6 renames the GPU warmup entry point to warmup() and extends it to CPU mode, while the released 0.5 line still exposes warmup_gpu(). Route the manager through a small compatibility helper that prefers warmup(), falls back to warmup_gpu(), and raises AttributeError when neither exists, so the same source runs against both runtime generations. The CPU regression test now spies on the compatibility helper rather than on warmup_gpu, which keeps it independent of the installed OVPhysX generation.
OVPhysX 0.6 makes destroy() the only runtime lifecycle entry point; the released 0.5 line exposes release(). Calling release() unconditionally against a current runtime raises after views and the stage have been drained, but before the native instance and process-lifecycle ownership are released. Resolve the entry point the same way _warmup_physx does: prefer the current API, fall back to the legacy one, and report both names if neither is present. Keep manager ownership until current destruction succeeds or reaches a terminal state, so an invocation failure before native teardown can be retried. This keeps a single checkout working against both runtime generations.
Greptile SummaryThe PR adds version-selected OVPhysX 0.5.11/0.6 lifecycle dispatch and preserves runtime ownership when 0.6 destruction fails before teardown. It also initializes actuator-resolved soft velocity limits during collection construction and updates the associated tests, documentation, and changelogs.
Confidence Score: 5/5The PR appears safe to merge; no concrete blocking or independently actionable non-blocking defects were identified. The lifecycle routing, destruction ownership handling, and construction-time actuator-limit initialization are internally consistent with their covered runtime paths, and the investigated edge cases did not establish a reachable failure. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Import compatibility layer] --> B{Installed OVPhysX version >= 0.6?}
B -- Yes --> C[warmup / destroy]
B -- No or unknown --> D[warmup_gpu / release]
C --> E[Initialize and run simulation]
D --> E
E --> F[Close manager]
F --> G{Destroy completed or handle is terminal?}
G -- Yes --> H[Release PhysX and OVStage owners]
G -- No --> I[Preserve owners for retry]
Reviews (1): Last reviewed commit: "Select OVPhysX lifecycle APIs by install..." | Re-trigger Greptile |
There was a problem hiding this comment.
Isaac Lab Review Bot
The version-selected OVPhysX lifecycle mapping and actuator soft-limit initialization are coherent, but the new retry-oriented teardown behavior unintentionally changes failure handling for the pinned OVPhysX 0.5.11 release path.
- Design and architecture: Resolving immutable lifecycle entry points once at import cleanly isolates OVPhysX 0.5.11/0.6 compatibility. However,
_release_physxapplies the 0.6-specific owner-retention policy to both lifecycle generations, changing established 0.5.11 cleanup semantics whenrelease()raises. Retention should be gated to the 0.6destroy()path. - API: The compatibility helpers are documented and internally consistent, dependency pins remain unchanged, and the actuator velocity-limit documentation matches the implemented resolution behavior. No public API compatibility issue was identified.
- Implementation: Per-group soft joint velocity limits are initialized for Lab and Newton-managed actuator paths, including unbounded remotized actuators. Teardown still attempts the selected lifecycle method, but a 0.5.11
release()failure can now leave_physxand_ovstageretained whileclose()resets the rest of the manager state; preserve the prior legacy cleanup behavior and restrict retry retention to the 0.6 lifecycle.
Minor fixes needed. Posted 1 actionable finding inline.
Automated review; human maintainers own approval decisions.
| finally: | ||
| physx.release() | ||
| cls._destroy_physx(physx) | ||
| except Exception: |
There was a problem hiding this comment.
🟡 Warning · Design Architecture — Destroy-failure retention not gated on 0.6 lifecycle
This retain-on-failure policy also governs the pinned 0.5.11 release() path. Previously cls._physx was cleared and _destroy_ovstage() ran unconditionally. Now, if release() raises and the handle probe does not raise RuntimeError (e.g. AttributeError on a 0.5.11 object), destroyed stays false: the OVStage leaks and close() returns with cls._physx still set while all other manager state is reset. Gate retention on the 0.6 lifecycle selection.
There was a problem hiding this comment.
Addressed in 5cd8349. The legacy release() path now preserves its prior unconditional owner cleanup, while handle probing and retry retention are gated to the selected OVPhysX 0.6 destroy() path. I added a regression test that fails on the reviewed commit and passes with this fix.
d3b8778 to
5cd8349
Compare
|
run-ci |
| assert detect_ovphysx_version() is None | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( |
There was a problem hiding this comment.
🤖 AI-assisted suggestion (non-blocking): Could we consolidate this classifier table and the mapping tests below into one parameterized test of the final lifecycle mapping?
uses_current_lifecycle_api() is exercised again through build_lifecycle_entry_points(), so separately asserting both layers duplicates the same < 0.6 decision. One table covering None, 0.5.11, 0.6, a local 0.6 build, and 1.0 would preserve fallback, boundary, local-version, and forward-compatibility coverage.
I would keep the three metadata-detection tests and the read-only mapping test, but remove test_installed_entry_points_match_the_installed_version(), since it largely restates the module initializer. This would reduce this file from 14 collected cases to about 9 while still testing the observable compatibility contract.
There was a problem hiding this comment.
Addressed in 2200d71. The classifier and mapping coverage is now one parameterized test of the final lifecycle mapping, and the initializer-restatement test was removed. The file now collects 9 cases while retaining metadata, fallback, boundary, prerelease/local-build, forward-compatibility, and immutability coverage.
| ] | ||
|
|
||
|
|
||
| def test_manager_uses_current_warmup_api(monkeypatch): |
There was a problem hiding this comment.
🤖 AI-assisted suggestion (non-blocking): These six tests cover useful behavior, but most of their setup is identical. Could we parameterize by lifecycle generation and exercise both selected methods in each positive case?
One legacy row could assert warmup_gpu() followed by release(), and one current row could assert warmup() followed by destroy(). The missing-entry-point checks could become a second parameterized test over warmup and destroy.
That would preserve selection and diagnostic coverage while reducing the four positive tests to two collected cases and removing most of the repeated SimpleNamespace, mapping, and call-recording setup. I would keep the three ownership-state tests below separate because legacy failure, retryable current failure, and terminal current failure assert genuinely distinct postconditions.
There was a problem hiding this comment.
Addressed in 2200d71. The positive dispatch coverage now has one legacy row and one current row that each exercise both lifecycle calls, and the missing-entry-point checks are parameterized by operation. The three ownership-state tests remain separate.
AntoineRichard
left a comment
There was a problem hiding this comment.
🤖 AI-assisted review — test maintainability
The lifecycle coverage is valuable, particularly the three teardown tests for legacy cleanup, retryable OVPhysX 0.6 destruction, and terminal OVPhysX 0.6 destruction. Those tests protect distinct ownership invariants and should remain separate.
The reported 57 passing tests include 34 pre-existing cases. This PR adds 23 collected cases: 14 compatibility cases and 9 manager cases; the CPU runtime test is modified rather than added.
I left two non-blocking inline suggestions to consolidate overlapping classification/mapping coverage and parameterize the lifecycle dispatch wrappers. Together, those changes could reduce the additions from 23 to roughly 16 collected cases, about 30%, without removing meaningful regression coverage.
No blocking change is requested from this test-volume review.
| from isaaclab_ov.physics import ovphysx_manager as om_mod | ||
|
|
||
| events = [] | ||
| monkeypatch.setattr(om_mod, "OVPHYSX_LIFECYCLE_ENTRY_POINTS", {"warmup": "warmup_gpu", "destroy": "release"}) |
There was a problem hiding this comment.
Non-blocking, but worth fixing while you're here: the pre-existing test_manager_attaches_and_releases_owned_ovstage (L464) also drives _release_physx() with a fake that only implements release(), and unlike this test it does not pin OVPHYSX_LIFECYCLE_ENTRY_POINTS. With the installed ovphysx metadata reporting 0.6.x it fails with
AttributeError: OVPhysX does not expose the selected destroy() lifecycle entry point
(reproduced by patching importlib.metadata.version to return 0.6.0; the other 56 tests in these two files pass under both versions). Either pin the legacy mapping there as done here, or add destroy = release to that fake so the suite stays green on a 0.6 install.
There was a problem hiding this comment.
Addressed in 2200d71. The existing OVStage ownership test now explicitly pins the legacy mapping, and the full compatibility suite passes with installed metadata simulated as 0.6.0. I also handled the review note about prereleases by classifying on version.release[:2], with a 0.6 development/local-build case.
marcodiiga
left a comment
There was a problem hiding this comment.
LGTM. Checked the mapping against both the 0.5.11 wheel and the 0.6 API: warmup_gpu/release -> warmup/destroy is correct, and the owner-retention logic matches the 0.6 destroy() contract (a native or process-shutdown failure marks the instance destroyed before raising, so probing handle for RuntimeError is the right terminal check; only a pre-teardown failure keeps it valid for a retry). Skipping the explicit CPU warmup on 0.6 is fine since warmup() runs lazily on the first tensor read.
Two non-blocking notes:
- One pre-existing test is not version-agnostic (inline comment).
uses_current_lifecycle_apicompares againstVersion("0.6"). A 0.6 pre-release tag (e.g.0.6.0rc1or0.6.0.dev1) sorts below that under PEP 440 and would select the legacy names; comparingversion.release[:2] >= (0, 6)avoids it if such builds are ever used for testing.
The failing multi-GPU smoke job is a runner setup issue (Isaac Sim package vs. virtualenv), unrelated to this change.
|
run-ci |
|
run-ci |
|
run-ci |
Description
Support both the released OVPhysX 0.5.11 lifecycle API and the OVPhysX 0.6 lifecycle API without changing Isaac Lab's dependency pins.
This follows the existing OVRTX 0.4.1/0.5 compatibility pattern:
ovphysxdistribution version once at import.warmup_gpu()/release()for OVPhysX 0.5.11 and towarmup()/destroy()for the OVPhysX 0.6 release line and later, including prerelease and development builds.ovandovphysxextras pinned toovphysx==0.5.11; OVPhysX 0.6 remains a manual internal-testing overlay.The lifecycle changes preserve existing 0.5.11 cleanup semantics. For OVPhysX 0.6 only, they also retain OVPhysX and OVStage ownership when
destroy()fails before native teardown so cleanup can be retried; a terminal destroy error releases dead owners.The scope is limited to OVPhysX lifecycle compatibility code, tests, and the
isaaclab_ovchangelog fragment. No actuator changes or dependency updates are included.Type of change
Validation
uv run --frozen --extra test --extra ovphysx python -m pytest source/isaaclab_ov/test/physics/test_ovphysx_compat.py source/isaaclab_ov/test/physics/test_ovphysx_scene_data_backend.py -q— 50 passed against the pinned OVPhysX 0.5.11 environment.uv run --extra test --extra ovphysx python -m pytest source/isaaclab_ov/test/assets/test_rigid_object.py::test_warmup_attach_stage_not_called_for_cpu -q— 1 passed against the real OVPhysX 0.5.11 CPU runtime.uv run --frozen --extra test --extra ov python -m pytest source/isaaclab_ov/test/test_ovrtx_clone_plan.py -q— 17 passed after merging upstream fix Fix OVRTX clone-plan test import #7582.develop— passed.uv run isaaclab -f— passed.Screenshots
Not applicable.
Checklist
uv run isaaclab -fsource/<pkg>/changelog.d/for every touched packageCONTRIBUTORS.md