diff --git a/docs/source/device/body_tracking.rst b/docs/source/device/body_tracking.rst index 6fb559203e..d04911d472 100644 --- a/docs/source/device/body_tracking.rst +++ b/docs/source/device/body_tracking.rst @@ -203,9 +203,9 @@ joint ``is_valid`` flags to determine which joints have valid poses. For a minimal C++ reader see ``examples/schemaio/full_body_printer.cpp``, which creates the tracker, queries the required OpenXR extensions, and prints the joint data each frame through ``DeviceIOSession``. The Python equivalent is -``examples/oxr/python/test_full_body_tracker.py``. Neither embeds -``CloudXRLauncher``, so start the runtime first and source its environment in -the shell that launches them: +``examples/oxr/python/isaaccapture_examples/oxr/test_full_body_tracker.py``. Neither +embeds ``CloudXRLauncher``, so start the runtime first and source its +environment in the shell that launches them: .. code-block:: bash diff --git a/docs/source/device/oak.rst b/docs/source/device/oak.rst index d7ebd94582..d5c5d8171a 100644 --- a/docs/source/device/oak.rst +++ b/docs/source/device/oak.rst @@ -169,7 +169,7 @@ controllers, etc.): while True: session.step() # metadata is recorded each update -See also :code-file:`examples/oxr/python/test_oak_camera.py` (``--mode +See also :code-file:`examples/oxr/python/isaaccapture_examples/oxr/test_oak_camera.py` (``--mode schema-pusher``) for a standalone PluginManager + DeviceIOSession example of the same SchemaPusher flow. diff --git a/docs/source/device/trackers.rst b/docs/source/device/trackers.rst index b5568bb8e7..98a54027fb 100644 --- a/docs/source/device/trackers.rst +++ b/docs/source/device/trackers.rst @@ -190,7 +190,7 @@ Tracks the HMD head pose via the OpenXR view space. - Examples: - :code-file:`examples/oxr/cpp/oxr_simple_api_demo.cpp` - - :code-file:`examples/oxr/python/modular_example.py` + - :code-file:`examples/oxr/python/isaaccapture_examples/oxr/modular_example.py` HandTracker ~~~~~~~~~~~ @@ -206,12 +206,12 @@ Tracks articulated hand joints (26 joints per hand, following the OpenXR - :code-file:`tests/cpp/core/schema/test_hand.cpp` - :code-file:`tests/python/core/schema/test_hand.py` - - :code-file:`examples/oxr/python/test_synthetic_hands.py` + - :code-file:`examples/oxr/python/isaaccapture_examples/oxr/test_synthetic_hands.py` - Examples: - :code-file:`examples/oxr/cpp/oxr_simple_api_demo.cpp` - - :code-file:`examples/oxr/python/modular_example.py` + - :code-file:`examples/oxr/python/isaaccapture_examples/oxr/modular_example.py` - :code-file:`examples/retargeting/python/isaaccapture_examples/retargeting/sources_example.py` ControllerTracker @@ -228,7 +228,7 @@ axis inputs. Uses standard OpenXR action bindings. - :code-file:`tests/cpp/core/schema/test_controller.cpp` - :code-file:`tests/python/core/schema/test_controller.py` - - :code-file:`examples/oxr/python/test_controller_tracker.py` + - :code-file:`examples/oxr/python/isaaccapture_examples/oxr/test_controller_tracker.py` - Examples: @@ -254,7 +254,7 @@ reads the PICO ``XR_BD_body_tracking`` extension directly. - :code-file:`tests/cpp/core/schema/test_full_body.cpp` - :code-file:`tests/python/core/schema/test_full_body.py` - - :code-file:`examples/oxr/python/test_full_body_tracker.py` + - :code-file:`examples/oxr/python/isaaccapture_examples/oxr/test_full_body_tracker.py` - Examples: @@ -287,7 +287,7 @@ utility internally. - :code-file:`tests/cpp/core/schema/test_oak.cpp` - :code-file:`tests/python/core/schema/test_camera.py` - - :code-file:`examples/oxr/python/test_oak_camera.py` + - :code-file:`examples/oxr/python/isaaccapture_examples/oxr/test_oak_camera.py` - Examples: @@ -378,7 +378,7 @@ Usage Examples For end-to-end usage patterns combining trackers with a ``DeviceIOSession``, see: - **C++**: :code-file:`examples/oxr/cpp/oxr_simple_api_demo.cpp` -- **Python**: :code-file:`examples/oxr/python/modular_example.py` +- **Python**: :code-file:`examples/oxr/python/isaaccapture_examples/oxr/modular_example.py` For higher-level usage with the teleop session manager and retargeting, see: diff --git a/examples/oxr/CMakeLists.txt b/examples/oxr/CMakeLists.txt index 6dcfb26495..1becdfcf51 100644 --- a/examples/oxr/CMakeLists.txt +++ b/examples/oxr/CMakeLists.txt @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.20) @@ -7,4 +7,4 @@ cmake_minimum_required(VERSION 3.20) add_subdirectory(cpp) include(${CMAKE_SOURCE_DIR}/cmake/InstallPythonExample.cmake) -install_python_example(DESTINATION examples/oxr/python) +install_python_example(DESTINATION examples/oxr) diff --git a/examples/oxr/README.md b/examples/oxr/README.md index 7e63f62118..18c1f19373 100644 --- a/examples/oxr/README.md +++ b/examples/oxr/README.md @@ -1,5 +1,5 @@ @@ -31,8 +31,8 @@ examples/oxr/ │ ├── CMakeLists.txt │ ├── oxr_session_sharing.cpp │ └── oxr_simple_api_demo.cpp -└── python/ # Python examples - ├── pyproject.toml # uv configuration +├── pyproject.toml # the distribution +└── python/isaaccapture_examples/oxr/ ├── modular_example.py ├── test_modular.py ├── test_extensions.py @@ -41,34 +41,25 @@ examples/oxr/ ## Python Examples -Python examples use `uv` for dependency management and execution. - ### Running Python Examples -After building and installing, navigate to the installed examples: +Install the example once, then run any module from anywhere: ```bash -# From install directory (recommended) -cd install/examples/oxr/python +uv pip install -e ./examples/oxr export XR_RUNTIME_JSON=/path/to/cloudxr/openxr_cloudxr-dev.json -uv run test_modular.py +python -m isaaccapture_examples.oxr.test_modular ``` -Or run from source directory: - -```bash -# From source directory (requires build to be complete) -cd examples/oxr/python -export XR_RUNTIME_JSON=/path/to/cloudxr/openxr_cloudxr-dev.json -uv run test_modular.py -``` +The installed tree works the same way — `uv pip install -e +install/examples/oxr` resolves against the wheel that build produced. ### Available Python Examples -- **modular_example.py** - Basic hand + head tracking -- **test_modular.py** - Complete API test -- **test_extensions.py** - Extension query demonstration -- **test_session_sharing.py** - Session sharing between DeviceIOSession instances +- **modular_example** - Basic hand + head tracking +- **test_modular** - Complete API test +- **test_extensions** - Extension query demonstration +- **test_session_sharing** - Session sharing between DeviceIOSession instances ## C++ Examples @@ -110,9 +101,9 @@ cmake --build build cmake --install build # Run Python example -cd install/examples/oxr/python +uv pip install -e install/examples/oxr export XR_RUNTIME_JSON=/path/to/cloudxr/openxr_cloudxr-dev.json -uv run test_modular.py +python -m isaaccapture_examples.oxr.test_modular ``` ### C++ Example diff --git a/examples/oxr/pyproject.toml b/examples/oxr/pyproject.toml new file mode 100644 index 0000000000..cf42d8ed78 --- /dev/null +++ b/examples/oxr/pyproject.toml @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +# The dist name mirrors the import path, so an installed example claims no bare +# top-level name in site-packages. +name = "isaaccapture-examples-oxr" +version = "0.0.0" # Internal examples - not versioned +description = "OpenXR Tracking Python Examples" +requires-python = ">=3.11,<3.14" +dependencies = [ + "isaaccapture", + "numpy>=1.23.0", +] + +# `isaaccapture_examples` is a PEP 420 namespace shared by every example dist and +# must stay without an __init__.py. `only-include` + `sources`, not `packages`: +# the latter keeps only the last path component and would root the wheel at a +# bare top-level `oxr/`. +[tool.hatch.build.targets.wheel] +only-include = ["python/isaaccapture_examples/oxr"] +sources = ["python"] diff --git a/examples/oxr/python/README.md b/examples/oxr/python/README.md deleted file mode 100644 index afc1ace4c0..0000000000 --- a/examples/oxr/python/README.md +++ /dev/null @@ -1,99 +0,0 @@ - - -# OpenXR Python Examples - -Python examples demonstrating the OpenXR tracking API using the `isaaccapture` Python wheel. - -## Prerequisites - -1. **Build and install the project** (from the project root): - ```bash - cd ../../.. - cmake -B build - cmake --build build - cmake --install build - ``` - This creates the Python wheel in `build/wheels/` and installs it to `install/wheels/`. - -2. **Install uv** (if not already installed): - ```bash - curl -LsSf https://astral.sh/uv/install.sh | sh - ``` - -## Running Examples - -### From Install Directory (Recommended) - -After building and installing, run examples from the install directory: - -```bash -cd ../../../install/examples/oxr/python -export XR_RUNTIME_JSON=/path/to/your/openxr_runtime.json -uv run test_modular.py -``` - -### From Source Directory - -You can also run from the source directory: - -```bash -export XR_RUNTIME_JSON=/path/to/your/openxr_runtime.json -uv run test_modular.py -``` - -The `pyproject.toml` configuration automatically locates the built wheel. - -## Available Examples - -### modular_example.py -Basic demonstration of hand and head tracking. - -```bash -uv run modular_example.py -``` - -### test_modular.py -Comprehensive test of the modular tracking API. - -```bash -uv run test_modular.py -``` - -### test_extensions.py -Demonstrates querying available OpenXR extensions. - -```bash -uv run test_extensions.py -``` - -### test_session_sharing.py -Shows how to share sessions between multiple DeviceIOSession instances. - -```bash -uv run test_session_sharing.py -``` - -## Troubleshooting - -### Wheel not found -If you get an error about the wheel not being found, build the project: -```bash -cd ../../.. -cmake -B build -cmake --build build -``` - -### uv not installed -```bash -curl -LsSf https://astral.sh/uv/install.sh | sh -# Then restart your shell -``` - -### OpenXR runtime errors -Set the XR_RUNTIME_JSON environment variable: -```bash -export XR_RUNTIME_JSON=/path/to/your/openxr_runtime.json -``` diff --git a/examples/oxr/python/isaaccapture_examples/oxr/__init__.py b/examples/oxr/python/isaaccapture_examples/oxr/__init__.py new file mode 100644 index 0000000000..52a7a9daf0 --- /dev/null +++ b/examples/oxr/python/isaaccapture_examples/oxr/__init__.py @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 diff --git a/examples/oxr/python/modular_example.py b/examples/oxr/python/isaaccapture_examples/oxr/modular_example.py similarity index 100% rename from examples/oxr/python/modular_example.py rename to examples/oxr/python/isaaccapture_examples/oxr/modular_example.py diff --git a/examples/oxr/python/modular_example_with_mcap.py b/examples/oxr/python/isaaccapture_examples/oxr/modular_example_with_mcap.py similarity index 100% rename from examples/oxr/python/modular_example_with_mcap.py rename to examples/oxr/python/isaaccapture_examples/oxr/modular_example_with_mcap.py diff --git a/examples/oxr/python/test_controller_tracker.py b/examples/oxr/python/isaaccapture_examples/oxr/test_controller_tracker.py similarity index 100% rename from examples/oxr/python/test_controller_tracker.py rename to examples/oxr/python/isaaccapture_examples/oxr/test_controller_tracker.py diff --git a/examples/oxr/python/test_extensions.py b/examples/oxr/python/isaaccapture_examples/oxr/test_extensions.py similarity index 100% rename from examples/oxr/python/test_extensions.py rename to examples/oxr/python/isaaccapture_examples/oxr/test_extensions.py diff --git a/examples/oxr/python/test_full_body_tracker.py b/examples/oxr/python/isaaccapture_examples/oxr/test_full_body_tracker.py similarity index 100% rename from examples/oxr/python/test_full_body_tracker.py rename to examples/oxr/python/isaaccapture_examples/oxr/test_full_body_tracker.py diff --git a/examples/oxr/python/test_hand_inactive_on_plugin_stop.py b/examples/oxr/python/isaaccapture_examples/oxr/test_hand_inactive_on_plugin_stop.py similarity index 96% rename from examples/oxr/python/test_hand_inactive_on_plugin_stop.py rename to examples/oxr/python/isaaccapture_examples/oxr/test_hand_inactive_on_plugin_stop.py index 7fadcdc1dd..b6d644fbf8 100644 --- a/examples/oxr/python/test_hand_inactive_on_plugin_stop.py +++ b/examples/oxr/python/isaaccapture_examples/oxr/test_hand_inactive_on_plugin_stop.py @@ -20,7 +20,9 @@ import isaaccapture.oxr as oxr import isaaccapture.plugin_manager as pm -PLUGIN_ROOT_DIR = Path(__file__).resolve().parent.parent.parent.parent / "plugins" +# Six levels up is the tree root -- the checkout, or the install prefix +# when running from install/examples/. +PLUGIN_ROOT_DIR = Path(__file__).resolve().parents[5] / "plugins" ACTIVE_WAIT_S = 15.0 # max time to wait for hands to become active INACTIVE_WAIT_S = 5.0 # max time to wait for hands to become inactive after stop diff --git a/examples/oxr/python/test_modular.py b/examples/oxr/python/isaaccapture_examples/oxr/test_modular.py similarity index 100% rename from examples/oxr/python/test_modular.py rename to examples/oxr/python/isaaccapture_examples/oxr/test_modular.py diff --git a/examples/oxr/python/test_oak_camera.py b/examples/oxr/python/isaaccapture_examples/oxr/test_oak_camera.py similarity index 98% rename from examples/oxr/python/test_oak_camera.py rename to examples/oxr/python/isaaccapture_examples/oxr/test_oak_camera.py index 4fac39e6af..b10efc30c5 100755 --- a/examples/oxr/python/test_oak_camera.py +++ b/examples/oxr/python/isaaccapture_examples/oxr/test_oak_camera.py @@ -26,7 +26,9 @@ import isaaccapture.deviceio as deviceio import isaaccapture.oxr as oxr -PLUGIN_ROOT_DIR = Path(__file__).resolve().parent.parent.parent.parent / "plugins" +# Six levels up is the tree root -- the checkout, or the install prefix +# when running from install/examples/. +PLUGIN_ROOT_DIR = Path(__file__).resolve().parents[5] / "plugins" MODE_NO_METADATA = "no-metadata" MODE_SCHEMA_PUSHER = "schema-pusher" diff --git a/examples/oxr/python/test_package_version.py b/examples/oxr/python/isaaccapture_examples/oxr/test_package_version.py similarity index 88% rename from examples/oxr/python/test_package_version.py rename to examples/oxr/python/isaaccapture_examples/oxr/test_package_version.py index f7a490c9a5..09a694c580 100755 --- a/examples/oxr/python/test_package_version.py +++ b/examples/oxr/python/isaaccapture_examples/oxr/test_package_version.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 """Validate isaaccapture module version reporting against CI-provided expected version.""" diff --git a/examples/oxr/python/test_session_sharing.py b/examples/oxr/python/isaaccapture_examples/oxr/test_session_sharing.py similarity index 100% rename from examples/oxr/python/test_session_sharing.py rename to examples/oxr/python/isaaccapture_examples/oxr/test_session_sharing.py diff --git a/examples/oxr/python/test_synthetic_hands.py b/examples/oxr/python/isaaccapture_examples/oxr/test_synthetic_hands.py similarity index 96% rename from examples/oxr/python/test_synthetic_hands.py rename to examples/oxr/python/isaaccapture_examples/oxr/test_synthetic_hands.py index 9c5f07fe66..1521702209 100644 --- a/examples/oxr/python/test_synthetic_hands.py +++ b/examples/oxr/python/isaaccapture_examples/oxr/test_synthetic_hands.py @@ -25,7 +25,9 @@ # The test will look for plugins in the install directory relative to this script # This script is in install/examples/oxr/python # Plugins are in install/plugins -PLUGIN_ROOT_DIR = Path(__file__).resolve().parent.parent.parent.parent / "plugins" +# Six levels up is the tree root -- the checkout, or the install prefix +# when running from install/examples/. +PLUGIN_ROOT_DIR = Path(__file__).resolve().parents[5] / "plugins" def run_test(): diff --git a/examples/oxr/python/pyproject.toml b/examples/oxr/python/pyproject.toml deleted file mode 100644 index e5fb2720da..0000000000 --- a/examples/oxr/python/pyproject.toml +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -[project] -name = "oxr-examples" -version = "0.0.0" # Internal examples - not versioned -description = "OpenXR Tracking Python Examples" -requires-python = ">=3.11,<3.14" -dependencies = [ - "isaaccapture", - "numpy>=1.23.0", -] diff --git a/scripts/run_tests_with_cloudxr.sh b/scripts/run_tests_with_cloudxr.sh index a99b870bd5..97ebe7f9d3 100755 --- a/scripts/run_tests_with_cloudxr.sh +++ b/scripts/run_tests_with_cloudxr.sh @@ -24,10 +24,12 @@ set -euo pipefail # the ctest configuration in `build-ubuntu.yml` instead, so that they can be # run on a runner without GPU support immediately after the build. #============================================================================== +# Paths are relative to /app/tests in the test image, which is a copy of +# examples/oxr/python/ -- so they carry the namespace prefix. CXR_PYTHON_GPU_TESTS=( - "test_package_version.py" - "test_extensions.py" - "test_modular.py" + "isaaccapture_examples/oxr/test_package_version.py" + "isaaccapture_examples/oxr/test_extensions.py" + "isaaccapture_examples/oxr/test_modular.py" ) CXR_NATIVE_GPU_TESTS=( diff --git a/src/plugins/oak/README.md b/src/plugins/oak/README.md index b956eed960..29a958eb34 100644 --- a/src/plugins/oak/README.md +++ b/src/plugins/oak/README.md @@ -144,7 +144,7 @@ with TeleopSession(config) as session: session.step() # metadata is recorded each update ``` -See also `examples/oxr/python/test_oak_camera.py --mode schema-pusher` for a +See also `examples/oxr/python/isaaccapture_examples/oxr/test_oak_camera.py --mode schema-pusher` for a standalone PluginManager + DeviceIOSession example of the same SchemaPusher flow. diff --git a/tests/AGENTS.md b/tests/AGENTS.md index f244cdc85e..d7cf84278b 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -61,5 +61,5 @@ Shared C++ fixtures (not executables) live under `tests/cpp/viz/support/` ## Out of scope here - `src/core/codegen/test_*.py` — unittest, co-located with the generator. -- `examples/oxr/python/test_*.py` — standalone scripts for CloudXR GPU CI. +- `examples/oxr/python/isaaccapture_examples/oxr/test_*.py` — standalone scripts for CloudXR GPU CI. - ROS2 Docker integration helpers under `examples/teleop_ros2/.../integration_tests/`.