Skip to content

Commit 3128116

Browse files
Mayankm96jtigue-bdai
authored andcommitted
Improves contribution guidelines for IsaacLab (isaac-sim#3403)
# Description I realized there were some comments I often have to repeat in my reviewing process. I tried to add some of them into the code style page to directly point developers to it. ## Type of change - This change requires a documentation update ## Checklist - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Signed-off-by: Mayank Mittal <[email protected]> Co-authored-by: James Tigue <[email protected]>
1 parent 01f6da7 commit 3128116

File tree

5 files changed

+270
-9
lines changed

5 files changed

+270
-9
lines changed

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
Thank you for your interest in sending a pull request. Please make sure to check the contribution guidelines.
55
66
Link: https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html
7+
8+
💡 Please try to keep PRs small and focused. Large PRs are harder to review and merge.
79
-->
810

911
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.
@@ -21,8 +23,8 @@ is demanded by more than one party. -->
2123

2224
- Bug fix (non-breaking change which fixes an issue)
2325
- New feature (non-breaking change which adds functionality)
24-
- Breaking change (fix or feature that would cause existing functionality to not work as expected)
25-
- This change requires a documentation update
26+
- Breaking change (existing functionality will not work without user modification)
27+
- Documentation update
2628

2729
## Screenshots
2830

@@ -40,6 +42,7 @@ To upload images to a PR -- simply drag and drop an image while in edit mode and
4042

4143
## Checklist
4244

45+
- [ ] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
4346
- [ ] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format`
4447
- [ ] I have made corresponding changes to the documentation
4548
- [ ] My changes generate no new warnings

docs/conf.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@
122122
"python": ("https://docs.python.org/3", None),
123123
"numpy": ("https://numpy.org/doc/stable/", None),
124124
"trimesh": ("https://trimesh.org/", None),
125-
"torch": ("https://pytorch.org/docs/stable/", None),
125+
"torch": ("https://docs.pytorch.org/docs/stable", None),
126126
"isaacsim": ("https://docs.isaacsim.omniverse.nvidia.com/5.0.0/py/", None),
127127
"gymnasium": ("https://gymnasium.farama.org/", None),
128128
"warp": ("https://nvidia.github.io/warp/", None),
@@ -162,6 +162,7 @@
162162
"isaacsim.core.api",
163163
"isaacsim.core.cloner",
164164
"isaacsim.core.version",
165+
"isaacsim.core.utils",
165166
"isaacsim.robot_motion.motion_generation",
166167
"isaacsim.gui.components",
167168
"isaacsim.asset.importer.urdf",

docs/source/overview/environments.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,10 @@ for the lift-cube environment:
190190
.. |galbot_stack-link| replace:: `Isaac-Stack-Cube-Galbot-Left-Arm-Gripper-RmpFlow-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/stack/config/galbot/stack_rmp_rel_env_cfg.py>`__
191191
.. |kuka-allegro-lift-link| replace:: `Isaac-Dexsuite-Kuka-Allegro-Lift-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/dexsuite_kuka_allegro_env_cfg.py>`__
192192
.. |kuka-allegro-reorient-link| replace:: `Isaac-Dexsuite-Kuka-Allegro-Reorient-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/dexsuite_kuka_allegro_env_cfg.py>`__
193-
194193
.. |cube-shadow-link| replace:: `Isaac-Repose-Cube-Shadow-Direct-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_tasks/isaaclab_tasks/direct/shadow_hand/shadow_hand_env_cfg.py>`__
195194
.. |cube-shadow-ff-link| replace:: `Isaac-Repose-Cube-Shadow-OpenAI-FF-Direct-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_tasks/isaaclab_tasks/direct/shadow_hand/shadow_hand_env_cfg.py>`__
196195
.. |cube-shadow-lstm-link| replace:: `Isaac-Repose-Cube-Shadow-OpenAI-LSTM-Direct-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_tasks/isaaclab_tasks/direct/shadow_hand/shadow_hand_env_cfg.py>`__
197196
.. |cube-shadow-vis-link| replace:: `Isaac-Repose-Cube-Shadow-Vision-Direct-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_tasks/isaaclab_tasks/direct/shadow_hand/shadow_hand_vision_env.py>`__
198-
199197
.. |agibot_place_mug-link| replace:: `Isaac-Place-Mug-Agibot-Left-Arm-RmpFlow-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/place/config/agibot/place_upright_mug_rmp_rel_env_cfg.py>`__
200198
.. |agibot_place_toy-link| replace:: `Isaac-Place-Toy2Box-Agibot-Right-Arm-RmpFlow-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/place/config/agibot/place_toy2box_rmp_rel_env_cfg.py>`__
201199

docs/source/refs/contributing.rst

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ Please ensure that your code is well-formatted, documented and passes all the te
5454
Large pull requests are difficult to review and may take a long time to merge.
5555

5656

57+
More details on the code style and testing can be found in the `Coding Style`_ and `Unit Testing`_ sections.
58+
59+
5760
Contributing Documentation
5861
--------------------------
5962

@@ -237,6 +240,62 @@ For documentation, we adopt the `Google Style Guide <https://sphinxcontrib-napol
237240
for docstrings. We use `Sphinx <https://www.sphinx-doc.org/en/master/>`__ for generating the documentation.
238241
Please make sure that your code is well-documented and follows the guidelines.
239242

243+
Code Structure
244+
^^^^^^^^^^^^^^
245+
246+
We follow a specific structure for the codebase. This helps in maintaining the codebase and makes it easier to
247+
understand.
248+
249+
In a Python file, we follow the following structure:
250+
251+
.. code:: python
252+
253+
# Imports: These are sorted by the pre-commit hooks.
254+
# Constants
255+
# Functions (public)
256+
# Classes (public)
257+
# _Functions (private)
258+
# _Classes (private)
259+
260+
Imports are sorted by the pre-commit hooks. Unless there is a good reason to do otherwise, please do not
261+
import the modules inside functions or classes. To deal with circular imports, we use the
262+
:obj:`typing.TYPE_CHECKING` variable. Please refer to the `Circular Imports`_ section for more details.
263+
264+
Python does not have a concept of private and public classes and functions. However, we follow the
265+
convention of prefixing the private functions and classes with an underscore.
266+
The public functions and classes are the ones that are intended to be used by the users. The private
267+
functions and classes are the ones that are intended to be used internally in that file.
268+
Irrespective of the public or private nature of the functions and classes, we follow the Style Guide
269+
for the code and make sure that the code and documentation are consistent.
270+
271+
Similarly, within Python classes, we follow the following structure:
272+
273+
.. code:: python
274+
275+
# Constants
276+
# Class variables (public or private): Must have the type hint ClassVar[type]
277+
# Dunder methods: __init__, __del__
278+
# Representation: __repr__, __str__
279+
# Properties: @property
280+
# Instance methods (public)
281+
# Class methods (public)
282+
# Static methods (public)
283+
# _Instance methods (private)
284+
# _Class methods (private)
285+
# _Static methods (private)
286+
287+
The rule of thumb is that the functions within the classes are ordered in the way a user would
288+
expect to use them. For instance, if the class contains the method :meth:`initialize`, :meth:`reset`,
289+
:meth:`update`, and :meth:`close`, then they should be listed in the order of their usage.
290+
The same applies for private functions in the class. Their order is based on the order of call inside the
291+
class.
292+
293+
.. dropdown:: Code skeleton
294+
:icon: code
295+
296+
.. literalinclude:: snippets/code_skeleton.py
297+
:language: python
298+
240299
Circular Imports
241300
^^^^^^^^^^^^^^^^
242301

@@ -414,15 +473,47 @@ We summarize the key points below:
414473

415474

416475
Unit Testing
417-
^^^^^^^^^^^^
476+
------------
418477

419478
We use `pytest <https://docs.pytest.org>`__ for unit testing.
420479
Good tests not only cover the basic functionality of the code but also the edge cases.
421480
They should be able to catch regressions and ensure that the code is working as expected.
422481
Please make sure that you add tests for your changes.
423482

483+
.. tab-set::
484+
:sync-group: os
485+
486+
.. tab-item:: :icon:`fa-brands fa-linux` Linux
487+
:sync: linux
488+
489+
.. code-block:: bash
490+
491+
# Run all tests
492+
./isaaclab.sh --test # or "./isaaclab.sh -t"
493+
494+
# Run all tests in a particular file
495+
./isaaclab.sh -p -m pytest source/isaaclab/test/deps/test_torch.py
496+
497+
# Run a particular test
498+
./isaaclab.sh -p -m pytest source/isaaclab/test/deps/test_torch.py::test_array_slicing
499+
500+
.. tab-item:: :icon:`fa-brands fa-windows` Windows
501+
:sync: windows
502+
503+
.. code-block:: bash
504+
505+
# Run all tests
506+
isaaclab.bat --test # or "isaaclab.bat -t"
507+
508+
# Run all tests in a particular file
509+
isaaclab.bat -p -m pytest source/isaaclab/test/deps/test_torch.py
510+
511+
# Run a particular test
512+
isaaclab.bat -p -m pytest source/isaaclab/test/deps/test_torch.py::test_array_slicing
513+
514+
424515
Tools
425-
^^^^^
516+
-----
426517

427518
We use the following tools for maintaining code quality:
428519

@@ -435,6 +526,19 @@ Please check `here <https://pre-commit.com/#install>`__ for instructions
435526
to set these up. To run over the entire repository, please execute the
436527
following command in the terminal:
437528

438-
.. code:: bash
529+
.. tab-set::
530+
:sync-group: os
531+
532+
.. tab-item:: :icon:`fa-brands fa-linux` Linux
533+
:sync: linux
534+
535+
.. code-block:: bash
536+
537+
./isaaclab.sh --format # or "./isaaclab.sh -f"
538+
539+
.. tab-item:: :icon:`fa-brands fa-windows` Windows
540+
:sync: windows
541+
542+
.. code-block:: bash
439543
440-
./isaaclab.sh --format # or "./isaaclab.sh -f"
544+
isaaclab.bat --format # or "isaaclab.bat -f"
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# Copyright (c) 2022-2025, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
2+
# All rights reserved.
3+
#
4+
# SPDX-License-Identifier: BSD-3-Clause
5+
6+
import os
7+
import sys
8+
from typing import ClassVar
9+
10+
11+
DEFAULT_TIMEOUT: int = 30
12+
"""Default timeout for the task."""
13+
14+
_MAX_RETRIES: int = 3 # private constant (note the underscore)
15+
"""Maximum number of retries for the task."""
16+
17+
18+
def run_task(task_name: str):
19+
"""Run a task by name.
20+
21+
Args:
22+
task_name: The name of the task to run.
23+
"""
24+
print(f"Running task: {task_name}")
25+
26+
27+
class TaskRunner:
28+
"""Runs and manages tasks."""
29+
30+
DEFAULT_NAME: ClassVar[str] = "runner"
31+
"""Default name for the runner."""
32+
33+
_registry: ClassVar[dict] = {}
34+
"""Registry of runners."""
35+
36+
def __init__(self, name: str):
37+
"""Initialize the runner.
38+
39+
Args:
40+
name: The name of the runner.
41+
"""
42+
self.name = name
43+
self._tasks = [] # private instance variable
44+
45+
def __del__(self):
46+
"""Clean up the runner."""
47+
print(f"Cleaning up {self.name}")
48+
49+
def __repr__(self) -> str:
50+
return f"TaskRunner(name={self.name!r})"
51+
52+
def __str__(self) -> str:
53+
return f"TaskRunner: {self.name}"
54+
55+
"""
56+
Properties.
57+
"""
58+
59+
@property
60+
def task_count(self) -> int:
61+
return len(self._tasks)
62+
63+
"""
64+
Operations.
65+
"""
66+
67+
def initialize(self):
68+
"""Initialize the runner."""
69+
print("Initializing runner...")
70+
71+
def update(self, task: str):
72+
"""Update the runner with a new task.
73+
74+
Args:
75+
task: The task to add.
76+
"""
77+
self._tasks.append(task)
78+
print(f"Added task: {task}")
79+
80+
def close(self):
81+
"""Close the runner."""
82+
print("Closing runner...")
83+
84+
"""
85+
Operations: Registration.
86+
"""
87+
88+
@classmethod
89+
def register(cls, name: str, runner: "TaskRunner"):
90+
"""Register a runner.
91+
92+
Args:
93+
name: The name of the runner.
94+
runner: The runner to register.
95+
"""
96+
if name in cls._registry:
97+
_log_error(f"Runner {name} already registered. Skipping registration.")
98+
return
99+
cls._registry[name] = runner
100+
101+
@staticmethod
102+
def validate_task(task: str) -> bool:
103+
"""Validate a task.
104+
105+
Args:
106+
task: The task to validate.
107+
108+
Returns:
109+
True if the task is valid, False otherwise.
110+
"""
111+
return bool(task and task.strip())
112+
113+
"""
114+
Internal operations.
115+
"""
116+
117+
def _reset(self):
118+
"""Reset the runner."""
119+
self._tasks.clear()
120+
121+
@classmethod
122+
def _get_registry(cls) -> dict:
123+
"""Get the registry."""
124+
return cls._registry
125+
126+
@staticmethod
127+
def _internal_helper():
128+
"""Internal helper."""
129+
print("Internal helper called.")
130+
131+
132+
"""
133+
Helper operations.
134+
"""
135+
136+
137+
def _log_error(message: str):
138+
"""Internal helper to log errors.
139+
140+
Args:
141+
message: The message to log.
142+
"""
143+
print(f"[ERROR] {message}")
144+
145+
146+
class _TaskHelper:
147+
"""Private utility class for internal task logic."""
148+
149+
def compute(self) -> int:
150+
"""Compute the result.
151+
152+
Returns:
153+
The result of the computation.
154+
"""
155+
return 42

0 commit comments

Comments
 (0)